Repository: v2fly/v2ray-core Branch: master Commit: 2e97de9b4f26 Files: 1366 Total size: 5.5 MB Directory structure: gitextract_rgu_9fid/ ├── .github/ │ ├── CODE_OF_CONDUCT.md │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_cn.md │ │ ├── bug_en.md │ │ └── other_en.md │ ├── ISSUE_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── codeql-analysis.yml │ ├── linter.yml │ ├── release.yml │ ├── sign.yml │ ├── stale.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── annotations.go ├── app/ │ ├── app.go │ ├── browserforwarder/ │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ └── forwarder.go │ ├── commander/ │ │ ├── commander.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── outbound.go │ │ ├── service.go │ │ └── webcommander/ │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ └── webcommander.go │ ├── dispatcher/ │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── default.go │ │ ├── dispatcher.go │ │ ├── errors.generated.go │ │ ├── fakednssniffer.go │ │ ├── sniffer.go │ │ ├── stats.go │ │ └── stats_test.go │ ├── dns/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── dns.go │ │ ├── dns_test.go │ │ ├── dnscommon.go │ │ ├── dnscommon_test.go │ │ ├── errors.generated.go │ │ ├── fakedns/ │ │ │ ├── errors.generated.go │ │ │ ├── fake.go │ │ │ ├── fakedns.go │ │ │ ├── fakedns.pb.go │ │ │ ├── fakedns.proto │ │ │ └── fakedns_test.go │ │ ├── fakedns.go │ │ ├── fakedns_test.go │ │ ├── hosts.go │ │ ├── hosts_test.go │ │ ├── nameserver.go │ │ ├── nameserver_doh.go │ │ ├── nameserver_doh_test.go │ │ ├── nameserver_fakedns.go │ │ ├── nameserver_local.go │ │ ├── nameserver_local_test.go │ │ ├── nameserver_quic.go │ │ ├── nameserver_quic_test.go │ │ ├── nameserver_tcp.go │ │ ├── nameserver_tcp_test.go │ │ └── nameserver_udp.go │ ├── instman/ │ │ ├── command/ │ │ │ ├── command.go │ │ │ ├── command.pb.go │ │ │ ├── command.proto │ │ │ └── command_grpc.pb.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ └── instman.go │ ├── log/ │ │ ├── command/ │ │ │ ├── command.go │ │ │ ├── command_test.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── config_grpc.pb.go │ │ │ └── errors.generated.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── log.go │ │ ├── log_creator.go │ │ └── log_test.go │ ├── observatory/ │ │ ├── burst/ │ │ │ ├── burst.go │ │ │ ├── burstobserver.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ ├── healthping.go │ │ │ ├── healthping_result.go │ │ │ ├── healthping_result_test.go │ │ │ └── ping.go │ │ ├── command/ │ │ │ ├── command.go │ │ │ ├── command.pb.go │ │ │ ├── command.proto │ │ │ ├── command_grpc.pb.go │ │ │ └── errors.generated.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── explainErrors.go │ │ ├── multiobservatory/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ └── multi.go │ │ ├── observatory.go │ │ └── observer.go │ ├── persistentstorage/ │ │ ├── filesystemstorage/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ └── fs.go │ │ ├── protostorage/ │ │ │ └── protokv.go │ │ └── storage.go │ ├── policy/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── manager.go │ │ ├── manager_test.go │ │ └── policy.go │ ├── proxyman/ │ │ ├── command/ │ │ │ ├── command.go │ │ │ ├── command.pb.go │ │ │ ├── command.proto │ │ │ ├── command_grpc.pb.go │ │ │ ├── doc.go │ │ │ └── errors.generated.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── inbound/ │ │ │ ├── always.go │ │ │ ├── dynamic.go │ │ │ ├── errors.generated.go │ │ │ ├── inbound.go │ │ │ └── worker.go │ │ └── outbound/ │ │ ├── errors.generated.go │ │ ├── handler.go │ │ ├── handler_test.go │ │ └── outbound.go │ ├── restfulapi/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── restful_api.go │ │ ├── service.go │ │ └── service_test.go │ ├── reverse/ │ │ ├── bridge.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── portal.go │ │ ├── portal_test.go │ │ └── reverse.go │ ├── router/ │ │ ├── balancing.go │ │ ├── balancing_override.go │ │ ├── command/ │ │ │ ├── command.go │ │ │ ├── command.pb.go │ │ │ ├── command.proto │ │ │ ├── command_grpc.pb.go │ │ │ ├── command_test.go │ │ │ ├── config.go │ │ │ └── errors.generated.go │ │ ├── condition.go │ │ ├── condition_geoip.go │ │ ├── condition_geoip_test.go │ │ ├── condition_test.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── router.go │ │ ├── router_test.go │ │ ├── routercommon/ │ │ │ ├── common.pb.go │ │ │ └── common.proto │ │ ├── strategy_leastload.go │ │ ├── strategy_leastload_test.go │ │ ├── strategy_leastping.go │ │ ├── strategy_random.go │ │ ├── weight.go │ │ └── weight_test.go │ ├── stats/ │ │ ├── channel.go │ │ ├── channel_test.go │ │ ├── command/ │ │ │ ├── command.go │ │ │ ├── command.pb.go │ │ │ ├── command.proto │ │ │ ├── command_grpc.pb.go │ │ │ ├── command_test.go │ │ │ └── errors.generated.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── counter.go │ │ ├── counter_test.go │ │ ├── errors.generated.go │ │ ├── stats.go │ │ └── stats_test.go │ ├── subscription/ │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── containers/ │ │ │ ├── base64urlline/ │ │ │ │ ├── base64urlline.go │ │ │ │ ├── errors.generated.go │ │ │ │ └── parser.go │ │ │ ├── containers.go │ │ │ ├── dataurlsingle/ │ │ │ │ ├── dataurl.go │ │ │ │ └── errors.generated.go │ │ │ ├── errors.generated.go │ │ │ ├── jsonfieldarray/ │ │ │ │ ├── errors.generated.go │ │ │ │ ├── jsonfieldarray.go │ │ │ │ ├── jsonified/ │ │ │ │ │ ├── errors.generated.go │ │ │ │ │ ├── jsonified.go │ │ │ │ │ └── parser.go │ │ │ │ └── parser.go │ │ │ ├── tryall.go │ │ │ └── urlline/ │ │ │ ├── errors.generated.go │ │ │ ├── parser.go │ │ │ └── urlline.go │ │ ├── documentfetcher/ │ │ │ ├── dataurlfetcher/ │ │ │ │ ├── dataurl.go │ │ │ │ └── errors.generated.go │ │ │ ├── errors.generated.go │ │ │ ├── fetcher.go │ │ │ └── httpfetcher/ │ │ │ ├── errors.generated.go │ │ │ └── http.go │ │ ├── entries/ │ │ │ ├── entries.go │ │ │ ├── errors.generated.go │ │ │ ├── nonnative/ │ │ │ │ ├── converter.go │ │ │ │ ├── definitions/ │ │ │ │ │ ├── shadowsocks.jsont │ │ │ │ │ ├── shadowsocks2022.jsont │ │ │ │ │ └── vmess.jsont │ │ │ │ ├── errors.generated.go │ │ │ │ ├── matchdef.go │ │ │ │ ├── nonnative.go │ │ │ │ └── nonnativeifce/ │ │ │ │ └── nonnativeifce.go │ │ │ ├── outbound/ │ │ │ │ ├── errors.generated.go │ │ │ │ └── outbound.go │ │ │ └── register.go │ │ ├── errors.generated.go │ │ ├── specs/ │ │ │ ├── abstract_spec.pb.go │ │ │ ├── abstract_spec.proto │ │ │ ├── errors.generated.go │ │ │ ├── outbound_parser.go │ │ │ ├── skeleton.go │ │ │ └── specs.go │ │ ├── subscription.go │ │ ├── subscription_rpc.pb.go │ │ ├── subscription_rpc.proto │ │ └── subscriptionmanager/ │ │ ├── command/ │ │ │ ├── command.go │ │ │ ├── command.pb.go │ │ │ ├── command.proto │ │ │ ├── command_grpc.pb.go │ │ │ └── errors.generated.go │ │ ├── delta.go │ │ ├── errors.generated.go │ │ ├── known_metadata.go │ │ ├── manager.go │ │ ├── manager_rpc.go │ │ ├── serverspec_materialize.go │ │ ├── subdocapplier.go │ │ ├── subdocchecker.go │ │ ├── subdocupdater.go │ │ └── tracked_subscription.go │ └── tun/ │ ├── config.pb.go │ ├── config.proto │ ├── device/ │ │ ├── device.go │ │ ├── errors.generated.go │ │ ├── gvisor/ │ │ │ ├── errors.generated.go │ │ │ ├── gvisor.go │ │ │ ├── gvisor_linux.go │ │ │ └── gvisor_others.go │ │ └── linkWriterToWriter.go │ ├── errors.generated.go │ ├── handler.go │ ├── handler_tcp.go │ ├── handler_udp.go │ ├── net/ │ │ └── net.go │ ├── option.go │ ├── packetaddradaptar.go │ ├── packetparse/ │ │ ├── errors.generated.go │ │ ├── packetParse.go │ │ └── udp.go │ ├── stack.go │ ├── tun.go │ └── tunsorter/ │ ├── errors.generated.go │ └── tunsorter.go ├── common/ │ ├── antireplay/ │ │ ├── antireplay.go │ │ ├── bloomring.go │ │ └── replayfilter.go │ ├── bitmask/ │ │ ├── byte.go │ │ └── byte_test.go │ ├── buf/ │ │ ├── buf.go │ │ ├── buffer.go │ │ ├── buffer_test.go │ │ ├── copy.go │ │ ├── copy_test.go │ │ ├── errors.generated.go │ │ ├── io.go │ │ ├── io_test.go │ │ ├── multi_buffer.go │ │ ├── multi_buffer_test.go │ │ ├── reader.go │ │ ├── reader_test.go │ │ ├── readv_posix.go │ │ ├── readv_reader.go │ │ ├── readv_reader_wasm.go │ │ ├── readv_test.go │ │ ├── readv_unix.go │ │ ├── readv_windows.go │ │ ├── writer.go │ │ └── writer_test.go │ ├── bytespool/ │ │ └── pool.go │ ├── cache/ │ │ ├── lru.go │ │ └── lru_test.go │ ├── cmdarg/ │ │ ├── arg.go │ │ ├── cmdarg.go │ │ └── errors.generated.go │ ├── common.go │ ├── common_test.go │ ├── crypto/ │ │ ├── aes.go │ │ ├── auth.go │ │ ├── auth_test.go │ │ ├── benchmark_test.go │ │ ├── chacha20.go │ │ ├── chacha20_test.go │ │ ├── chunk.go │ │ ├── chunk_test.go │ │ ├── crypto.go │ │ ├── errors.generated.go │ │ ├── internal/ │ │ │ ├── chacha.go │ │ │ ├── chacha_core.generated.go │ │ │ └── chacha_core_gen.go │ │ └── io.go │ ├── dice/ │ │ ├── dice.go │ │ └── dice_test.go │ ├── drain/ │ │ ├── drain.go │ │ ├── drainer.go │ │ └── errors.generated.go │ ├── dualStack/ │ │ ├── fusedPacketConn/ │ │ │ └── fusedPacketSocket.go │ │ └── happyEyeball/ │ │ └── racingDialer.go │ ├── environment/ │ │ ├── app.go │ │ ├── base.go │ │ ├── connection.go │ │ ├── deferredpersistentstorage/ │ │ │ └── defereredPersistentStorage.go │ │ ├── envctx/ │ │ │ └── env.go │ │ ├── envimpl/ │ │ │ └── fs.go │ │ ├── filesystemcap/ │ │ │ └── fscap.go │ │ ├── filesystemimpl/ │ │ │ └── fsimpl.go │ │ ├── proxy.go │ │ ├── rootcap.go │ │ ├── rootcap_impl.go │ │ ├── systemnetworkimpl/ │ │ │ └── systemnetwork.go │ │ ├── transientstorageimpl/ │ │ │ ├── errors.generated.go │ │ │ └── storage.go │ │ └── transport.go │ ├── errors/ │ │ ├── errors.go │ │ ├── errors_test.go │ │ └── multi_error.go │ ├── errors.generated.go │ ├── interfaces.go │ ├── log/ │ │ ├── access.go │ │ ├── log.go │ │ ├── log.pb.go │ │ ├── log.proto │ │ ├── log_test.go │ │ ├── logger.go │ │ └── logger_test.go │ ├── mux/ │ │ ├── client.go │ │ ├── client_test.go │ │ ├── errors.generated.go │ │ ├── frame.go │ │ ├── frame_test.go │ │ ├── mux.go │ │ ├── mux_test.go │ │ ├── reader.go │ │ ├── server.go │ │ ├── session.go │ │ ├── session_test.go │ │ └── writer.go │ ├── natTraversal/ │ │ └── stun/ │ │ ├── filteredStunConnection.go │ │ ├── natTypeTest.go │ │ ├── processor.go │ │ ├── stunClientConn.go │ │ └── stuncli/ │ │ └── stuncli.go │ ├── net/ │ │ ├── abstactOutbount/ │ │ │ ├── errors.generated.go │ │ │ └── outbound.go │ │ ├── address.go │ │ ├── address.pb.go │ │ ├── address.proto │ │ ├── address_test.go │ │ ├── connection.go │ │ ├── destination.go │ │ ├── destination.pb.go │ │ ├── destination.proto │ │ ├── destination_test.go │ │ ├── errors.generated.go │ │ ├── net.go │ │ ├── network.go │ │ ├── network.pb.go │ │ ├── network.proto │ │ ├── packetaddr/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── connection_adaptor.go │ │ │ ├── packetaddr.go │ │ │ ├── packetaddr_test.go │ │ │ └── special_address.go │ │ ├── port.go │ │ ├── port.pb.go │ │ ├── port.proto │ │ ├── port_test.go │ │ └── system.go │ ├── packetswitch/ │ │ ├── gvisorstack/ │ │ │ ├── adapter.go │ │ │ ├── adapter_test.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── dialer.go │ │ │ ├── stack.go │ │ │ └── tcp_listener.go │ │ ├── interconnect/ │ │ │ ├── interconnect.go │ │ │ ├── networkLayer_cable.go │ │ │ └── networkLayer_cable_test.go │ │ └── packetswitch.go │ ├── peer/ │ │ ├── latency.go │ │ └── peer.go │ ├── platform/ │ │ ├── ctlcmd/ │ │ │ └── attr_other.go │ │ ├── filesystem/ │ │ │ ├── file.go │ │ │ └── fsifce/ │ │ │ └── ifce.go │ │ ├── others.go │ │ ├── platform.go │ │ ├── platform_test.go │ │ ├── securedload/ │ │ │ ├── embedded.go │ │ │ ├── embeddedhash.go │ │ │ ├── errors.generated.go │ │ │ ├── file.go │ │ │ ├── securedload.go │ │ │ └── verify.go │ │ └── windows.go │ ├── protocol/ │ │ ├── account.go │ │ ├── address.go │ │ ├── address_test.go │ │ ├── bittorrent/ │ │ │ └── bittorrent.go │ │ ├── context.go │ │ ├── dns/ │ │ │ ├── errors.generated.go │ │ │ └── io.go │ │ ├── errors.generated.go │ │ ├── headers.go │ │ ├── headers.pb.go │ │ ├── headers.proto │ │ ├── http/ │ │ │ ├── headers.go │ │ │ ├── headers_test.go │ │ │ ├── sniff.go │ │ │ └── sniff_test.go │ │ ├── id.go │ │ ├── id_test.go │ │ ├── payload.go │ │ ├── protocol.go │ │ ├── quic/ │ │ │ ├── cipher_suite.go │ │ │ ├── sniff.go │ │ │ └── sniff_test.go │ │ ├── server_picker.go │ │ ├── server_picker_test.go │ │ ├── server_spec.go │ │ ├── server_spec.pb.go │ │ ├── server_spec.proto │ │ ├── server_spec_test.go │ │ ├── time.go │ │ ├── time_test.go │ │ ├── tls/ │ │ │ ├── cert/ │ │ │ │ ├── .gitignore │ │ │ │ ├── cert.go │ │ │ │ ├── cert_test.go │ │ │ │ ├── errors.generated.go │ │ │ │ └── privateKey.go │ │ │ ├── sniff.go │ │ │ └── sniff_test.go │ │ ├── udp/ │ │ │ ├── packet.go │ │ │ └── udp.go │ │ ├── user.go │ │ ├── user.pb.go │ │ └── user.proto │ ├── protoext/ │ │ ├── errors.generated.go │ │ ├── extensions.go │ │ ├── extensions.pb.go │ │ ├── extensions.proto │ │ └── testing/ │ │ ├── extension_test.go │ │ ├── test.pb.go │ │ └── test.proto │ ├── protofilter/ │ │ ├── errors.generated.go │ │ └── filter.go │ ├── registry/ │ │ ├── errors.generated.go │ │ ├── implementation_set.go │ │ ├── registry.go │ │ └── restrict.go │ ├── retry/ │ │ ├── errors.generated.go │ │ ├── retry.go │ │ └── retry_test.go │ ├── serial/ │ │ ├── resolver.go │ │ ├── serial.go │ │ ├── serial_test.go │ │ ├── string.go │ │ ├── string_test.go │ │ ├── typed_message.go │ │ └── typed_message_test.go │ ├── session/ │ │ ├── context.go │ │ └── session.go │ ├── signal/ │ │ ├── done/ │ │ │ └── done.go │ │ ├── notifier.go │ │ ├── notifier_test.go │ │ ├── pubsub/ │ │ │ ├── pubsub.go │ │ │ └── pubsub_test.go │ │ ├── semaphore/ │ │ │ └── semaphore.go │ │ ├── timer.go │ │ └── timer_test.go │ ├── strmatcher/ │ │ ├── benchmark_indexmatcher_test.go │ │ ├── benchmark_matchers_test.go │ │ ├── indexmatcher_linear.go │ │ ├── indexmatcher_linear_test.go │ │ ├── indexmatcher_mph.go │ │ ├── indexmatcher_mph_test.go │ │ ├── matchergroup_ac_automation.go │ │ ├── matchergroup_ac_automation_test.go │ │ ├── matchergroup_domain.go │ │ ├── matchergroup_domain_test.go │ │ ├── matchergroup_full.go │ │ ├── matchergroup_full_test.go │ │ ├── matchergroup_mph.go │ │ ├── matchergroup_mph_test.go │ │ ├── matchergroup_simple.go │ │ ├── matchergroup_simple_test.go │ │ ├── matchergroup_substr.go │ │ ├── matchergroup_substr_test.go │ │ ├── matchers.go │ │ ├── matchers_test.go │ │ └── strmatcher.go │ ├── taggedfeatures/ │ │ ├── configloader.go │ │ ├── creator.go │ │ ├── errors.generated.go │ │ ├── holder.go │ │ ├── skeleton.pb.go │ │ ├── skeleton.proto │ │ └── taggedfeatures.go │ ├── task/ │ │ ├── common.go │ │ ├── periodic.go │ │ ├── periodic_test.go │ │ ├── task.go │ │ ├── taskDerive/ │ │ │ ├── derive.go │ │ │ ├── errors.generated.go │ │ │ └── tryAll.go │ │ └── task_test.go │ ├── type.go │ ├── type_test.go │ ├── units/ │ │ ├── bytesize.go │ │ └── bytesize_test.go │ └── uuid/ │ ├── uuid.go │ └── uuid_test.go ├── config.go ├── config.pb.go ├── config.proto ├── context.go ├── context_test.go ├── core.go ├── errors.generated.go ├── features/ │ ├── dns/ │ │ ├── client.go │ │ ├── fakedns.go │ │ └── localdns/ │ │ ├── client.go │ │ └── errors.generated.go │ ├── errors.generated.go │ ├── extension/ │ │ ├── browser.go │ │ ├── contextreceiver.go │ │ ├── instance.go │ │ ├── observatory.go │ │ ├── storage/ │ │ │ └── storage.go │ │ └── storage.go │ ├── feature.go │ ├── inbound/ │ │ └── inbound.go │ ├── outbound/ │ │ └── outbound.go │ ├── policy/ │ │ ├── default.go │ │ └── policy.go │ ├── routing/ │ │ ├── balancer.go │ │ ├── context.go │ │ ├── dispatcher.go │ │ ├── dns/ │ │ │ ├── context.go │ │ │ └── errors.generated.go │ │ ├── router.go │ │ └── session/ │ │ └── context.go │ └── stats/ │ ├── errors.generated.go │ └── stats.go ├── format.go ├── functions.go ├── functions_test.go ├── go.mod ├── go.sum ├── infra/ │ ├── conf/ │ │ ├── cfgcommon/ │ │ │ ├── buildable.go │ │ │ ├── common.go │ │ │ ├── common_test.go │ │ │ ├── duration/ │ │ │ │ ├── duration.go │ │ │ │ └── duration_test.go │ │ │ ├── errors.generated.go │ │ │ ├── loader/ │ │ │ │ ├── errors.generated.go │ │ │ │ └── loader.go │ │ │ ├── muxcfg/ │ │ │ │ └── mux.go │ │ │ ├── proxycfg/ │ │ │ │ ├── errors.generated.go │ │ │ │ └── proxy.go │ │ │ ├── session.go │ │ │ ├── sniffer/ │ │ │ │ ├── errors.generated.go │ │ │ │ └── sniffer.go │ │ │ ├── socketcfg/ │ │ │ │ └── socket.go │ │ │ ├── testassist/ │ │ │ │ └── general.go │ │ │ └── tlscfg/ │ │ │ ├── errors.generated.go │ │ │ └── tls.go │ │ ├── geodata/ │ │ │ ├── attr.go │ │ │ ├── errors.generated.go │ │ │ ├── geodata.go │ │ │ ├── geodata_test.go │ │ │ ├── geodataproto.go │ │ │ ├── memconservative/ │ │ │ │ ├── cache.go │ │ │ │ ├── decode.go │ │ │ │ ├── decode_test.go │ │ │ │ ├── errors.generated.go │ │ │ │ └── memc.go │ │ │ └── standard/ │ │ │ ├── errors.generated.go │ │ │ └── standard.go │ │ ├── json/ │ │ │ ├── json_test.go │ │ │ ├── reader.go │ │ │ ├── reader_test.go │ │ │ ├── toml.go │ │ │ ├── toml_test.go │ │ │ ├── yaml.go │ │ │ └── yaml_test.go │ │ ├── jsonpb/ │ │ │ ├── errors.generated.go │ │ │ └── jsonpb.go │ │ ├── merge/ │ │ │ ├── errors.generated.go │ │ │ ├── map.go │ │ │ ├── merge.go │ │ │ ├── merge_test.go │ │ │ ├── priority.go │ │ │ ├── rules.go │ │ │ └── tag.go │ │ ├── mergers/ │ │ │ ├── errors.generated.go │ │ │ ├── extensions.go │ │ │ ├── merge.go │ │ │ ├── merger_base.go │ │ │ ├── mergers.go │ │ │ └── names.go │ │ ├── rule/ │ │ │ ├── errors.generated.go │ │ │ ├── rule.go │ │ │ └── rule_test.go │ │ ├── serial/ │ │ │ ├── errors.generated.go │ │ │ ├── loader.go │ │ │ ├── loader_test.go │ │ │ └── serial.go │ │ ├── synthetic/ │ │ │ ├── dns/ │ │ │ │ ├── dns.go │ │ │ │ ├── dns_test.go │ │ │ │ ├── errors.generated.go │ │ │ │ └── fakedns.go │ │ │ ├── log/ │ │ │ │ └── log.go │ │ │ └── router/ │ │ │ ├── errors.generated.go │ │ │ ├── router.go │ │ │ ├── router_strategy.go │ │ │ └── router_test.go │ │ ├── v2jsonpb/ │ │ │ ├── any2.go │ │ │ ├── errors.generated.go │ │ │ ├── follower.go │ │ │ ├── followerany.go │ │ │ ├── followerlist.go │ │ │ ├── followermap.go │ │ │ └── v2jsonpb.go │ │ ├── v4/ │ │ │ ├── api.go │ │ │ ├── blackhole.go │ │ │ ├── blackhole_test.go │ │ │ ├── browser_forwarder.go │ │ │ ├── conf.go │ │ │ ├── dns_proxy.go │ │ │ ├── dns_proxy_test.go │ │ │ ├── dokodemo.go │ │ │ ├── dokodemo_test.go │ │ │ ├── errors.generated.go │ │ │ ├── freedom.go │ │ │ ├── freedom_test.go │ │ │ ├── gun.go │ │ │ ├── http.go │ │ │ ├── http_test.go │ │ │ ├── hysteria2.go │ │ │ ├── lint.go │ │ │ ├── loopback.go │ │ │ ├── observatory.go │ │ │ ├── policy.go │ │ │ ├── policy_test.go │ │ │ ├── reverse.go │ │ │ ├── reverse_test.go │ │ │ ├── services.go │ │ │ ├── shadowsocks.go │ │ │ ├── shadowsocks_test.go │ │ │ ├── socks.go │ │ │ ├── socks_test.go │ │ │ ├── transport.go │ │ │ ├── transport_authenticators.go │ │ │ ├── transport_internet.go │ │ │ ├── transport_test.go │ │ │ ├── trojan.go │ │ │ ├── v2ray.go │ │ │ ├── v2ray_test.go │ │ │ ├── vless.go │ │ │ ├── vless_test.go │ │ │ ├── vmess.go │ │ │ └── vmess_test.go │ │ └── v5cfg/ │ │ ├── common.go │ │ ├── errors.generated.go │ │ ├── inbound.go │ │ ├── init.go │ │ ├── outbound.go │ │ ├── root.go │ │ ├── skeleton.go │ │ ├── stream.go │ │ └── v5cfg.go │ └── vformat/ │ └── main.go ├── main/ │ ├── commands/ │ │ ├── all/ │ │ │ ├── api/ │ │ │ │ ├── api.go │ │ │ │ ├── balancer_info.go │ │ │ │ ├── balancer_override.go │ │ │ │ ├── jsonv4/ │ │ │ │ │ ├── inbounds_add.go │ │ │ │ │ ├── inbounds_remove.go │ │ │ │ │ ├── init.go │ │ │ │ │ ├── outbounds_add.go │ │ │ │ │ └── outbounds_remove.go │ │ │ │ ├── log.go │ │ │ │ ├── shared.go │ │ │ │ └── stats.go │ │ │ ├── commands.go │ │ │ ├── engineering/ │ │ │ │ ├── convertpb.go │ │ │ │ ├── encodedataurl.go │ │ │ │ ├── engineering.go │ │ │ │ ├── errors.generated.go │ │ │ │ ├── generateRandomData/ │ │ │ │ │ ├── errors.generated.go │ │ │ │ │ └── generateRandomData.go │ │ │ │ ├── nonnativelinkexec.go │ │ │ │ ├── nonnativelinkextract.go │ │ │ │ ├── reversepb.go │ │ │ │ └── subscriptionEntriesExtract.go │ │ │ ├── errors.generated.go │ │ │ ├── format_doc.go │ │ │ ├── jsonv4/ │ │ │ │ ├── convert.go │ │ │ │ └── init.go │ │ │ ├── love.go │ │ │ ├── merge_doc.go │ │ │ ├── tls/ │ │ │ │ ├── cert.go │ │ │ │ ├── chainhash.go │ │ │ │ ├── ping.go │ │ │ │ └── tls.go │ │ │ ├── uuid.go │ │ │ └── verify.go │ │ ├── base/ │ │ │ ├── command.go │ │ │ ├── env.go │ │ │ ├── execute.go │ │ │ ├── help.go │ │ │ └── root.go │ │ ├── errors.generated.go │ │ ├── helpers/ │ │ │ ├── config_load.go │ │ │ └── fs.go │ │ ├── run.go │ │ ├── test.go │ │ └── version.go │ ├── distro/ │ │ ├── all/ │ │ │ ├── all.go │ │ │ ├── pion.go │ │ │ └── wireguard.go │ │ └── debug/ │ │ └── debug.go │ ├── errors.generated.go │ ├── formats/ │ │ ├── errors.generated.go │ │ └── formats.go │ ├── main.go │ ├── main_test.go │ ├── plugins/ │ │ ├── plugin.go │ │ └── plugin_pprof/ │ │ └── plugin_pprof.go │ └── v2binding/ │ ├── startUp.go │ ├── v2api/ │ │ └── api.go │ └── v2binding.go ├── mocks.go ├── proto.go ├── proxy/ │ ├── blackhole/ │ │ ├── blackhole.go │ │ ├── blackhole_test.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── config_test.go │ │ └── errors.generated.go │ ├── dns/ │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── dns.go │ │ ├── dns_test.go │ │ └── errors.generated.go │ ├── dokodemo/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── dokodemo.go │ │ └── errors.generated.go │ ├── freedom/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ └── freedom.go │ ├── http/ │ │ ├── client.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── http.go │ │ ├── server.go │ │ └── simplified/ │ │ ├── config.go │ │ ├── config.pb.go │ │ └── config.proto │ ├── hysteria2/ │ │ ├── client.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── hysteria2.go │ │ ├── protocol.go │ │ └── server.go │ ├── loopback/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ └── loopback.go │ ├── proxy.go │ ├── shadowsocks/ │ │ ├── client.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── config_test.go │ │ ├── errors.generated.go │ │ ├── protocol.go │ │ ├── protocol_test.go │ │ ├── server.go │ │ ├── shadowsocks.go │ │ └── simplified/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ └── errors.generated.go │ ├── shadowsocks2022/ │ │ ├── client.go │ │ ├── client_session.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── eih_aes.go │ │ ├── encoding.go │ │ ├── errors.generated.go │ │ ├── kdf_blake3.go │ │ ├── method_aes128gcm.go │ │ ├── method_aes256gcm.go │ │ ├── requestsalt.go │ │ ├── ss2022.go │ │ └── udp_aes.go │ ├── socks/ │ │ ├── client.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── protocol.go │ │ ├── protocol_test.go │ │ ├── server.go │ │ ├── simplified/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ └── config.proto │ │ └── socks.go │ ├── trojan/ │ │ ├── client.go │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ ├── protocol.go │ │ ├── protocol_test.go │ │ ├── server.go │ │ ├── simplified/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ └── config.proto │ │ ├── trojan.go │ │ └── validator.go │ ├── vless/ │ │ ├── account.go │ │ ├── account.pb.go │ │ ├── account.proto │ │ ├── encoding/ │ │ │ ├── addons.go │ │ │ ├── addons.pb.go │ │ │ ├── addons.proto │ │ │ ├── encoding.go │ │ │ ├── encoding_test.go │ │ │ └── errors.generated.go │ │ ├── errors.generated.go │ │ ├── inbound/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ └── inbound.go │ │ ├── outbound/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ └── outbound.go │ │ ├── validator.go │ │ └── vless.go │ ├── vlite/ │ │ ├── inbound/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── connAdp.go │ │ │ ├── errors.generated.go │ │ │ └── inbound.go │ │ ├── outbound/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ └── outbound.go │ │ └── vlite.go │ ├── vmess/ │ │ ├── account.go │ │ ├── account.pb.go │ │ ├── account.proto │ │ ├── aead/ │ │ │ ├── authid.go │ │ │ ├── authid_test.go │ │ │ ├── consts.go │ │ │ ├── encrypt.go │ │ │ ├── encrypt_test.go │ │ │ ├── kdf.go │ │ │ └── kdf_test.go │ │ ├── encoding/ │ │ │ ├── auth.go │ │ │ ├── auth_test.go │ │ │ ├── client.go │ │ │ ├── commands.go │ │ │ ├── commands_test.go │ │ │ ├── encoding.go │ │ │ ├── encoding_test.go │ │ │ ├── errors.generated.go │ │ │ └── server.go │ │ ├── errors.generated.go │ │ ├── inbound/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ └── inbound.go │ │ ├── outbound/ │ │ │ ├── command.go │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ └── outbound.go │ │ ├── validator.go │ │ ├── validator_test.go │ │ ├── vmess.go │ │ └── vmessCtxInterface.go │ └── wireguard/ │ ├── outbound/ │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── errors.generated.go │ │ └── outbound.go │ └── wgcommon/ │ ├── config.pb.go │ ├── config.proto │ ├── errors.generated.go │ ├── filterDebug.go │ ├── setup.go │ ├── wgConnAdaptor.go │ ├── wgConnAdaptor_test.go │ ├── wgDeviceAdaptor.go │ ├── wgDeviceAdaptor_test.go │ ├── wgLogAdaptor.go │ ├── wgcommon.go │ └── wgdevice.go ├── release/ │ ├── config/ │ │ ├── config.json │ │ ├── systemd/ │ │ │ └── system/ │ │ │ ├── v2ray.service │ │ │ └── v2ray@.service │ │ ├── vpoint_socks_vmess.json │ │ └── vpoint_vmess_freedom.json │ ├── container/ │ │ ├── Containerfile │ │ └── downloadAssets.sh │ ├── debian/ │ │ ├── changelog │ │ ├── control │ │ ├── copyright │ │ ├── rules │ │ ├── source/ │ │ │ └── format │ │ ├── v2ray-docs.docs │ │ ├── v2ray-domain-list-community.install │ │ ├── v2ray-geoip-only-cn-private.install │ │ ├── v2ray-geoip.install │ │ ├── v2ray.install │ │ ├── v2ray.service │ │ └── v2ray@.service │ ├── extra/ │ │ └── browserforwarder/ │ │ ├── index.html │ │ └── index.js │ ├── friendly-filenames.json │ ├── install-release.sh │ ├── requestsign.sh │ └── user-package.sh ├── testing/ │ ├── mocks/ │ │ ├── dns.go │ │ ├── io.go │ │ ├── log.go │ │ ├── mux.go │ │ ├── outbound.go │ │ └── proxy.go │ ├── scenarios/ │ │ ├── cert/ │ │ │ ├── self-signed_cert.pem │ │ │ └── self-signed_key.pem │ │ ├── command_test.go │ │ ├── common.go │ │ ├── common_coverage.go │ │ ├── common_instanceMgr.go │ │ ├── common_instanceMgr_test.go │ │ ├── common_regular.go │ │ ├── config/ │ │ │ ├── grpc_client.json │ │ │ ├── grpc_server.json │ │ │ ├── grpc_servicename_client.json │ │ │ ├── grpc_servicename_server.json │ │ │ ├── httpupgrade_client.json │ │ │ ├── httpupgrade_earlydataShortEarlyData_client.json │ │ │ ├── httpupgrade_earlydataShortEarlyData_server.json │ │ │ ├── httpupgrade_earlydata_client.json │ │ │ ├── httpupgrade_earlydata_server.json │ │ │ ├── httpupgrade_server.json │ │ │ ├── meek_client.json │ │ │ ├── meek_server.json │ │ │ ├── mekya_client.json │ │ │ └── mekya_server.json │ │ ├── dns_test.go │ │ ├── dokodemo_test.go │ │ ├── feature_test.go │ │ ├── grpc_test.go │ │ ├── http_test.go │ │ ├── httpupgrade_test.go │ │ ├── hy2_test.go │ │ ├── meek_test.go │ │ ├── mekya_test.go │ │ ├── policy_test.go │ │ ├── reverse_test.go │ │ ├── shadowsocks_test.go │ │ ├── socks_test.go │ │ ├── tls_test.go │ │ ├── transport_test.go │ │ └── vmess_test.go │ └── servers/ │ ├── http/ │ │ └── http.go │ ├── tcp/ │ │ ├── port.go │ │ └── tcp.go │ └── udp/ │ ├── port.go │ └── udp.go ├── transport/ │ ├── config.go │ ├── config.pb.go │ ├── config.proto │ ├── internet/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── connection.go │ │ ├── dialer.go │ │ ├── dialer_test.go │ │ ├── domainsocket/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── dial.go │ │ │ ├── errgen.go │ │ │ ├── errors.generated.go │ │ │ ├── listener.go │ │ │ └── listener_test.go │ │ ├── dtls/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── dialer.go │ │ │ ├── dtls.go │ │ │ ├── errors.generated.go │ │ │ └── listener.go │ │ ├── errors.generated.go │ │ ├── filelocker.go │ │ ├── filelocker_other.go │ │ ├── filelocker_windows.go │ │ ├── grpc/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── dial.go │ │ │ ├── encoding/ │ │ │ │ ├── conn.go │ │ │ │ ├── customSeviceName.go │ │ │ │ ├── encoding.go │ │ │ │ ├── errors.generated.go │ │ │ │ ├── stream.pb.go │ │ │ │ ├── stream.proto │ │ │ │ └── stream_grpc.pb.go │ │ │ ├── errors.generated.go │ │ │ ├── grpc.go │ │ │ └── hub.go │ │ ├── header.go │ │ ├── header_test.go │ │ ├── headers/ │ │ │ ├── http/ │ │ │ │ ├── config.go │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── errors.generated.go │ │ │ │ ├── http.go │ │ │ │ ├── http_test.go │ │ │ │ ├── linkedreadRequest.go │ │ │ │ └── resp.go │ │ │ ├── noop/ │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ └── noop.go │ │ │ ├── srtp/ │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── srtp.go │ │ │ │ └── srtp_test.go │ │ │ ├── tls/ │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── dtls.go │ │ │ │ └── dtls_test.go │ │ │ ├── utp/ │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── utp.go │ │ │ │ └── utp_test.go │ │ │ ├── wechat/ │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── wechat.go │ │ │ │ └── wechat_test.go │ │ │ └── wireguard/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ └── wireguard.go │ │ ├── http/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── dialer.go │ │ │ ├── errors.generated.go │ │ │ ├── http.go │ │ │ ├── http_test.go │ │ │ └── hub.go │ │ ├── httpupgrade/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── connection.go │ │ │ ├── dialer.go │ │ │ ├── errors.generated.go │ │ │ ├── httpupgrade.go │ │ │ └── hub.go │ │ ├── hysteria2/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── conn.go │ │ │ ├── dialer.go │ │ │ ├── errors.generated.go │ │ │ ├── hub.go │ │ │ ├── hy2_transport_test.go │ │ │ └── hysteria2.go │ │ ├── internet.go │ │ ├── kcp/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── connection.go │ │ │ ├── connection_test.go │ │ │ ├── crypt.go │ │ │ ├── crypt_test.go │ │ │ ├── cryptreal.go │ │ │ ├── dialer.go │ │ │ ├── errors.generated.go │ │ │ ├── io.go │ │ │ ├── io_test.go │ │ │ ├── kcp.go │ │ │ ├── kcp_test.go │ │ │ ├── listener.go │ │ │ ├── output.go │ │ │ ├── receiving.go │ │ │ ├── segment.go │ │ │ ├── segment_test.go │ │ │ ├── sending.go │ │ │ ├── xor.go │ │ │ ├── xor_amd64.go │ │ │ └── xor_amd64.s │ │ ├── memory_settings.go │ │ ├── quic/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── conn.go │ │ │ ├── dialer.go │ │ │ ├── errors.generated.go │ │ │ ├── hub.go │ │ │ ├── pool.go │ │ │ ├── quic.go │ │ │ └── quic_test.go │ │ ├── request/ │ │ │ ├── assembler/ │ │ │ │ ├── packetconn/ │ │ │ │ │ ├── errors.generated.go │ │ │ │ │ ├── packetConn.pb.go │ │ │ │ │ ├── packetConn.proto │ │ │ │ │ ├── packetbundle.go │ │ │ │ │ ├── packetconn.go │ │ │ │ │ ├── req2packet.go │ │ │ │ │ ├── udpassembler.go │ │ │ │ │ ├── udpassemblerClient.go │ │ │ │ │ └── udpassemblerServer.go │ │ │ │ └── simple/ │ │ │ │ ├── client.go │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── errors.generated.go │ │ │ │ ├── server.go │ │ │ │ └── simple.go │ │ │ ├── assembler.go │ │ │ ├── assembly/ │ │ │ │ ├── assembly.go │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── dialer.go │ │ │ │ ├── errors.generated.go │ │ │ │ └── hub.go │ │ │ ├── options.go │ │ │ ├── request.go │ │ │ ├── reverser.go │ │ │ ├── roundtripper/ │ │ │ │ └── httprt/ │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── errors.generated.go │ │ │ │ └── httprt.go │ │ │ ├── roundtripper.go │ │ │ ├── roundtripperreverserserver/ │ │ │ │ ├── accesschecker_password.go │ │ │ │ ├── accesschecker_password_test.go │ │ │ │ ├── clicommand/ │ │ │ │ │ ├── errors.generated.go │ │ │ │ │ ├── generate_token_cli.go │ │ │ │ │ └── roundTripperReserseServerCli.go │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── errors.generated.go │ │ │ │ ├── reverser.go │ │ │ │ ├── reverserimpl.go │ │ │ │ └── reverserimpl_test.go │ │ │ └── stereotype/ │ │ │ ├── meek/ │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── errors.generated.go │ │ │ │ └── meek.go │ │ │ └── mekya/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ └── mekya.go │ │ ├── security/ │ │ │ ├── connprop.go │ │ │ ├── errors.generated.go │ │ │ ├── security.go │ │ │ └── util.go │ │ ├── socket_activation_other.go │ │ ├── socket_activation_unix.go │ │ ├── sockopt.go │ │ ├── sockopt_darwin.go │ │ ├── sockopt_freebsd.go │ │ ├── sockopt_linux.go │ │ ├── sockopt_linux_test.go │ │ ├── sockopt_other.go │ │ ├── sockopt_test.go │ │ ├── sockopt_windows.go │ │ ├── system_dialer.go │ │ ├── system_dns_android.go │ │ ├── system_dns_android_test.go │ │ ├── system_listener.go │ │ ├── system_listener_test.go │ │ ├── tagged/ │ │ │ ├── tagged.go │ │ │ └── taggedimpl/ │ │ │ ├── errors.generated.go │ │ │ ├── impl.go │ │ │ └── taggedimpl.go │ │ ├── tcp/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── dialer.go │ │ │ ├── errors.generated.go │ │ │ ├── hub.go │ │ │ ├── sockopt_freebsd.go │ │ │ ├── sockopt_linux.go │ │ │ ├── sockopt_linux_test.go │ │ │ ├── sockopt_other.go │ │ │ └── tcp.go │ │ ├── tcp_hub.go │ │ ├── tls/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── config_other.go │ │ │ ├── config_test.go │ │ │ ├── config_windows.go │ │ │ ├── ech.go │ │ │ ├── ech_go122.go │ │ │ ├── engine.go │ │ │ ├── errors.generated.go │ │ │ ├── pin.go │ │ │ ├── pin_test.go │ │ │ ├── tls.go │ │ │ └── utls/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ ├── nameMapper.go │ │ │ └── utls.go │ │ ├── tlsmirror/ │ │ │ ├── data.pb.go │ │ │ ├── data.proto │ │ │ ├── httponconnection/ │ │ │ │ ├── errors.generated.go │ │ │ │ └── singleconnhttp.go │ │ │ ├── interface.go │ │ │ ├── mirrorbase/ │ │ │ │ ├── base.go │ │ │ │ ├── conn.go │ │ │ │ ├── crypto.go │ │ │ │ ├── crypto_test.go │ │ │ │ └── errors.generated.go │ │ │ ├── mirrorcommon/ │ │ │ │ ├── handshake.go │ │ │ │ ├── loopback_protect.go │ │ │ │ ├── record.go │ │ │ │ ├── record_consts.go │ │ │ │ └── recordstream.go │ │ │ ├── mirrorcrypto/ │ │ │ │ ├── decryptor.go │ │ │ │ ├── derive_key.go │ │ │ │ ├── encrypter.go │ │ │ │ ├── errors.generated.go │ │ │ │ ├── mirrorcrypto.go │ │ │ │ ├── tls_cipher_suites.go │ │ │ │ └── tls_cipher_suites_linkname.go │ │ │ ├── mirrorenrollment/ │ │ │ │ ├── cancelContextOnCloseConn.go │ │ │ │ ├── clicommand/ │ │ │ │ │ └── enrollmentlink_cli.go │ │ │ │ ├── client.go │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── enrollment.go │ │ │ │ ├── enrollmentlink.go │ │ │ │ ├── enrollmentlink_test.go │ │ │ │ ├── errors.generated.go │ │ │ │ ├── httpenrollmentconfirmation/ │ │ │ │ │ ├── client.go │ │ │ │ │ ├── clientbuilder.go │ │ │ │ │ ├── enrollment.go │ │ │ │ │ ├── errors.generated.go │ │ │ │ │ ├── hub.go │ │ │ │ │ └── server.go │ │ │ │ ├── keyderivation.go │ │ │ │ ├── roundtripperenrollmentconfirmation/ │ │ │ │ │ ├── client.go │ │ │ │ │ ├── config.pb.go │ │ │ │ │ ├── config.proto │ │ │ │ │ ├── errors.generated.go │ │ │ │ │ ├── rttconfirmation.go │ │ │ │ │ ├── server.go │ │ │ │ │ └── serverinverserole.go │ │ │ │ ├── server.go │ │ │ │ └── serverenrollmentprocessor.go │ │ │ ├── server/ │ │ │ │ ├── ciphersuits_lookup.go │ │ │ │ ├── client.go │ │ │ │ ├── client_conn.go │ │ │ │ ├── config.pb.go │ │ │ │ ├── config.proto │ │ │ │ ├── conn.go │ │ │ │ ├── errors.generated.go │ │ │ │ ├── hub.go │ │ │ │ ├── outbound.go │ │ │ │ ├── padding.go │ │ │ │ └── server.go │ │ │ └── tlstrafficgen/ │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── errors.generated.go │ │ │ └── trafficgen.go │ │ ├── transportcommon/ │ │ │ ├── dialer.go │ │ │ ├── errors.generated.go │ │ │ ├── httpDialer.go │ │ │ ├── listener.go │ │ │ └── tansportcommon.go │ │ ├── udp/ │ │ │ ├── config.go │ │ │ ├── config.pb.go │ │ │ ├── config.proto │ │ │ ├── copy.go │ │ │ ├── dialer.go │ │ │ ├── dispatcher.go │ │ │ ├── dispatcher_packetaddr.go │ │ │ ├── dispatcher_split.go │ │ │ ├── dispatcher_split_test.go │ │ │ ├── errors.generated.go │ │ │ ├── hub.go │ │ │ ├── hub_freebsd.go │ │ │ ├── hub_linux.go │ │ │ ├── hub_other.go │ │ │ ├── monodest.go │ │ │ └── udp.go │ │ └── websocket/ │ │ ├── config.go │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── connection.go │ │ ├── connforwarder.go │ │ ├── dialer.go │ │ ├── errors.generated.go │ │ ├── hub.go │ │ ├── ws.go │ │ └── ws_test.go │ ├── link.go │ └── pipe/ │ ├── impl.go │ ├── pipe.go │ ├── pipe_test.go │ ├── reader.go │ └── writer.go ├── v2ray.go └── v2ray_test.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at conduct@v2fly.org. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: .github/ISSUE_TEMPLATE/bug_cn.md ================================================ --- name: V2Ray 程序问题 about: "提交一个 V2Ray 的程序问题报告。" --- ## 你正在使用哪个版本的 V2Ray? ## 你的使用场景是什么? ## 你看到的异常现象是什么? ## 你期待看到的正常表现是怎样的? ## 请附上你的配置 **服务端配置:** ```javascript // 在这里附上服务器端配置文件 ``` **客户端配置:** ```javascript // 在这里附上客户端配置 ``` ## 请附上出错时软件输出的错误日志 **服务器端错误日志:** ```javascript // 在这里附上服务器端日志 ``` **客户端错误日志:** ```javascript // 在这里附上客户端日志 ``` ## 请附上访问日志 ```javascript // 在这里附上服务器端日志 ``` ## 其它相关的配置文件(如 Nginx)和相关日志 ## 如果 V2Ray 无法启动,请附上 `--test` 命令的输出 ## 如果 V2Ray 服务运行异常,请附上 journal 日志 ================================================ FILE: .github/ISSUE_TEMPLATE/bug_en.md ================================================ --- name: Bug report about: "Create a bug report to help us improve" --- ## What version of V2Ray are you using? ## What's your scenario of using V2Ray? ## What problems have you encountered? ## What's your expectation? ## Please attach your configuration here **Server configuration:** ```javascript // Please attach your server configuration here. ``` **Client configuration:** ```javascript // Please attach your client configuration here. ``` ## Please attach error logs here **Server error log:** ```javascript // Please attach your server error log here. ``` **Client error log:** ```javascript // Please attach your client error log here. ``` ## Please attach access log here ```javascript // Please attach your server access log here. ``` ## Other configurations (such as Nginx) and logs here ## If V2Ray cannot start up, please attach output from `--test` command ## If V2Ray service is abnormal, please attach journal log here ================================================ FILE: .github/ISSUE_TEMPLATE/other_en.md ================================================ --- name: Other about: "其它问题请使用 https://github.com/v2fly/v2ray-core/discussions 进行讨论 / Please discuss other issues at https://github.com/v2fly/v2ray-core/discussions" --- 如果你遇到的问题不是 V2Ray 的 bug,比如你不清楚要如何配置,请使用[Discussion](https://github.com/v2fly/v2ray-core/discussions)进行讨论。 此 Issue 会被立即关闭。 If you are not sure if your question is truely a bug in V2Ray, please discuss it [here](https://github.com/v2fly/v2ray-core/discussions) first. This issue will be closed immediately. ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ 如果你遇到的问题不是 V2Ray 的 bug,比如你不清楚要如何配置,请使用[Discussion](https://github.com/v2fly/discussion/issues)进行讨论。 此 Issue 会被立即关闭。 If you are not sure if your question is truely a bug in V2Ray, please discuss it [here](https://github.com/v2fly/discussion/issues) first. This issue will be closed immediately. ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "daily" open-pull-requests-limit: 10 - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ name: CodeQL on: push: branches: [master] pull_request: branches: [master] types: [opened, synchronize, reopened] paths-ignore: - "**/*.md" - "**/*.txt" jobs: analyze: runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: ["go"] steps: - name: Checkout repository uses: actions/checkout@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: autobuild - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 ================================================ FILE: .github/workflows/linter.yml ================================================ name: Linter on: push: branches: - master - v* paths: - "**/*.go" - ".github/workflows/linter.yml" pull_request: types: [opened, synchronize, reopened] paths: - "**/*.go" - ".github/workflows/linter.yml" jobs: lint: if: github.repository == 'v2fly/v2ray-core' runs-on: ubuntu-latest steps: - name: Checkout codebase uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: ./go.mod cache-dependency-path: ./go.sum - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: args: --config=.github/linters/.golangci.yml only-new-issues: true ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: release: types: - prereleased - released push: branches: - master - v* - dev* paths: - "**/*.go" - "go.mod" - "go.sum" - ".github/workflows/release.yml" pull_request: types: [opened, synchronize, reopened] paths: - "**/*.go" - "go.mod" - "go.sum" - ".github/workflows/release.yml" jobs: build: strategy: matrix: # Include amd64 on all platforms. goos: [windows, freebsd, openbsd, linux, dragonfly, darwin] goarch: [amd64, 386] exclude: # Exclude i386 on darwin and dragonfly. - goarch: 386 goos: dragonfly - goarch: 386 goos: darwin - goarch: amd64 goos: linux include: # BEGIN Linux ARM 5 6 7 - goos: linux goarch: arm goarm: 7 - goos: linux goarch: arm goarm: 6 - goos: linux goarch: arm goarm: 5 # END Linux ARM 5 6 7 # BEGIN Windows ARM 7 # - goos: windows # goarch: arm # goarm: 7 # END Windows ARM 7 # BEGIN FreeBSD ARM 6 7 - goos: freebsd goarch: arm goarm: 6 - goos: freebsd goarch: arm goarm: 7 # END FreeBSD ARM 6 7 # BEGIN OpenBSD ARM 6 7 - goos: openbsd goarch: arm goarm: 6 - goos: openbsd goarch: arm goarm: 7 # END OpenBSD ARM 6 7 # BEGIN Other architectures - goos: darwin goarch: arm64 - goos: linux goarch: arm64 - goos: linux goarch: riscv64 - goos: linux goarch: loong64 - goos: windows goarch: arm64 - goos: android goarch: arm64 - goos: freebsd goarch: arm64 - goos: openbsd goarch: arm64 # BEGIN MIPS - goos: linux goarch: mips64 - goos: linux goarch: mips64le - goos: linux goarch: mipsle - goos: linux goarch: mips - goos: linux goarch: arm64 pie: pie - goos: linux goarch: amd64 pie: pie - goos: linux goarch: amd64 pie: # END MIPS # END Other architectures fail-fast: false runs-on: ubuntu-latest env: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} GOARM: ${{ matrix.goarm }} PIE_ENABLED: ${{ matrix.pie }} CGO_ENABLED: 0 IS_PRERELEASE: ${{ github.event.release.prerelease }} steps: - name: Checkout codebase uses: actions/checkout@v6 with: fetch-depth: 0 - name: Show workflow information id: get_filename run: | export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$PIE_ENABLED\"].friendlyName" -r < release/friendly-filenames.json) if [ "$GOARCH" = "arm64" ] && [ "$PIE_ENABLED" = "pie" ] && [ "$IS_PRERELEASE" = "true" ]; then export _NAME="${_NAME}-prerelease" fi echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, RELEASE_NAME: $_NAME" echo "ASSET_NAME=$_NAME" >> $GITHUB_OUTPUT echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: ./go.mod cache-dependency-path: ./go.sum - name: Get project dependencies run: go mod download - name: Build V2Ray run: | mkdir -p build_assets EXTRA_ARG="" case "$GOOS-$GOARCH" in "linux-amd64") ;& "linux-arm64") if [ ! -z $PIE_ENABLED ] then EXTRA_ARG=$EXTRA_ARG" -buildmode=pie" fi ;; esac go build $EXTRA_ARG -v -o build_assets/v2ray -trimpath -ldflags "-s -w -buildid=" ./main - name: Rename Windows V2Ray if: matrix.goos == 'windows' run: | cd ./build_assets || exit 1 mv v2ray v2ray.exe - name: Download geo files run: | wget -O release/config/geoip.dat "https://raw.githubusercontent.com/v2fly/geoip/release/geoip.dat" wget -O release/config/geoip-only-cn-private.dat "https://raw.githubusercontent.com/v2fly/geoip/release/geoip-only-cn-private.dat" wget -O release/config/geosite.dat "https://raw.githubusercontent.com/v2fly/domain-list-community/release/dlc.dat" - name: Prepare package run: cp -v ./release/config/*.* ./build_assets - name: Prepare package for Linux if: matrix.goos == 'linux' run: cp -rv ./release/config/systemd ./build_assets/ - name: Create ZIP archive run: | pushd build_assets || exit 1 zip -9vr ../v2ray-$ASSET_NAME.zip . popd || exit 1 FILE=./v2ray-$ASSET_NAME.zip DGST=$FILE.dgst openssl dgst -md5 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha1 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha256 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >>$DGST - name: Upload ZIP file to Artifacts uses: actions/upload-artifact@v7 with: name: v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip path: v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip - name: Upload files to GitHub release uses: svenstaro/upload-release-action@v2 if: github.event_name == 'release' with: repo_token: ${{ secrets.GITHUB_TOKEN }} file_glob: true file: ./v2ray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip* tag: ${{ github.ref }} overwrite: true signature: runs-on: ubuntu-latest needs: build steps: - name: Checkout codebase uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: ./go.mod cache-dependency-path: ./go.sum - uses: actions/download-artifact@v8 with: path: build_artifacts - name: Create extra package run: | pushd ./release/extra/ zip -9vr ../../build_artifacts/v2ray-extra.zip . popd - name: Generate shasum run: | go get -v github.com/v2fly/V2BuildAssist/v2buildutil cd build_artifacts || exit 1 mkdir .temp mv ./*/*.zip ./.temp rmdir ./*/ mv ./.temp/* . ls -lah --recursive { go run github.com/v2fly/V2BuildAssist/v2buildutil gen version $(git describe --tags $(git rev-list --tags --max-count=1)) go run github.com/v2fly/V2BuildAssist/v2buildutil gen project "v2fly" for zip in $(ls *.zip); do go run github.com/v2fly/V2BuildAssist/v2buildutil gen file ${zip} done } >Release.unsigned.unsorted go run github.com/v2fly/V2BuildAssist/v2buildutil gen sort < Release.unsigned.unsorted > Release.unsigned rm -f Release.unsigned.unsorted FILE=./Release.unsigned DGST=$FILE.dgst openssl dgst -md5 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha1 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha256 $FILE | sed 's/([^)]*)//g' >>$DGST openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >>$DGST - uses: actions/upload-artifact@v7 with: name: Release.unsigned path: build_artifacts/Release.unsigned - uses: actions/upload-artifact@v7 with: name: Release.unsigned.dgst path: build_artifacts/Release.unsigned.dgst - uses: actions/upload-artifact@v7 with: name: v2ray-extra.zip path: build_artifacts/v2ray-extra.zip - name: Upload Release.unsigned related files uses: svenstaro/upload-release-action@v2 if: github.event_name == 'release' with: repo_token: ${{ secrets.GITHUB_TOKEN }} file_glob: true file: build_artifacts/Release.unsigned* tag: ${{ github.ref }} overwrite: true - name: Upload extra package uses: svenstaro/upload-release-action@v2 if: github.event_name == 'release' with: repo_token: ${{ secrets.GITHUB_TOKEN }} file_glob: true file: build_artifacts/v2ray-extra.zip tag: ${{ github.ref }} overwrite: true buildContainer: if: github.event_name == 'release' needs: signature name: Build And Push image runs-on: ubuntu-latest env: REGISTRY_USER: ${{ github.actor }} REGISTRY_PASSWORD: ${{ github.token }} IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} RELEASE_REPO: ${{ github.repository }} VERSION: ${{ github.event.release.tag_name }} TIMESTAMP: ${{ github.event.release.created_at }} PRERELEASE: ${{ github.event.release.prerelease }} VARIANTS: | # : std: extra:-extra ARCHS: | # : 32:386 64:amd64 arm32-v6:arm/v6 arm32-v7a:arm/v7 arm64-v8a:arm64 arm64-v8a:arm64/v8 steps: - uses: actions/checkout@v6 - name: Log in to ghcr.io uses: redhat-actions/podman-login@v1 with: username: ${{ env.REGISTRY_USER }} password: ${{ env.REGISTRY_PASSWORD }} registry: ${{ env.IMAGE_REGISTRY }} - name: Build Images id: build-image run: | image=v2ray tags= timestamp=$(date -d $TIMESTAMP +%s) versions="$VERSION" # full version (v1.2.3) versions="$versions $(echo $VERSION | cut -d. -f1,2)" # minor version (v1.2) if [ $PRERELEASE = false ]; then versions="$versions $(echo $VERSION | cut -d. -f1)" # major version (v1) versions="$versions latest" fi formatEach() { format=$1 shift 1 echo "$@" | xargs -n1 | xargs -i echo "$format" | xargs } for variant in $VARIANTS; do v2Variant=$(echo $variant | cut -d: -f1) containerVariant=$(echo $variant | cut -d: -f2) variantTags=$(formatEach "{}$containerVariant" $versions) tags="$tags $variantTags" for arch in $ARCHS; do v2Arch=$(echo $arch | cut -d: -f1) containerArch=$(echo $arch | cut -d: -f2) bash ./release/container/downloadAssets.sh $VERSION $v2Arch $containerArch $v2Variant for tag in $variantTags; do buildah bud \ --manifest $image:$tag \ --file ./release/container/Containerfile \ --platform linux/$containerArch \ --timestamp $timestamp \ --squash \ ./context/linux/$containerArch/$v2Variant done done done echo image=$image >> $GITHUB_OUTPUT echo tags=$tags >> $GITHUB_OUTPUT - name: Push To ghcr.io uses: redhat-actions/push-to-registry@v2 with: image: ${{ steps.build-image.outputs.image }} tags: ${{ steps.build-image.outputs.tags }} registry: ${{ env.IMAGE_REGISTRY }} ================================================ FILE: .github/workflows/sign.yml ================================================ name: Sign on: release: types: [released] jobs: sign: runs-on: ubuntu-latest steps: - name: Checkout default branch uses: actions/checkout@v6 - name: Grant it execution permission run: | chmod +x $GITHUB_WORKSPACE/release/requestsign.sh - name: Invoke release signing env: SIGN_SERVICE_PASSWORD: ${{ secrets.SIGN_SERVICE_PASSWORD }} SIGN_SERIVCE_URL: ${{ secrets.SIGN_SERIVCE_URL_V5 }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | export SIGN_VERSION=$(cat $GITHUB_EVENT_PATH| jq -r ".release.tag_name") echo $SIGN_VERSION $GITHUB_WORKSPACE/release/requestsign.sh ================================================ FILE: .github/workflows/stale.yml ================================================ name: Mark stale issues and pull requests on: schedule: - cron: "30 1 * * *" jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v10 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: "This issue is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days" stale-pr-message: "It has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days" days-before-stale: 120 days-before-close: 5 exempt-pr-labels: "planned" ================================================ FILE: .github/workflows/test.yml ================================================ name: Test on: push: branches: - master - v* - dev* paths: - "**/*.go" - "go.mod" - "go.sum" - ".github/workflows/test.yml" pull_request: types: [opened, synchronize, reopened] paths: - "**/*.go" - "go.mod" - "go.sum" - ".github/workflows/test.yml" jobs: test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [windows-latest, ubuntu-latest, macos-latest] steps: - name: Checkout codebase uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: ./go.mod cache-dependency-path: ./go.sum - name: Check Go modules run: | go mod tidy git diff --exit-code go.mod go.sum go mod verify - name: Run tests with coverage report run: | go test ./... -v -timeout=1h -coverprofile=coverage.out -covermode=atomic - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 if: runner.os == 'Linux' env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: slug: ${{ github.repository }} verbose: true fail_ci_if_error: true ================================================ FILE: .gitignore ================================================ # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ *.DS_Store .idea *.zip *.tar.gz v2ray v2ctl mockgen vprotogen !infra/vprotogen/ errorgen !common/errors/errorgen/ *.dat *~ [._]*.un~ .golangci.yml ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015-2025 V2Fly Community Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================
V2Ray

Project V

Project V is a set of network tools that helps you to build your own computer network. It secures your network connections and thus protects your privacy.

[![GitHub Test Badge](https://github.com/v2fly/v2ray-core/actions/workflows/test.yml/badge.svg)](https://github.com/v2fly/v2ray-core/actions/workflows/test.yml) [![codecov.io](https://codecov.io/gh/v2fly/v2ray-core/branch/master/graph/badge.svg?branch=master)](https://codecov.io/gh/v2fly/v2ray-core?branch=master) [![goreportcard](https://goreportcard.com/badge/github.com/v2fly/v2ray-core/v5)](https://goreportcard.com/report/github.com/v2fly/v2ray-core/v5) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/e150b7ede2114388921943bf23d95161)](https://app.codacy.com/gh/v2fly/v2ray-core/dashboard) [![Downloads](https://img.shields.io/github/downloads/v2fly/v2ray-core/total.svg)](https://github.com/v2fly/v2ray-core/releases/latest) ## Related Links - [Documentation](https://www.v2fly.org) and [Newcomer's Instructions](https://www.v2fly.org/guide/start.html) - Welcome to translate V2Ray documents via [Transifex](https://www.transifex.com/v2fly/public/) ## Packaging Status > If you are willing to package V2Ray for other distros/platforms, please let us know or seek for help via [GitHub issues](https://github.com/v2fly/v2ray-core/issues). [![Packaging status](https://repology.org/badge/vertical-allrepos/v2ray.svg)](https://repology.org/project/v2ray/versions) ## License [The MIT License (MIT)](https://raw.githubusercontent.com/v2fly/v2ray-core/master/LICENSE) ## Credits This repo relies on the following third-party projects: - In production: - [gorilla/websocket](https://github.com/gorilla/websocket) - [quic-go/quic-go](https://github.com/quic-go/quic-go) - [pires/go-proxyproto](https://github.com/pires/go-proxyproto) - [seiflotfy/cuckoofilter](https://github.com/seiflotfy/cuckoofilter) - [google/starlark-go](https://github.com/google/starlark-go) - [jhump/protoreflect](https://github.com/jhump/protoreflect) - [inetaf/netaddr](https://github.com/inetaf/netaddr) - For testing only: - [miekg/dns](https://github.com/miekg/dns) - [h12w/socks](https://github.com/h12w/socks) ================================================ FILE: SECURITY.md ================================================ # 安全策略 Security Policy ## 受支持的版本 Supported Versions 目前 v2ray-core 项目由 [V2Fly 社区](https://github.com/v2fly) 继续提供代码维护,由于精力有限且项目复杂度较高,只维护主线代码的功能和安全性完整。原则上主页的兼容性保证继续遵循, 如有例外另行说明。 Currently v2ray-core project is maintained by [V2Fly community](https://github.com/v2fly). Feature and security guarantee may only be limited to the master branch, though we would still try our best to follow the compatibility claims listed on the official website. ## 汇报安全风险 Reporting a Vulnerability 使用邮箱: security |at| v2fly.org。 Report to email: security |at| v2fly.org. GPG public key: ``` pub rsa4096 2020-06-02 [SC] [有效至:2022-01-02] E2E35E27914FB007C0D4B6DDB117BA3BE8B494A7 uid [ 绝对 ] V2Fly Developers sub rsa4096 2020-06-02 [E] [有效至:2022-01-02] sub rsa4096 2020-11-08 [S] [有效至:2022-01-02] // 用于 Debian / Ubuntu 签名 -----BEGIN PGP PUBLIC KEY BLOCK----- mQINBF7V7pQBEACozcw4/BlPgFWaz4AdN8HKSrCDLlN+/g7m4AKZIo13fAnDh+sJ 2H4NrWNr0xxgovbco5Xw4OSSwY1BuUhnb4AmIyxbwqUQD2UADe5xD6gMBwNiJTP4 02VCHhh7DnWTeLbAsUgRotxUCxsWVvd2F08SYGfJggOVftOnG+VNnwzTOvHWFVEw 1Pv1DaY7bKSA0voACerRbAPCYqhmElAGJHYNjBMaxqCaWFJWpAFfBxkvS1FDVyZk BsABhn6sOcGJn8EYSHUIXhWwqtkQCjBB4OOik+Jn+S2DFGyk5l1NrGRQtX8C0BYn nc7VaxtFOp5fnJ4y0GNd4AM9KO0/Ojosi6b64l407Fj9i9OXznmZUACQw2u+VcL3 qNy768hsTmka3pXzpRHZwYcOLOEr3jGHmLOtXgQ656OjF8Xd9DJ4cB42X8iBeqTp iQchHIdBpnu27ZbBFy09OMak+STB5zA0JmxDaC8b48mVkc0BMRXdYl7wWXJsEJf1 roAOr3RCBKiE840w0PLOTnUljfqazPYTwzs91oP+SeZjBmGOpaAh7bh5BVOpzPSE bdA61/n01GEb5bpOKpaTi9GviF3RCbfFnLKJnBq0vHvW9BqKTVFRPAKkBGuOPBdy 8MBNY+VY/2aP3ukZUoYe8Ypl9Q7dVPRjnoWaH0sEMzftoh+3s7GSSgAylQARAQAB tCBWMkZseSBEZXZlbG9wZXJzIDxkZXZAdjJmbHkub3JnPokCVAQTAQgAPgIbAwUL CQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBOLjXieRT7AHwNS23bEXujvotJSnBQJf p4leBQkC+1DKAAoJELEXujvotJSn124P/0swu9POvEQtxVlRzNh2VjAGHZ5NEDnl pMrhfC5ryCYtlVS/kc2WwRhIRHKzr9nbamgSxUCiyLagfnIjhIvAohun49grYNzG MZWRURiuFrCnYbD7juJTvfbzZCzJk7LPsdnqHWr8fYcOZMTOZVzQiQB2jUx2KeRm yV8aV21Z8gMLqSGjs06a0UaRbKB0FSysTURm91/jFmiH43aG1s/LcB9/lKf5HpNl 9or6LrEOrokAwtkMSBYTqm7Dp1j+cK0iOMw2CmMqmQZkV+i6msYrQRiX/X6YufiM wfMMSdOZOz9KG+k+C6N1swSbGeDMrJfnDUDbvrAXKhDjNgY7UBwbk69Abd7Y9aQz /jVmrFEWt4lisBxglBot60CRUTM2boK/uQS5zBCJhemeg14F9Q/FRiUTlS8jQoeK PWeK2lagYJS8lpJZLXkqe4xSpjCgoT0Z+lYSfTjx+T0AFF+xz5E243Lb5kDxwnR9 Y5CZt3vV6GWBYOt9MEL3pk7AnYyNT1y1KIiMyONh/Z1koUdHr4J9exllnsmAJQUa W/j0UtVsLsvUjFv9RTr9w5p/U0J0VLIN0YOpx4wYaBEwFIa8lsL+Ey1Vphkvvjfz uMRAHe4v+axWb1f1hVCBjtyCVyvzf+i9RTAYsBJ3MJ0C8cvvrm10N9B7MHh0JZA5 PcJSilailp1TuQINBF7V7pQBEADkQdO75smeKnmPt0/aNNlb7JDOSWW5VY0kYgx3 6Toh139JstIQ2xz0CLSGReizUFB6eR3DXmezLrmhkgN2Aq5A+hCtFAJwWKuKr1HS usvJ1el9h0oh7IO+tF8E/gNYwWfabjPX27FGVCHR1qG7ffN51Bghrnwi1T4YC98E R9EGU6N0Xs9DeIJL9WQPH/DF22251i/OAXkqKVGn3PNe2cBsp0yKxr9mlSyzjrha KXokPiPcvNqlnkiDCgfiRj7c2C2Lyl9PoEiGpsNZaCZYkMPgjM0xiLenQddwRyOU z2cLG3d8WdCTRyHSZd/YQtSi5R6AnkJEsVtUiDN5zwNFVpQlTq3jNHsVUpjFU2nK ourTZVCCLbAC60VTdxLN6eFO0f+lS2WjyJ7uZ9SGbS6uP0jMNphH/QjVF848bWXs 1CuZty5QQY7+MTNUAhSWWntrpTkdXYqT0zUqiOc1YNnkfg3hvC4d0dbnFTfcyZnB Sg8e7/9n6+ms75/deYgnLuA6h7pkIcflm7pUMfVKXKz5Vlc8FC9ia0UtobeKBKqi jObfiO/zmNL0HQBeX0e8GkJrCyv6ikD8cUqsmVtgw7jdxGsV0SL5CddDnGKsc68O pGDmkAuRqR3QtXju/4r7a8IEVveGWc3rUvddYrtqbbCNWCN0JKX13PEvbNAm+2eD MGQtcQARAQABiQI8BBgBCAAmAhsMFiEE4uNeJ5FPsAfA1LbdsRe6O+i0lKcFAl+n dwcFCQL7PnMACgkQsRe6O+i0lKeWfxAApopL5I9p4btmkcLIg2lkA1n+czFekbdr 2tjFKrBER4QWkyDCUE8QaVo/ECveTHmnxrTB/djW6xqPVS77PL8xOATIYTo6qU38 oTCB1T7/P2L9qI72BzcRY5f9ZPyJhCtrkvjCPzjUjw+ZIPIOgQcWgKHWnE+OyUKD 0GkVEUME3QP5S4Nr3XGrgS7oxDAmD52u7pn0mSk5WmEcLW0oGwsVdc4aDXxpX+u/ gkBZysmAuomPov7iXVosMakl+4rz30yPcrL9A81m1WAeB3PGkpaO3B++8Ql+FBCQ OrLtPn/nnIzEuAXB1Hd8vYzxtRM2CZvhRExM7xofnhkBJOtR/ddfbJa7H5+Aruc0 4S0JIaqMCrC6tZezjTACAzrWULmZZGmrHbLrmXBuLk0huRkeIRnDzHP+DoE2UciL 3hR9EGOHX9O/dGb3bb3y11LAf7GI28ZG7So1GeoFkEOga1IJnsBnXCqwM8vbDDWq /7aLb3/m0gT7DUfjeXKfWPJXcnaq8r4llHzDn2i6ax4Uq/brCOLj9ovVGIctZTbt yvsFOc1bVkSuUM+pMkCtBx80/sJSB2Nu94S6osdaUlRE+jaCcqEbPd+G68Yd0Khi CL8zF1a3dX1dpuVFTLNpXOgrviGBzXQmzFeil7mWFs0l+1XZOPz9nhmRrMn6wV3n i4KItRSJAXy5Ag0EX6d5hQEQAMsVyLTXdybeei2nWDb5jtzzC3AtSnPWtKG4B86C BXncaZpU43hKI3oduW2+42eM8n8KTvO11r9xv4zKATfaHBZq2hkKZdDQjuSstovr a3hapHHknHeNVTg3yuiakKzpr6FK23W/GE1lJfhz254v9+dRV0KazWksXvpGEdgI +6sC4Nr5bKgJVEQibyrrL0gmzlVB/oQU/W4eGvk21zmgMlHri+edBLpVtlCmn7k/ 0t+2X9D1Pq2nkjMUurB9EJ1z24LMldmPOl6P7iJCx9kSUjcHrEg56q5VSZq50FAj DeSjAqsdussI8cdstCMktE9nhizxVKFXpbXifqoYfJwCo23wFqQJpyPgQqHIT12s GWRUa/MF6hRYg/5CyeadDmkmnKPTPjmQ2S2SFNXX7xs+dZKvIvXP30z4cpuVY8i8 chZSRNb8K0L9T0Jme7CPm28F6lvDUkNDQ1WErXZruHbOKwQOfQBdXK3nedOiUpBt 401HVlGUJSInfEb3JXU01tRqnnzI/y5z7cWCGEMEa6TeaCrMbVvl8xeAA1w/nw0y zHz6/Pnf4TITuCH22aa7+xfgpq8gRLhUUws89mbQT+9fd8tT65+Q8xcaLCyzrLAq zND5sVZ4/PwaYc8UNZcHjeQR7aYWI1xgr/IwY1wyDWZLbWgkk0HVxpvYdMEpJryD AyMdABEBAAGJBHIEGAEIACYWIQTi414nkU+wB8DUtt2xF7o76LSUpwUCX6d5hQIb AgUJAim2AAJACRCxF7o76LSUp8F0IAQZAQgAHRYhBK8FZLGpNMztuW02YbJOz+X/ ddOBBQJfp3mFAAoJELJOz+X/ddOBNKQP/0nwIC4R9gQhY53vME7VA7elIrBiSM6d Va26a7J1nrCcpDAE7Lp0TqzrDMqyen+IL4X5QK5sKTgenYTgjppEJIQn+Wup54ix I+YOQ8MVLfN6/3QPACWMngSPRF+UKDg4hyTCEL+/GCgTp58oXrl/YIO6Oqt5drog w4+4ufU1/eKTb2ruGULGl9jZvFSZpLdsvJ19xJB2kC1k8GVNu7MnUL+S2pU/9kO4 5EZ/jEa1wT45zev+HdmzX5TYW6SLaI9HKHMqbQz2EHc3tRYIDaz3FE3s4VdMjqpp e42SvkOYaguc6cXToDbzBmU+iWGlXCTHfNhxwxoUYcKZlDEkEtvSYHJOb0k9eqbT gvMb5GjbAgqqwOBwtN3v790j8jEG+cdXR3qHcEx0bw3F2Bd18U7j946OxHLKE5Xk 2sWEG422maVrE9o1DdeTV5oDFNNPBzqfjgGBZCCKrjkpldhDOHeoDU2aFMJ7yVqw ZwKwJ5f8fdNS13UnQVwGsZ2BsW1cox5ZGZ/C5A7mfSF1WAgJcYIw4M2JQbDn4Yuw yqjyg53lT3OurBONbEZ7unnsLqpT9qKwZ1qCemqGRJieXXxJwl7G4gBgZbH0rBJR 6dhbyt4c2JE8MMdC65mDWneltNM6pttC/j5jCuvIlZGACZ91UuLLediJJWAlOJ+1 fBQ0m8TD6d8ZrakP/RFMLZrxh9WPaFB43sW/b1Fq2h933HQ29oSQFuXhsHsx1Vaq HTRTcBB7kywAr9+zMYsOsk0/WnoZNGoMkUWu/gFkb6CdUcsdEumgyZ8S24VoBCHB T1fD/8eOA5K82hwAFcKbPwuuTLtf9b9HB4/xsObfcczTeqIknzIPsGlgVz4w1c9a StSo4iI4bCSLL+/mqiXZ+ArXJ/z4Vejl92fNLWVOlOrjkBV+AY6iAFCCsxJ1O5ud 5a5r1bUeBXd0BcQ1m/hpjawMC1y0SkIBTQCgxIQoPoxJ27hHNIN1R2nkqfY9vboQ 7O0uIHF8fmuz93xg68ZTW0JHwOw4Mz88lGibE2laHApjKWZAtF/i+LlhbnewtESL EuGTT7gt7cSHgnBiDEIm5UJVEGeM0sMReztxy9V7glohH5DV8GpVK/GncKlsrh1K BuEuz7IrqKlBzhsDy0SrNZpX7EzsiU1uvoA6teT4EPey8qXH+7WR9B2ad1Zc5yE3 zv4BpnWkkJp8qdYu4fdCs/mrmnBR5G1YdOAIlNWhU74Wdyq+W4HfTWMgvJHmElnZ UvQ9RDTWnw2+3n2ATeLf9ZwW1g4/Dqh55OaLtJZo5me8vU9W+vkm34xzfVfD/mus ljogw5eiGyj8j3lUVjYWu28l/bz0zDUueWmHhV8E8z0Cn7OhrHPpUCHx2Aep =quYd -----END PGP PUBLIC KEY BLOCK----- ``` ================================================ FILE: annotations.go ================================================ package core // Annotation is a concept in V2Ray. This struct is only for documentation. It is not used anywhere. // Annotations begin with "v2ray:" in comment, as metadata of functions or types. type Annotation struct { // API is for types or functions that can be used in other libs. Possible values are: // // * v2ray:api:beta for types or functions that are ready for use, but maybe changed in the future. // * v2ray:api:stable for types or functions with guarantee of backward compatibility. // * v2ray:api:deprecated for types or functions that should not be used anymore. // // Types or functions without api annotation should not be used externally. API string } ================================================ FILE: app/app.go ================================================ // Package app contains feature implementations of V2Ray. The features may be enabled during runtime. package app ================================================ FILE: app/browserforwarder/config.pb.go ================================================ package browserforwarder import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Config is the settings for BrowserForwarder. type Config struct { state protoimpl.MessageState `protogen:"open.v1"` ListenAddr string `protobuf:"bytes,1,opt,name=listen_addr,json=listenAddr,proto3" json:"listen_addr,omitempty"` ListenPort int32 `protobuf:"varint,2,opt,name=listen_port,json=listenPort,proto3" json:"listen_port,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_browserforwarder_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_browserforwarder_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_browserforwarder_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetListenAddr() string { if x != nil { return x.ListenAddr } return "" } func (x *Config) GetListenPort() int32 { if x != nil { return x.ListenPort } return 0 } var File_app_browserforwarder_config_proto protoreflect.FileDescriptor const file_app_browserforwarder_config_proto_rawDesc = "" + "\n" + "!app/browserforwarder/config.proto\x12\x1fv2ray.core.app.browserforwarder\x1a common/protoext/extensions.proto\"b\n" + "\x06Config\x12\x1f\n" + "\vlisten_addr\x18\x01 \x01(\tR\n" + "listenAddr\x12\x1f\n" + "\vlisten_port\x18\x02 \x01(\x05R\n" + "listenPort:\x16\x82\xb5\x18\x12\n" + "\aservice\x12\abrowserB~\n" + "#com.v2ray.core.app.browserforwarderP\x01Z3github.com/v2fly/v2ray-core/v5/app/browserforwarder\xaa\x02\x1fV2Ray.Core.App.Browserforwarderb\x06proto3" var ( file_app_browserforwarder_config_proto_rawDescOnce sync.Once file_app_browserforwarder_config_proto_rawDescData []byte ) func file_app_browserforwarder_config_proto_rawDescGZIP() []byte { file_app_browserforwarder_config_proto_rawDescOnce.Do(func() { file_app_browserforwarder_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_browserforwarder_config_proto_rawDesc), len(file_app_browserforwarder_config_proto_rawDesc))) }) return file_app_browserforwarder_config_proto_rawDescData } var file_app_browserforwarder_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_app_browserforwarder_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.browserforwarder.Config } var file_app_browserforwarder_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_app_browserforwarder_config_proto_init() } func file_app_browserforwarder_config_proto_init() { if File_app_browserforwarder_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_browserforwarder_config_proto_rawDesc), len(file_app_browserforwarder_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_browserforwarder_config_proto_goTypes, DependencyIndexes: file_app_browserforwarder_config_proto_depIdxs, MessageInfos: file_app_browserforwarder_config_proto_msgTypes, }.Build() File_app_browserforwarder_config_proto = out.File file_app_browserforwarder_config_proto_goTypes = nil file_app_browserforwarder_config_proto_depIdxs = nil } ================================================ FILE: app/browserforwarder/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.browserforwarder; option csharp_namespace = "V2Ray.Core.App.Browserforwarder"; option go_package = "github.com/v2fly/v2ray-core/v5/app/browserforwarder"; option java_package = "com.v2ray.core.app.browserforwarder"; option java_multiple_files = true; import "common/protoext/extensions.proto"; // Config is the settings for BrowserForwarder. message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "browser"; string listen_addr = 1; int32 listen_port = 2; } ================================================ FILE: app/browserforwarder/errors.generated.go ================================================ package browserforwarder import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/browserforwarder/forwarder.go ================================================ package browserforwarder import ( "bytes" "context" "io" "net/http" "strings" "time" "github.com/v2fly/BrowserBridge/handler" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/platform/securedload" "github.com/v2fly/v2ray-core/v5/features/extension" "github.com/v2fly/v2ray-core/v5/transport/internet" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type Forwarder struct { ctx context.Context forwarder *handler.HTTPHandle httpserver *http.Server config *Config } func (f *Forwarder) ServeHTTP(writer http.ResponseWriter, request *http.Request) { requestPath := request.URL.Path[1:] switch requestPath { case "": fallthrough case "index.js": BridgeResource(writer, request, requestPath) case "link": f.forwarder.ServeBridge(writer, request) } } func (f *Forwarder) DialWebsocket(url string, header http.Header) (io.ReadWriteCloser, error) { protocolHeader := false protocolHeaderValue := "" unsupportedHeader := false for k, v := range header { if k == "Sec-Websocket-Protocol" { protocolHeader = true protocolHeaderValue = v[0] } else { unsupportedHeader = true } } if unsupportedHeader { return nil, newError("unsupported header used, only Sec-Websocket-Protocol is supported for forwarder") } if !protocolHeader { return f.forwarder.Dial(url) } return f.forwarder.Dial2(url, protocolHeaderValue) } func (f *Forwarder) Type() interface{} { return extension.BrowserForwarderType() } func (f *Forwarder) Start() error { if f.config.ListenAddr != "" { f.forwarder = handler.NewHttpHandle() f.httpserver = &http.Server{Handler: f} var listener net.Listener var err error address := net.ParseAddress(f.config.ListenAddr) switch { case address.Family().IsIP(): listener, err = internet.ListenSystem(f.ctx, &net.TCPAddr{IP: address.IP(), Port: int(f.config.ListenPort)}, nil) case strings.EqualFold(address.Domain(), "localhost"): listener, err = internet.ListenSystem(f.ctx, &net.TCPAddr{IP: net.IP{127, 0, 0, 1}, Port: int(f.config.ListenPort)}, nil) default: return newError("forwarder cannot listen on the address: ", address) } if err != nil { return newError("forwarder cannot listen on the port ", f.config.ListenPort).Base(err) } go func() { if err := f.httpserver.Serve(listener); err != nil { newError("cannot serve http forward server").Base(err).WriteToLog() } }() } return nil } func (f *Forwarder) Close() error { if f.httpserver != nil { return f.httpserver.Close() } return nil } func BridgeResource(rw http.ResponseWriter, r *http.Request, path string) { content := path if content == "" { content = "index.html" } data, err := securedload.GetAssetSecured("browserforwarder/" + content) if err != nil { err = newError("cannot load necessary resources").Base(err) http.Error(rw, err.Error(), http.StatusForbidden) return } http.ServeContent(rw, r, path, time.Now(), bytes.NewReader(data)) } func NewForwarder(ctx context.Context, cfg *Config) *Forwarder { return &Forwarder{config: cfg, ctx: ctx} } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { return NewForwarder(ctx, cfg.(*Config)), nil })) } ================================================ FILE: app/commander/commander.go ================================================ package commander //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "net" "sync" "google.golang.org/grpc" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/infra/conf/v5cfg" ) type CommanderIfce interface { features.Feature ExtractGrpcServer() *grpc.Server } // Commander is a V2Ray feature that provides gRPC methods to external clients. type Commander struct { sync.Mutex server *grpc.Server services []Service ohm outbound.Manager tag string } // NewCommander creates a new Commander based on the given config. func NewCommander(ctx context.Context, config *Config) (*Commander, error) { c := &Commander{ tag: config.Tag, } common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) { c.ohm = om })) for _, rawConfig := range config.Service { config, err := serial.GetInstanceOf(rawConfig) if err != nil { return nil, err } rawService, err := common.CreateObject(ctx, config) if err != nil { return nil, err } service, ok := rawService.(Service) if !ok { return nil, newError("not a Service.") } c.services = append(c.services, service) } return c, nil } // Type implements common.HasType. func (c *Commander) Type() interface{} { return (*CommanderIfce)(nil) } // Start implements common.Runnable. func (c *Commander) Start() error { c.Lock() c.server = grpc.NewServer() for _, service := range c.services { service.Register(c.server) } c.Unlock() listener := &OutboundListener{ buffer: make(chan net.Conn, 4), done: done.New(), } go func() { if err := c.server.Serve(listener); err != nil { newError("failed to start grpc server").Base(err).AtError().WriteToLog() } }() if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil { newError("failed to remove existing handler").WriteToLog() } return c.ohm.AddHandler(context.Background(), &Outbound{ tag: c.tag, listener: listener, }) } // Close implements common.Closable. func (c *Commander) Close() error { c.Lock() defer c.Unlock() if c.server != nil { c.server.Stop() c.server = nil } return nil } // ExtractGrpcServer extracts the gRPC server from Commander. // Private function for core code base. func (c *Commander) ExtractGrpcServer() *grpc.Server { c.Lock() defer c.Unlock() return c.server } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { return NewCommander(ctx, cfg.(*Config)) })) } func init() { common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { simplifiedConfig := cfg.(*SimplifiedConfig) fullConfig := &Config{ Tag: simplifiedConfig.Tag, Service: nil, } for _, v := range simplifiedConfig.Name { pack, err := v5cfg.LoadHeterogeneousConfigFromRawJSON(ctx, "grpcservice", v, []byte("{}")) if err != nil { return nil, err } fullConfig.Service = append(fullConfig.Service, serial.ToTypedMessage(pack)) } return common.CreateObject(ctx, fullConfig) })) } ================================================ FILE: app/commander/config.pb.go ================================================ package commander import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Config is the settings for Commander. type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // Tag of the outbound handler that handles grpc connections. Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` // Services that supported by this server. All services must implement Service // interface. Service []*anypb.Any `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_commander_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_commander_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_commander_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetTag() string { if x != nil { return x.Tag } return "" } func (x *Config) GetService() []*anypb.Any { if x != nil { return x.Service } return nil } // ReflectionConfig is the placeholder config for ReflectionService. type ReflectionConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ReflectionConfig) Reset() { *x = ReflectionConfig{} mi := &file_app_commander_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ReflectionConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReflectionConfig) ProtoMessage() {} func (x *ReflectionConfig) ProtoReflect() protoreflect.Message { mi := &file_app_commander_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReflectionConfig.ProtoReflect.Descriptor instead. func (*ReflectionConfig) Descriptor() ([]byte, []int) { return file_app_commander_config_proto_rawDescGZIP(), []int{1} } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` Name []string `protobuf:"bytes,2,rep,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_app_commander_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_app_commander_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_app_commander_config_proto_rawDescGZIP(), []int{2} } func (x *SimplifiedConfig) GetTag() string { if x != nil { return x.Tag } return "" } func (x *SimplifiedConfig) GetName() []string { if x != nil { return x.Name } return nil } var File_app_commander_config_proto protoreflect.FileDescriptor const file_app_commander_config_proto_rawDesc = "" + "\n" + "\x1aapp/commander/config.proto\x12\x18v2ray.core.app.commander\x1a\x19google/protobuf/any.proto\x1a common/protoext/extensions.proto\"J\n" + "\x06Config\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12.\n" + "\aservice\x18\x02 \x03(\v2\x14.google.protobuf.AnyR\aservice\"1\n" + "\x10ReflectionConfig:\x1d\x82\xb5\x18\x19\n" + "\vgrpcservice\x12\n" + "reflection\"R\n" + "\x10SimplifiedConfig\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12\x12\n" + "\x04name\x18\x02 \x03(\tR\x04name:\x18\x82\xb5\x18\x14\n" + "\aservice\x12\tcommanderBi\n" + "\x1ccom.v2ray.core.app.commanderP\x01Z,github.com/v2fly/v2ray-core/v5/app/commander\xaa\x02\x18V2Ray.Core.App.Commanderb\x06proto3" var ( file_app_commander_config_proto_rawDescOnce sync.Once file_app_commander_config_proto_rawDescData []byte ) func file_app_commander_config_proto_rawDescGZIP() []byte { file_app_commander_config_proto_rawDescOnce.Do(func() { file_app_commander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_commander_config_proto_rawDesc), len(file_app_commander_config_proto_rawDesc))) }) return file_app_commander_config_proto_rawDescData } var file_app_commander_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_app_commander_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.commander.Config (*ReflectionConfig)(nil), // 1: v2ray.core.app.commander.ReflectionConfig (*SimplifiedConfig)(nil), // 2: v2ray.core.app.commander.SimplifiedConfig (*anypb.Any)(nil), // 3: google.protobuf.Any } var file_app_commander_config_proto_depIdxs = []int32{ 3, // 0: v2ray.core.app.commander.Config.service:type_name -> google.protobuf.Any 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_app_commander_config_proto_init() } func file_app_commander_config_proto_init() { if File_app_commander_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_commander_config_proto_rawDesc), len(file_app_commander_config_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_commander_config_proto_goTypes, DependencyIndexes: file_app_commander_config_proto_depIdxs, MessageInfos: file_app_commander_config_proto_msgTypes, }.Build() File_app_commander_config_proto = out.File file_app_commander_config_proto_goTypes = nil file_app_commander_config_proto_depIdxs = nil } ================================================ FILE: app/commander/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.commander; option csharp_namespace = "V2Ray.Core.App.Commander"; option go_package = "github.com/v2fly/v2ray-core/v5/app/commander"; option java_package = "com.v2ray.core.app.commander"; option java_multiple_files = true; import "google/protobuf/any.proto"; import "common/protoext/extensions.proto"; // Config is the settings for Commander. message Config { // Tag of the outbound handler that handles grpc connections. string tag = 1; // Services that supported by this server. All services must implement Service // interface. repeated google.protobuf.Any service = 2; } // ReflectionConfig is the placeholder config for ReflectionService. message ReflectionConfig { option (v2ray.core.common.protoext.message_opt).type = "grpcservice"; option (v2ray.core.common.protoext.message_opt).short_name = "reflection"; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "commander"; string tag = 1; repeated string name = 2; } ================================================ FILE: app/commander/errors.generated.go ================================================ package commander import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/commander/outbound.go ================================================ package commander import ( "context" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/transport" ) func NewOutboundListener() *OutboundListener { return &OutboundListener{ buffer: make(chan net.Conn, 4), done: done.New(), } } // OutboundListener is a net.Listener for listening gRPC connections. type OutboundListener struct { buffer chan net.Conn done *done.Instance } func (l *OutboundListener) add(conn net.Conn) { select { case l.buffer <- conn: case <-l.done.Wait(): conn.Close() default: conn.Close() } } // Accept implements net.Listener. func (l *OutboundListener) Accept() (net.Conn, error) { select { case <-l.done.Wait(): return nil, newError("listen closed") case c := <-l.buffer: return c, nil } } // Close implement net.Listener. func (l *OutboundListener) Close() error { common.Must(l.done.Close()) L: for { select { case c := <-l.buffer: c.Close() default: break L } } return nil } // Addr implements net.Listener. func (l *OutboundListener) Addr() net.Addr { return &net.TCPAddr{ IP: net.IP{0, 0, 0, 0}, Port: 0, } } func NewOutbound(tag string, listener *OutboundListener) *Outbound { return &Outbound{ tag: tag, listener: listener, } } // Outbound is a outbound.Handler that handles gRPC connections. type Outbound struct { tag string listener *OutboundListener access sync.RWMutex closed bool } // Dispatch implements outbound.Handler. func (co *Outbound) Dispatch(ctx context.Context, link *transport.Link) { co.access.RLock() if co.closed { common.Interrupt(link.Reader) common.Interrupt(link.Writer) co.access.RUnlock() return } closeSignal := done.New() c := net.NewConnection(net.ConnectionInputMulti(link.Writer), net.ConnectionOutputMulti(link.Reader), net.ConnectionOnClose(closeSignal)) co.listener.add(c) co.access.RUnlock() <-closeSignal.Wait() } // Tag implements outbound.Handler. func (co *Outbound) Tag() string { return co.tag } // Start implements common.Runnable. func (co *Outbound) Start() error { co.access.Lock() co.closed = false co.access.Unlock() return nil } // Close implements common.Closable. func (co *Outbound) Close() error { co.access.Lock() defer co.access.Unlock() co.closed = true return co.listener.Close() } ================================================ FILE: app/commander/service.go ================================================ package commander import ( "context" "google.golang.org/grpc" "google.golang.org/grpc/reflection" "github.com/v2fly/v2ray-core/v5/common" ) // Service is a Commander service. type Service interface { // Register registers the service itself to a gRPC server. Register(*grpc.Server) } type reflectionService struct{} func (r reflectionService) Register(s *grpc.Server) { reflection.Register(s) } func init() { common.Must(common.RegisterConfig((*ReflectionConfig)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { return reflectionService{}, nil })) } ================================================ FILE: app/commander/webcommander/config.pb.go ================================================ package webcommander import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` WebRoot []byte `protobuf:"bytes,2,opt,name=web_root,json=webRoot,proto3" json:"web_root,omitempty"` WebRootFile string `protobuf:"bytes,96002,opt,name=web_root_file,json=webRootFile,proto3" json:"web_root_file,omitempty"` ApiMountpoint string `protobuf:"bytes,3,opt,name=api_mountpoint,json=apiMountpoint,proto3" json:"api_mountpoint,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_commander_webcommander_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_commander_webcommander_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_commander_webcommander_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetTag() string { if x != nil { return x.Tag } return "" } func (x *Config) GetWebRoot() []byte { if x != nil { return x.WebRoot } return nil } func (x *Config) GetWebRootFile() string { if x != nil { return x.WebRootFile } return "" } func (x *Config) GetApiMountpoint() string { if x != nil { return x.ApiMountpoint } return "" } var File_app_commander_webcommander_config_proto protoreflect.FileDescriptor const file_app_commander_webcommander_config_proto_rawDesc = "" + "\n" + "'app/commander/webcommander/config.proto\x12%v2ray.core.app.commander.webcommander\x1a common/protoext/extensions.proto\"\xaf\x01\n" + "\x06Config\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12\x19\n" + "\bweb_root\x18\x02 \x01(\fR\awebRoot\x124\n" + "\rweb_root_file\x18\x82\xee\x05 \x01(\tB\x0e\x82\xb5\x18\n" + "\"\bweb_rootR\vwebRootFile\x12%\n" + "\x0eapi_mountpoint\x18\x03 \x01(\tR\rapiMountpoint:\x1b\x82\xb5\x18\x17\n" + "\aservice\x12\fwebcommanderB\x90\x01\n" + ")com.v2ray.core.app.commander.webcommanderP\x01Z9github.com/v2fly/v2ray-core/v5/app/commander/webcommander\xaa\x02%V2Ray.Core.App.Commander.WebCommanderb\x06proto3" var ( file_app_commander_webcommander_config_proto_rawDescOnce sync.Once file_app_commander_webcommander_config_proto_rawDescData []byte ) func file_app_commander_webcommander_config_proto_rawDescGZIP() []byte { file_app_commander_webcommander_config_proto_rawDescOnce.Do(func() { file_app_commander_webcommander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_commander_webcommander_config_proto_rawDesc), len(file_app_commander_webcommander_config_proto_rawDesc))) }) return file_app_commander_webcommander_config_proto_rawDescData } var file_app_commander_webcommander_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_app_commander_webcommander_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.commander.webcommander.Config } var file_app_commander_webcommander_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_app_commander_webcommander_config_proto_init() } func file_app_commander_webcommander_config_proto_init() { if File_app_commander_webcommander_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_commander_webcommander_config_proto_rawDesc), len(file_app_commander_webcommander_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_commander_webcommander_config_proto_goTypes, DependencyIndexes: file_app_commander_webcommander_config_proto_depIdxs, MessageInfos: file_app_commander_webcommander_config_proto_msgTypes, }.Build() File_app_commander_webcommander_config_proto = out.File file_app_commander_webcommander_config_proto_goTypes = nil file_app_commander_webcommander_config_proto_depIdxs = nil } ================================================ FILE: app/commander/webcommander/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.commander.webcommander; option csharp_namespace = "V2Ray.Core.App.Commander.WebCommander"; option go_package = "github.com/v2fly/v2ray-core/v5/app/commander/webcommander"; option java_package = "com.v2ray.core.app.commander.webcommander"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "webcommander"; string tag = 1; bytes web_root = 2; string web_root_file = 96002 [(v2ray.core.common.protoext.field_opt).convert_time_read_file_into = "web_root"]; string api_mountpoint = 3; } ================================================ FILE: app/commander/webcommander/errors.generated.go ================================================ package webcommander import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/commander/webcommander/webcommander.go ================================================ package webcommander import ( "archive/zip" "bytes" "context" "io/fs" "net/http" "strings" "sync" "time" "github.com/improbable-eng/grpc-web/go/grpcweb" "google.golang.org/grpc" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/commander" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/outbound" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func newWebCommander(ctx context.Context, config *Config) (*WebCommander, error) { if config == nil { return nil, newError("config is nil") } if config.Tag == "" { return nil, newError("config.Tag is empty") } var webRootfs fs.FS if config.WebRoot != nil { zipReader, err := zip.NewReader(bytes.NewReader(config.WebRoot), int64(len(config.WebRoot))) if err != nil { return nil, newError("failed to create zip reader").Base(err) } webRootfs = zipReader } return &WebCommander{ctx: ctx, config: config, webRootfs: webRootfs}, nil } type WebCommander struct { sync.Mutex ctx context.Context ohm outbound.Manager cm commander.CommanderIfce server *http.Server wrappedGrpc *grpcweb.WrappedGrpcServer webRootfs fs.FS config *Config } func (w *WebCommander) ServeHTTP(writer http.ResponseWriter, request *http.Request) { apiPath := w.config.ApiMountpoint if strings.HasPrefix(request.URL.Path, apiPath) { request.URL.Path = strings.TrimPrefix(request.URL.Path, apiPath) if w.wrappedGrpc.IsGrpcWebRequest(request) { w.wrappedGrpc.ServeHTTP(writer, request) return } } if w.webRootfs != nil { http.ServeFileFS(writer, request, w.webRootfs, request.URL.Path) return } writer.WriteHeader(http.StatusNotFound) } func (w *WebCommander) asyncStart() { var grpcServer *grpc.Server for { grpcServer = w.cm.ExtractGrpcServer() if grpcServer != nil { break } time.Sleep(time.Second) } listener := commander.NewOutboundListener() wrappedGrpc := grpcweb.WrapServer(grpcServer) w.server = &http.Server{} w.wrappedGrpc = wrappedGrpc w.server.Handler = w go func() { err := w.server.Serve(listener) if err != nil { newError("failed to serve HTTP").Base(err).WriteToLog() } }() if err := w.ohm.RemoveHandler(context.Background(), w.config.Tag); err != nil { newError("failed to remove existing handler").WriteToLog() } if err := w.ohm.AddHandler(context.Background(), commander.NewOutbound(w.config.Tag, listener)); err != nil { newError("failed to add handler").Base(err).WriteToLog() } } func (w *WebCommander) Type() interface{} { return (*WebCommander)(nil) } func (w *WebCommander) Start() error { if err := core.RequireFeatures(w.ctx, func(cm commander.CommanderIfce, om outbound.Manager) { w.Lock() defer w.Unlock() w.cm = cm w.ohm = om go w.asyncStart() }); err != nil { return err } return nil } func (w *WebCommander) Close() error { w.Lock() defer w.Unlock() if w.server != nil { if err := w.server.Close(); err != nil { return newError("failed to close http server").Base(err) } w.server = nil } return nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return newWebCommander(ctx, config.(*Config)) })) } ================================================ FILE: app/dispatcher/config.pb.go ================================================ package dispatcher import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type SessionConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SessionConfig) Reset() { *x = SessionConfig{} mi := &file_app_dispatcher_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SessionConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SessionConfig) ProtoMessage() {} func (x *SessionConfig) ProtoReflect() protoreflect.Message { mi := &file_app_dispatcher_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SessionConfig.ProtoReflect.Descriptor instead. func (*SessionConfig) Descriptor() ([]byte, []int) { return file_app_dispatcher_config_proto_rawDescGZIP(), []int{0} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Settings *SessionConfig `protobuf:"bytes,1,opt,name=settings,proto3" json:"settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_dispatcher_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_dispatcher_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_dispatcher_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetSettings() *SessionConfig { if x != nil { return x.Settings } return nil } var File_app_dispatcher_config_proto protoreflect.FileDescriptor const file_app_dispatcher_config_proto_rawDesc = "" + "\n" + "\x1bapp/dispatcher/config.proto\x12\x19v2ray.core.app.dispatcher\"\x15\n" + "\rSessionConfigJ\x04\b\x01\x10\x02\"N\n" + "\x06Config\x12D\n" + "\bsettings\x18\x01 \x01(\v2(.v2ray.core.app.dispatcher.SessionConfigR\bsettingsBl\n" + "\x1dcom.v2ray.core.app.dispatcherP\x01Z-github.com/v2fly/v2ray-core/v5/app/dispatcher\xaa\x02\x19V2Ray.Core.App.Dispatcherb\x06proto3" var ( file_app_dispatcher_config_proto_rawDescOnce sync.Once file_app_dispatcher_config_proto_rawDescData []byte ) func file_app_dispatcher_config_proto_rawDescGZIP() []byte { file_app_dispatcher_config_proto_rawDescOnce.Do(func() { file_app_dispatcher_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dispatcher_config_proto_rawDesc), len(file_app_dispatcher_config_proto_rawDesc))) }) return file_app_dispatcher_config_proto_rawDescData } var file_app_dispatcher_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_app_dispatcher_config_proto_goTypes = []any{ (*SessionConfig)(nil), // 0: v2ray.core.app.dispatcher.SessionConfig (*Config)(nil), // 1: v2ray.core.app.dispatcher.Config } var file_app_dispatcher_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.app.dispatcher.Config.settings:type_name -> v2ray.core.app.dispatcher.SessionConfig 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_app_dispatcher_config_proto_init() } func file_app_dispatcher_config_proto_init() { if File_app_dispatcher_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dispatcher_config_proto_rawDesc), len(file_app_dispatcher_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_dispatcher_config_proto_goTypes, DependencyIndexes: file_app_dispatcher_config_proto_depIdxs, MessageInfos: file_app_dispatcher_config_proto_msgTypes, }.Build() File_app_dispatcher_config_proto = out.File file_app_dispatcher_config_proto_goTypes = nil file_app_dispatcher_config_proto_depIdxs = nil } ================================================ FILE: app/dispatcher/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.dispatcher; option csharp_namespace = "V2Ray.Core.App.Dispatcher"; option go_package = "github.com/v2fly/v2ray-core/v5/app/dispatcher"; option java_package = "com.v2ray.core.app.dispatcher"; option java_multiple_files = true; message SessionConfig { reserved 1; } message Config { SessionConfig settings = 1; } ================================================ FILE: app/dispatcher/default.go ================================================ package dispatcher //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "strings" "sync" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/strmatcher" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" routing_session "github.com/v2fly/v2ray-core/v5/features/routing/session" "github.com/v2fly/v2ray-core/v5/features/stats" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) var errSniffingTimeout = newError("timeout on sniffing") type cachedReader struct { sync.Mutex reader *pipe.Reader cache buf.MultiBuffer } func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error { mb, err := r.reader.ReadMultiBufferTimeout(deadline) if err != nil { return err } r.Lock() if !mb.IsEmpty() { r.cache, _ = buf.MergeMulti(r.cache, mb) } b.Clear() rawBytes := b.Extend(b.Cap()) n := r.cache.Copy(rawBytes) b.Resize(0, int32(n)) r.Unlock() return nil } func (r *cachedReader) readInternal() buf.MultiBuffer { r.Lock() defer r.Unlock() if r.cache != nil && !r.cache.IsEmpty() { mb := r.cache r.cache = nil return mb } return nil } func (r *cachedReader) ReadMultiBuffer() (buf.MultiBuffer, error) { mb := r.readInternal() if mb != nil { return mb, nil } return r.reader.ReadMultiBuffer() } func (r *cachedReader) ReadMultiBufferTimeout(timeout time.Duration) (buf.MultiBuffer, error) { mb := r.readInternal() if mb != nil { return mb, nil } return r.reader.ReadMultiBufferTimeout(timeout) } func (r *cachedReader) Interrupt() { r.Lock() if r.cache != nil { r.cache = buf.ReleaseMulti(r.cache) } r.Unlock() r.reader.Interrupt() } // DefaultDispatcher is a default implementation of Dispatcher. type DefaultDispatcher struct { ohm outbound.Manager router routing.Router policy policy.Manager stats stats.Manager } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { d := new(DefaultDispatcher) if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error { return d.Init(config.(*Config), om, router, pm, sm) }); err != nil { return nil, err } return d, nil })) } // Init initializes DefaultDispatcher. func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error { d.ohm = om d.router = router d.policy = pm d.stats = sm return nil } // Type implements common.HasType. func (*DefaultDispatcher) Type() interface{} { return routing.DispatcherType() } // Start implements common.Runnable. func (*DefaultDispatcher) Start() error { return nil } // Close implements common.Closable. func (*DefaultDispatcher) Close() error { return nil } func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *transport.Link) { opt := pipe.OptionsFromContext(ctx) uplinkReader, uplinkWriter := pipe.New(opt...) downlinkReader, downlinkWriter := pipe.New(opt...) inboundLink := &transport.Link{ Reader: downlinkReader, Writer: uplinkWriter, } outboundLink := &transport.Link{ Reader: uplinkReader, Writer: downlinkWriter, } sessionInbound := session.InboundFromContext(ctx) var user *protocol.MemoryUser if sessionInbound != nil { user = sessionInbound.User } if user != nil && len(user.Email) > 0 { p := d.policy.ForLevel(user.Level) if p.Stats.UserUplink { name := "user>>>" + user.Email + ">>>traffic>>>uplink" if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil { inboundLink.Writer = &SizeStatWriter{ Counter: c, Writer: inboundLink.Writer, } } } if p.Stats.UserDownlink { name := "user>>>" + user.Email + ">>>traffic>>>downlink" if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil { outboundLink.Writer = &SizeStatWriter{ Counter: c, Writer: outboundLink.Writer, } } } } return inboundLink, outboundLink } func shouldOverride(result SniffResult, domainOverride []string) bool { if result.Domain() == "" { return false } protocolString := result.Protocol() if resComp, ok := result.(SnifferResultComposite); ok { protocolString = resComp.ProtocolForDomainResult() } for _, p := range domainOverride { if strings.HasPrefix(protocolString, p) || strings.HasSuffix(protocolString, p) { return true } if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok { if resultSubset.IsProtoSubsetOf(p) { return true } } } return false } // Dispatch implements routing.Dispatcher. func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (*transport.Link, error) { if !destination.IsValid() { panic("Dispatcher: Invalid destination.") } ob := &session.Outbound{ Target: destination, } ctx = session.ContextWithOutbound(ctx, ob) inbound, outbound := d.getLink(ctx) content := session.ContentFromContext(ctx) if content == nil { content = new(session.Content) ctx = session.ContextWithContent(ctx, content) } sniffingRequest := content.SniffingRequest if !sniffingRequest.Enabled { go d.routedDispatch(ctx, outbound, destination) } else { go func() { cReader := &cachedReader{ reader: outbound.Reader.(*pipe.Reader), } outbound.Reader = cReader result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network) if err == nil { content.Protocol = result.Protocol() } if err == nil && shouldOverride(result, sniffingRequest.OverrideDestinationForProtocol) { if domain, err := strmatcher.ToDomain(result.Domain()); err == nil { newError("sniffed domain: ", domain, " for ", destination).WriteToLog(session.ExportIDToError(ctx)) destination.Address = net.ParseAddress(domain) ob.Target = destination } } d.routedDispatch(ctx, outbound, destination) }() } return inbound, nil } func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) { payload := buf.NewWithSize(32767) defer payload.Release() sniffer := NewSniffer(ctx) metaresult, metadataErr := sniffer.SniffMetadata(ctx) if metadataOnly { return metaresult, metadataErr } contentResult, contentErr := func() (SniffResult, error) { cacheDeadline := 200 * time.Millisecond totalAttempt := 0 for { select { case <-ctx.Done(): return nil, ctx.Err() default: cachingStartingTimeStamp := time.Now() cacheErr := cReader.Cache(payload, cacheDeadline) cachingTimeElapsed := time.Since(cachingStartingTimeStamp) cacheDeadline -= cachingTimeElapsed if !payload.IsEmpty() { result, err := sniffer.Sniff(ctx, payload.Bytes(), network) switch err { case common.ErrNoClue: // No Clue: protocol not matches, and sniffer cannot determine whether there will be a match or not totalAttempt++ case protocol.ErrProtoNeedMoreData: // Protocol Need More Data: protocol matches, but need more data to complete sniffing if cacheErr != nil { // Cache error (e.g. timeout) counts for failed attempt totalAttempt++ } default: return result, err } } if totalAttempt >= 2 || cacheDeadline <= 0 { return nil, errSniffingTimeout } } } }() if contentErr != nil && metadataErr == nil { return metaresult, nil } if contentErr == nil && metadataErr == nil { return CompositeResult(metaresult, contentResult), nil } return contentResult, contentErr } func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) { var handler outbound.Handler if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" { ctx = session.SetForcedOutboundTagToContext(ctx, "") if h := d.ohm.GetHandler(forcedOutboundTag); h != nil { newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) handler = h } else { newError("non existing tag for platform initialized detour: ", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx)) common.Close(link.Writer) common.Interrupt(link.Reader) return } } else if d.router != nil { if route, err := d.router.PickRoute(routing_session.AsRoutingContext(ctx)); err == nil { tag := route.GetOutboundTag() if h := d.ohm.GetHandler(tag); h != nil { newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) handler = h } else { newError("non existing tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } } else { newError("default route for ", destination).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } } if handler == nil { handler = d.ohm.GetDefaultHandler() } if handler == nil { newError("default outbound handler not exist").WriteToLog(session.ExportIDToError(ctx)) common.Close(link.Writer) common.Interrupt(link.Reader) return } if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil { if tag := handler.Tag(); tag != "" { accessMessage.Detour = tag if d.policy.ForSystem().OverrideAccessLogDest { accessMessage.To = destination } } log.Record(accessMessage) } handler.Dispatch(ctx, link) } ================================================ FILE: app/dispatcher/dispatcher.go ================================================ package dispatcher //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/dispatcher/errors.generated.go ================================================ package dispatcher import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/dispatcher/fakednssniffer.go ================================================ //go:build !confonly // +build !confonly package dispatcher import ( "context" "strings" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/dns" ) // newFakeDNSSniffer Creates a Fake DNS metadata sniffer func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) { var fakeDNSEngine dns.FakeDNSEngine { fakeDNSEngineFeat := core.MustFromContext(ctx).GetFeature((*dns.FakeDNSEngine)(nil)) if fakeDNSEngineFeat != nil { fakeDNSEngine = fakeDNSEngineFeat.(dns.FakeDNSEngine) } } if fakeDNSEngine == nil { errNotInit := newError("FakeDNSEngine is not initialized, but such a sniffer is used").AtError() return protocolSnifferWithMetadata{}, errNotInit } return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) { Target := session.OutboundFromContext(ctx).Target if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP { domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address) if domainFromFakeDNS != "" { newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx)) return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil } } if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil { ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt) if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok { inPool := fkr0.IsIPInIPPool(Target.Address) ipAddressInRangeValue.addressInRange = &inPool } } return nil, common.ErrNoClue }, metadataSniffer: true}, nil } type fakeDNSSniffResult struct { domainName string } func (fakeDNSSniffResult) Protocol() string { return "fakedns" } func (f fakeDNSSniffResult) Domain() string { return f.domainName } type fakeDNSExtraOpts int const ipAddressInRange fakeDNSExtraOpts = 1 type ipAddressInRangeOpt struct { addressInRange *bool } type DNSThenOthersSniffResult struct { domainName string protocolOriginalName string } func (f DNSThenOthersSniffResult) IsProtoSubsetOf(protocolName string) bool { return strings.HasPrefix(protocolName, f.protocolOriginalName) } func (DNSThenOthersSniffResult) Protocol() string { return "fakedns+others" } func (f DNSThenOthersSniffResult) Domain() string { return f.domainName } func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWithMetadata, others []protocolSnifferWithMetadata) (protocolSnifferWithMetadata, error) { // nolint: unparam // ctx may be used in the future _ = ctx return protocolSnifferWithMetadata{ protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) { ipAddressInRangeValue := &ipAddressInRangeOpt{} ctx = context.WithValue(ctx, ipAddressInRange, ipAddressInRangeValue) result, err := fakeDNSSniffer.protocolSniffer(ctx, bytes) if err == nil { return result, nil } if ipAddressInRangeValue.addressInRange != nil { if *ipAddressInRangeValue.addressInRange { for _, v := range others { if v.metadataSniffer || bytes != nil { if result, err := v.protocolSniffer(ctx, bytes); err == nil { return DNSThenOthersSniffResult{domainName: result.Domain(), protocolOriginalName: result.Protocol()}, nil } } } return nil, common.ErrNoClue } newError("ip address not in fake dns range, return as is").AtDebug().WriteToLog() return nil, common.ErrNoClue } newError("fake dns sniffer did not set address in range option, assume false.").AtWarning().WriteToLog() return nil, common.ErrNoClue }, metadataSniffer: false, }, nil } ================================================ FILE: app/dispatcher/sniffer.go ================================================ package dispatcher import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/protocol/bittorrent" "github.com/v2fly/v2ray-core/v5/common/protocol/http" "github.com/v2fly/v2ray-core/v5/common/protocol/quic" "github.com/v2fly/v2ray-core/v5/common/protocol/tls" ) type SniffResult interface { Protocol() string Domain() string } type protocolSniffer func(context.Context, []byte) (SniffResult, error) type protocolSnifferWithMetadata struct { protocolSniffer protocolSniffer // A Metadata sniffer will be invoked on connection establishment only, with nil body, // for both TCP and UDP connections // It will not be shown as a traffic type for routing unless there is no other successful sniffing. metadataSniffer bool network net.Network } type Sniffer struct { sniffer []protocolSnifferWithMetadata } func NewSniffer(ctx context.Context) *Sniffer { ret := &Sniffer{ sniffer: []protocolSnifferWithMetadata{ {func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP}, {func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP}, {func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP}, }, } if sniffer, err := newFakeDNSSniffer(ctx); err == nil { others := ret.sniffer ret.sniffer = append(ret.sniffer, sniffer) fakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others) if err == nil { ret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...) } } return ret } var errUnknownContent = newError("unknown content") func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) { var pendingSniffer []protocolSnifferWithMetadata for _, si := range s.sniffer { sniffer := si.protocolSniffer if si.metadataSniffer { continue } if si.network != network { continue } result, err := sniffer(c, payload) if err == common.ErrNoClue { pendingSniffer = append(pendingSniffer, si) continue } else if err == protocol.ErrProtoNeedMoreData { // Sniffer protocol matched, but need more data to complete sniffing s.sniffer = []protocolSnifferWithMetadata{si} return nil, protocol.ErrProtoNeedMoreData } if err == nil && result != nil { return result, nil } } if len(pendingSniffer) > 0 { s.sniffer = pendingSniffer return nil, common.ErrNoClue } return nil, errUnknownContent } func (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) { var pendingSniffer []protocolSnifferWithMetadata for _, si := range s.sniffer { s := si.protocolSniffer if !si.metadataSniffer { pendingSniffer = append(pendingSniffer, si) continue } result, err := s(c, nil) if err == common.ErrNoClue { pendingSniffer = append(pendingSniffer, si) continue } if err == nil && result != nil { return result, nil } } if len(pendingSniffer) > 0 { s.sniffer = pendingSniffer return nil, common.ErrNoClue } return nil, errUnknownContent } func CompositeResult(domainResult SniffResult, protocolResult SniffResult) SniffResult { return &compositeResult{domainResult: domainResult, protocolResult: protocolResult} } type compositeResult struct { domainResult SniffResult protocolResult SniffResult } func (c compositeResult) Protocol() string { return c.protocolResult.Protocol() } func (c compositeResult) Domain() string { return c.domainResult.Domain() } func (c compositeResult) ProtocolForDomainResult() string { return c.domainResult.Protocol() } type SnifferResultComposite interface { ProtocolForDomainResult() string } type SnifferIsProtoSubsetOf interface { IsProtoSubsetOf(protocolName string) bool } ================================================ FILE: app/dispatcher/stats.go ================================================ package dispatcher import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/features/stats" ) type SizeStatWriter struct { Counter stats.Counter Writer buf.Writer } func (w *SizeStatWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { w.Counter.Add(int64(mb.Len())) return w.Writer.WriteMultiBuffer(mb) } func (w *SizeStatWriter) Close() error { return common.Close(w.Writer) } func (w *SizeStatWriter) Interrupt() { common.Interrupt(w.Writer) } ================================================ FILE: app/dispatcher/stats_test.go ================================================ package dispatcher_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" ) type TestCounter int64 func (c *TestCounter) Value() int64 { return int64(*c) } func (c *TestCounter) Add(v int64) int64 { x := int64(*c) + v *c = TestCounter(x) return x } func (c *TestCounter) Set(v int64) int64 { *c = TestCounter(v) return v } func TestStatsWriter(t *testing.T) { var c TestCounter writer := &SizeStatWriter{ Counter: &c, Writer: buf.Discard, } mb := buf.MergeBytes(nil, []byte("abcd")) common.Must(writer.WriteMultiBuffer(mb)) mb = buf.MergeBytes(nil, []byte("efg")) common.Must(writer.WriteMultiBuffer(mb)) if c.Value() != 7 { t.Fatal("unexpected counter value. want 7, but got ", c.Value()) } } ================================================ FILE: app/dns/config.go ================================================ //go:build !confonly // +build !confonly package dns import ( "golang.org/x/net/dns/dnsmessage" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/strmatcher" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/features/dns" ) var typeMap = map[DomainMatchingType]strmatcher.Type{ DomainMatchingType_Full: strmatcher.Full, DomainMatchingType_Subdomain: strmatcher.Domain, DomainMatchingType_Keyword: strmatcher.Substr, DomainMatchingType_Regex: strmatcher.Regex, } // References: // https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml // https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan var localTLDsAndDotlessDomains = []*NameServer_PriorityDomain{ {Type: DomainMatchingType_Regex, Domain: "^[^.]+$"}, // This will only match domains without any dot {Type: DomainMatchingType_Subdomain, Domain: "local"}, {Type: DomainMatchingType_Subdomain, Domain: "localdomain"}, {Type: DomainMatchingType_Subdomain, Domain: "localhost"}, {Type: DomainMatchingType_Subdomain, Domain: "lan"}, {Type: DomainMatchingType_Subdomain, Domain: "home.arpa"}, {Type: DomainMatchingType_Subdomain, Domain: "example"}, {Type: DomainMatchingType_Subdomain, Domain: "invalid"}, {Type: DomainMatchingType_Subdomain, Domain: "test"}, } var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{ Rule: "geosite:private", Size: uint32(len(localTLDsAndDotlessDomains)), } func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) { strMType, f := typeMap[t] if !f { return nil, newError("unknown mapping type", t).AtWarning() } matcher, err := strMType.New(domain) if err != nil { return nil, newError("failed to create str matcher").Base(err) } return matcher, nil } func toNetIP(addrs []net.Address) ([]net.IP, error) { ips := make([]net.IP, 0, len(addrs)) for _, addr := range addrs { if addr.Family().IsIP() { ips = append(ips, addr.IP()) } else { return nil, newError("Failed to convert address", addr, "to Net IP.").AtWarning() } } return ips, nil } func toIPOption(s QueryStrategy) dns.IPOption { return dns.IPOption{ IPv4Enable: s == QueryStrategy_USE_IP || s == QueryStrategy_USE_IP4, IPv6Enable: s == QueryStrategy_USE_IP || s == QueryStrategy_USE_IP6, FakeEnable: false, } } func toReqTypes(option dns.IPOption) []dnsmessage.Type { var reqTypes []dnsmessage.Type if option.IPv4Enable { reqTypes = append(reqTypes, dnsmessage.TypeA) } if option.IPv6Enable { reqTypes = append(reqTypes, dnsmessage.TypeAAAA) } return reqTypes } func generateRandomTag() string { id := uuid.New() return "v2ray.system." + id.String() } ================================================ FILE: app/dns/config.pb.go ================================================ package dns import ( fakedns "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" routercommon "github.com/v2fly/v2ray-core/v5/app/router/routercommon" net "github.com/v2fly/v2ray-core/v5/common/net" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type DomainMatchingType int32 const ( DomainMatchingType_Full DomainMatchingType = 0 DomainMatchingType_Subdomain DomainMatchingType = 1 DomainMatchingType_Keyword DomainMatchingType = 2 DomainMatchingType_Regex DomainMatchingType = 3 ) // Enum value maps for DomainMatchingType. var ( DomainMatchingType_name = map[int32]string{ 0: "Full", 1: "Subdomain", 2: "Keyword", 3: "Regex", } DomainMatchingType_value = map[string]int32{ "Full": 0, "Subdomain": 1, "Keyword": 2, "Regex": 3, } ) func (x DomainMatchingType) Enum() *DomainMatchingType { p := new(DomainMatchingType) *p = x return p } func (x DomainMatchingType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (DomainMatchingType) Descriptor() protoreflect.EnumDescriptor { return file_app_dns_config_proto_enumTypes[0].Descriptor() } func (DomainMatchingType) Type() protoreflect.EnumType { return &file_app_dns_config_proto_enumTypes[0] } func (x DomainMatchingType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use DomainMatchingType.Descriptor instead. func (DomainMatchingType) EnumDescriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{0} } type QueryStrategy int32 const ( QueryStrategy_USE_IP QueryStrategy = 0 QueryStrategy_USE_IP4 QueryStrategy = 1 QueryStrategy_USE_IP6 QueryStrategy = 2 ) // Enum value maps for QueryStrategy. var ( QueryStrategy_name = map[int32]string{ 0: "USE_IP", 1: "USE_IP4", 2: "USE_IP6", } QueryStrategy_value = map[string]int32{ "USE_IP": 0, "USE_IP4": 1, "USE_IP6": 2, } ) func (x QueryStrategy) Enum() *QueryStrategy { p := new(QueryStrategy) *p = x return p } func (x QueryStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor { return file_app_dns_config_proto_enumTypes[1].Descriptor() } func (QueryStrategy) Type() protoreflect.EnumType { return &file_app_dns_config_proto_enumTypes[1] } func (x QueryStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use QueryStrategy.Descriptor instead. func (QueryStrategy) EnumDescriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{1} } type CacheStrategy int32 const ( CacheStrategy_CacheEnabled CacheStrategy = 0 CacheStrategy_CacheDisabled CacheStrategy = 1 ) // Enum value maps for CacheStrategy. var ( CacheStrategy_name = map[int32]string{ 0: "CacheEnabled", 1: "CacheDisabled", } CacheStrategy_value = map[string]int32{ "CacheEnabled": 0, "CacheDisabled": 1, } ) func (x CacheStrategy) Enum() *CacheStrategy { p := new(CacheStrategy) *p = x return p } func (x CacheStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (CacheStrategy) Descriptor() protoreflect.EnumDescriptor { return file_app_dns_config_proto_enumTypes[2].Descriptor() } func (CacheStrategy) Type() protoreflect.EnumType { return &file_app_dns_config_proto_enumTypes[2] } func (x CacheStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use CacheStrategy.Descriptor instead. func (CacheStrategy) EnumDescriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{2} } type FallbackStrategy int32 const ( FallbackStrategy_Enabled FallbackStrategy = 0 FallbackStrategy_Disabled FallbackStrategy = 1 FallbackStrategy_DisabledIfAnyMatch FallbackStrategy = 2 ) // Enum value maps for FallbackStrategy. var ( FallbackStrategy_name = map[int32]string{ 0: "Enabled", 1: "Disabled", 2: "DisabledIfAnyMatch", } FallbackStrategy_value = map[string]int32{ "Enabled": 0, "Disabled": 1, "DisabledIfAnyMatch": 2, } ) func (x FallbackStrategy) Enum() *FallbackStrategy { p := new(FallbackStrategy) *p = x return p } func (x FallbackStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (FallbackStrategy) Descriptor() protoreflect.EnumDescriptor { return file_app_dns_config_proto_enumTypes[3].Descriptor() } func (FallbackStrategy) Type() protoreflect.EnumType { return &file_app_dns_config_proto_enumTypes[3] } func (x FallbackStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use FallbackStrategy.Descriptor instead. func (FallbackStrategy) EnumDescriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{3} } type NameServer struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` Tag string `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"` PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"` Geoip []*routercommon.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"` OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"` FakeDns *fakedns.FakeDnsPoolMulti `protobuf:"bytes,11,opt,name=fake_dns,json=fakeDns,proto3" json:"fake_dns,omitempty"` // Deprecated. Use fallback_strategy. // // Deprecated: Marked as deprecated in app/dns/config.proto. SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"` QueryStrategy *QueryStrategy `protobuf:"varint,8,opt,name=query_strategy,json=queryStrategy,proto3,enum=v2ray.core.app.dns.QueryStrategy,oneof" json:"query_strategy,omitempty"` CacheStrategy *CacheStrategy `protobuf:"varint,9,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=v2ray.core.app.dns.CacheStrategy,oneof" json:"cache_strategy,omitempty"` FallbackStrategy *FallbackStrategy `protobuf:"varint,10,opt,name=fallback_strategy,json=fallbackStrategy,proto3,enum=v2ray.core.app.dns.FallbackStrategy,oneof" json:"fallback_strategy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NameServer) Reset() { *x = NameServer{} mi := &file_app_dns_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NameServer) String() string { return protoimpl.X.MessageStringOf(x) } func (*NameServer) ProtoMessage() {} func (x *NameServer) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NameServer.ProtoReflect.Descriptor instead. func (*NameServer) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{0} } func (x *NameServer) GetAddress() *net.Endpoint { if x != nil { return x.Address } return nil } func (x *NameServer) GetClientIp() []byte { if x != nil { return x.ClientIp } return nil } func (x *NameServer) GetTag() string { if x != nil { return x.Tag } return "" } func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain { if x != nil { return x.PrioritizedDomain } return nil } func (x *NameServer) GetGeoip() []*routercommon.GeoIP { if x != nil { return x.Geoip } return nil } func (x *NameServer) GetOriginalRules() []*NameServer_OriginalRule { if x != nil { return x.OriginalRules } return nil } func (x *NameServer) GetFakeDns() *fakedns.FakeDnsPoolMulti { if x != nil { return x.FakeDns } return nil } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *NameServer) GetSkipFallback() bool { if x != nil { return x.SkipFallback } return false } func (x *NameServer) GetQueryStrategy() QueryStrategy { if x != nil && x.QueryStrategy != nil { return *x.QueryStrategy } return QueryStrategy_USE_IP } func (x *NameServer) GetCacheStrategy() CacheStrategy { if x != nil && x.CacheStrategy != nil { return *x.CacheStrategy } return CacheStrategy_CacheEnabled } func (x *NameServer) GetFallbackStrategy() FallbackStrategy { if x != nil && x.FallbackStrategy != nil { return *x.FallbackStrategy } return FallbackStrategy_Enabled } type HostMapping struct { state protoimpl.MessageState `protogen:"open.v1"` Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.dns.DomainMatchingType" json:"type,omitempty"` Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"` // ProxiedDomain indicates the mapped domain has the same IP address on this // domain. V2Ray will use this domain for IP queries. ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HostMapping) Reset() { *x = HostMapping{} mi := &file_app_dns_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HostMapping) String() string { return protoimpl.X.MessageStringOf(x) } func (*HostMapping) ProtoMessage() {} func (x *HostMapping) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HostMapping.ProtoReflect.Descriptor instead. func (*HostMapping) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{1} } func (x *HostMapping) GetType() DomainMatchingType { if x != nil { return x.Type } return DomainMatchingType_Full } func (x *HostMapping) GetDomain() string { if x != nil { return x.Domain } return "" } func (x *HostMapping) GetIp() [][]byte { if x != nil { return x.Ip } return nil } func (x *HostMapping) GetProxiedDomain() string { if x != nil { return x.ProxiedDomain } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // Nameservers used by this DNS. Only traditional UDP servers are support at // the moment. A special value 'localhost' as a domain address can be set to // use DNS on local system. // // Deprecated: Marked as deprecated in app/dns/config.proto. NameServers []*net.Endpoint `protobuf:"bytes,1,rep,name=NameServers,proto3" json:"NameServers,omitempty"` // NameServer list used by this DNS client. NameServer []*NameServer `protobuf:"bytes,5,rep,name=name_server,json=nameServer,proto3" json:"name_server,omitempty"` // Static hosts. Domain to IP. // Deprecated. Use static_hosts. // // Deprecated: Marked as deprecated in app/dns/config.proto. Hosts map[string]*net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts,proto3" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes // (IPv6). ClientIp []byte `protobuf:"bytes,3,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` // Static domain-ip mapping in DNS server. StaticHosts []*HostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"` // Global fakedns object. FakeDns *fakedns.FakeDnsPoolMulti `protobuf:"bytes,16,opt,name=fake_dns,json=fakeDns,proto3" json:"fake_dns,omitempty"` // Tag is the inbound tag of DNS client. Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"` // Domain matcher to use DomainMatcher string `protobuf:"bytes,15,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"` // DisableCache disables DNS cache // Deprecated. Use cache_strategy. // // Deprecated: Marked as deprecated in app/dns/config.proto. DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"` // Deprecated. Use fallback_strategy. // // Deprecated: Marked as deprecated in app/dns/config.proto. DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"` // Deprecated. Use fallback_strategy. // // Deprecated: Marked as deprecated in app/dns/config.proto. DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"` // Default query strategy (IPv4, IPv6, or both) for each name server. QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=v2ray.core.app.dns.QueryStrategy" json:"query_strategy,omitempty"` // Default cache strategy for each name server. CacheStrategy CacheStrategy `protobuf:"varint,12,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=v2ray.core.app.dns.CacheStrategy" json:"cache_strategy,omitempty"` // Default fallback strategy for each name server. FallbackStrategy FallbackStrategy `protobuf:"varint,13,opt,name=fallback_strategy,json=fallbackStrategy,proto3,enum=v2ray.core.app.dns.FallbackStrategy" json:"fallback_strategy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_dns_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{2} } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *Config) GetNameServers() []*net.Endpoint { if x != nil { return x.NameServers } return nil } func (x *Config) GetNameServer() []*NameServer { if x != nil { return x.NameServer } return nil } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *Config) GetHosts() map[string]*net.IPOrDomain { if x != nil { return x.Hosts } return nil } func (x *Config) GetClientIp() []byte { if x != nil { return x.ClientIp } return nil } func (x *Config) GetStaticHosts() []*HostMapping { if x != nil { return x.StaticHosts } return nil } func (x *Config) GetFakeDns() *fakedns.FakeDnsPoolMulti { if x != nil { return x.FakeDns } return nil } func (x *Config) GetTag() string { if x != nil { return x.Tag } return "" } func (x *Config) GetDomainMatcher() string { if x != nil { return x.DomainMatcher } return "" } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *Config) GetDisableCache() bool { if x != nil { return x.DisableCache } return false } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *Config) GetDisableFallback() bool { if x != nil { return x.DisableFallback } return false } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *Config) GetDisableFallbackIfMatch() bool { if x != nil { return x.DisableFallbackIfMatch } return false } func (x *Config) GetQueryStrategy() QueryStrategy { if x != nil { return x.QueryStrategy } return QueryStrategy_USE_IP } func (x *Config) GetCacheStrategy() CacheStrategy { if x != nil { return x.CacheStrategy } return CacheStrategy_CacheEnabled } func (x *Config) GetFallbackStrategy() FallbackStrategy { if x != nil { return x.FallbackStrategy } return FallbackStrategy_Enabled } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // NameServer list used by this DNS client. NameServer []*SimplifiedNameServer `protobuf:"bytes,5,rep,name=name_server,json=nameServer,proto3" json:"name_server,omitempty"` // Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes // (IPv6). ClientIp string `protobuf:"bytes,3,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` // Static domain-ip mapping in DNS server. StaticHosts []*SimplifiedHostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"` // Global fakedns object. FakeDns *fakedns.FakeDnsPoolMulti `protobuf:"bytes,16,opt,name=fake_dns,json=fakeDns,proto3" json:"fake_dns,omitempty"` // Tag is the inbound tag of DNS client. Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"` // Domain matcher to use DomainMatcher string `protobuf:"bytes,15,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"` // DisableCache disables DNS cache // Deprecated. Use cache_strategy. // // Deprecated: Marked as deprecated in app/dns/config.proto. DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"` // Deprecated. Use fallback_strategy. // // Deprecated: Marked as deprecated in app/dns/config.proto. DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"` // Deprecated. Use fallback_strategy. // // Deprecated: Marked as deprecated in app/dns/config.proto. DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"` // Default query strategy (IPv4, IPv6, or both) for each name server. QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=v2ray.core.app.dns.QueryStrategy" json:"query_strategy,omitempty"` // Default cache strategy for each name server. CacheStrategy CacheStrategy `protobuf:"varint,12,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=v2ray.core.app.dns.CacheStrategy" json:"cache_strategy,omitempty"` // Default fallback strategy for each name server. FallbackStrategy FallbackStrategy `protobuf:"varint,13,opt,name=fallback_strategy,json=fallbackStrategy,proto3,enum=v2ray.core.app.dns.FallbackStrategy" json:"fallback_strategy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_app_dns_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{3} } func (x *SimplifiedConfig) GetNameServer() []*SimplifiedNameServer { if x != nil { return x.NameServer } return nil } func (x *SimplifiedConfig) GetClientIp() string { if x != nil { return x.ClientIp } return "" } func (x *SimplifiedConfig) GetStaticHosts() []*SimplifiedHostMapping { if x != nil { return x.StaticHosts } return nil } func (x *SimplifiedConfig) GetFakeDns() *fakedns.FakeDnsPoolMulti { if x != nil { return x.FakeDns } return nil } func (x *SimplifiedConfig) GetTag() string { if x != nil { return x.Tag } return "" } func (x *SimplifiedConfig) GetDomainMatcher() string { if x != nil { return x.DomainMatcher } return "" } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *SimplifiedConfig) GetDisableCache() bool { if x != nil { return x.DisableCache } return false } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *SimplifiedConfig) GetDisableFallback() bool { if x != nil { return x.DisableFallback } return false } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *SimplifiedConfig) GetDisableFallbackIfMatch() bool { if x != nil { return x.DisableFallbackIfMatch } return false } func (x *SimplifiedConfig) GetQueryStrategy() QueryStrategy { if x != nil { return x.QueryStrategy } return QueryStrategy_USE_IP } func (x *SimplifiedConfig) GetCacheStrategy() CacheStrategy { if x != nil { return x.CacheStrategy } return CacheStrategy_CacheEnabled } func (x *SimplifiedConfig) GetFallbackStrategy() FallbackStrategy { if x != nil { return x.FallbackStrategy } return FallbackStrategy_Enabled } type SimplifiedHostMapping struct { state protoimpl.MessageState `protogen:"open.v1"` Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.dns.DomainMatchingType" json:"type,omitempty"` Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` Ip []string `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"` // ProxiedDomain indicates the mapped domain has the same IP address on this // domain. V2Ray will use this domain for IP queries. ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedHostMapping) Reset() { *x = SimplifiedHostMapping{} mi := &file_app_dns_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedHostMapping) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedHostMapping) ProtoMessage() {} func (x *SimplifiedHostMapping) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedHostMapping.ProtoReflect.Descriptor instead. func (*SimplifiedHostMapping) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{4} } func (x *SimplifiedHostMapping) GetType() DomainMatchingType { if x != nil { return x.Type } return DomainMatchingType_Full } func (x *SimplifiedHostMapping) GetDomain() string { if x != nil { return x.Domain } return "" } func (x *SimplifiedHostMapping) GetIp() []string { if x != nil { return x.Ip } return nil } func (x *SimplifiedHostMapping) GetProxiedDomain() string { if x != nil { return x.ProxiedDomain } return "" } type SimplifiedNameServer struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` ClientIp string `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"` Tag string `protobuf:"bytes,7,opt,name=tag,proto3" json:"tag,omitempty"` PrioritizedDomain []*SimplifiedNameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"` Geoip []*routercommon.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"` OriginalRules []*SimplifiedNameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"` FakeDns *fakedns.FakeDnsPoolMulti `protobuf:"bytes,11,opt,name=fake_dns,json=fakeDns,proto3" json:"fake_dns,omitempty"` // Deprecated. Use fallback_strategy. // // Deprecated: Marked as deprecated in app/dns/config.proto. SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"` QueryStrategy *QueryStrategy `protobuf:"varint,8,opt,name=query_strategy,json=queryStrategy,proto3,enum=v2ray.core.app.dns.QueryStrategy,oneof" json:"query_strategy,omitempty"` CacheStrategy *CacheStrategy `protobuf:"varint,9,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=v2ray.core.app.dns.CacheStrategy,oneof" json:"cache_strategy,omitempty"` FallbackStrategy *FallbackStrategy `protobuf:"varint,10,opt,name=fallback_strategy,json=fallbackStrategy,proto3,enum=v2ray.core.app.dns.FallbackStrategy,oneof" json:"fallback_strategy,omitempty"` GeoDomain []*routercommon.GeoSite `protobuf:"bytes,68001,rep,name=geo_domain,json=geoDomain,proto3" json:"geo_domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedNameServer) Reset() { *x = SimplifiedNameServer{} mi := &file_app_dns_config_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedNameServer) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedNameServer) ProtoMessage() {} func (x *SimplifiedNameServer) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedNameServer.ProtoReflect.Descriptor instead. func (*SimplifiedNameServer) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{5} } func (x *SimplifiedNameServer) GetAddress() *net.Endpoint { if x != nil { return x.Address } return nil } func (x *SimplifiedNameServer) GetClientIp() string { if x != nil { return x.ClientIp } return "" } func (x *SimplifiedNameServer) GetTag() string { if x != nil { return x.Tag } return "" } func (x *SimplifiedNameServer) GetPrioritizedDomain() []*SimplifiedNameServer_PriorityDomain { if x != nil { return x.PrioritizedDomain } return nil } func (x *SimplifiedNameServer) GetGeoip() []*routercommon.GeoIP { if x != nil { return x.Geoip } return nil } func (x *SimplifiedNameServer) GetOriginalRules() []*SimplifiedNameServer_OriginalRule { if x != nil { return x.OriginalRules } return nil } func (x *SimplifiedNameServer) GetFakeDns() *fakedns.FakeDnsPoolMulti { if x != nil { return x.FakeDns } return nil } // Deprecated: Marked as deprecated in app/dns/config.proto. func (x *SimplifiedNameServer) GetSkipFallback() bool { if x != nil { return x.SkipFallback } return false } func (x *SimplifiedNameServer) GetQueryStrategy() QueryStrategy { if x != nil && x.QueryStrategy != nil { return *x.QueryStrategy } return QueryStrategy_USE_IP } func (x *SimplifiedNameServer) GetCacheStrategy() CacheStrategy { if x != nil && x.CacheStrategy != nil { return *x.CacheStrategy } return CacheStrategy_CacheEnabled } func (x *SimplifiedNameServer) GetFallbackStrategy() FallbackStrategy { if x != nil && x.FallbackStrategy != nil { return *x.FallbackStrategy } return FallbackStrategy_Enabled } func (x *SimplifiedNameServer) GetGeoDomain() []*routercommon.GeoSite { if x != nil { return x.GeoDomain } return nil } type NameServer_PriorityDomain struct { state protoimpl.MessageState `protogen:"open.v1"` Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.dns.DomainMatchingType" json:"type,omitempty"` Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NameServer_PriorityDomain) Reset() { *x = NameServer_PriorityDomain{} mi := &file_app_dns_config_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NameServer_PriorityDomain) String() string { return protoimpl.X.MessageStringOf(x) } func (*NameServer_PriorityDomain) ProtoMessage() {} func (x *NameServer_PriorityDomain) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NameServer_PriorityDomain.ProtoReflect.Descriptor instead. func (*NameServer_PriorityDomain) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{0, 0} } func (x *NameServer_PriorityDomain) GetType() DomainMatchingType { if x != nil { return x.Type } return DomainMatchingType_Full } func (x *NameServer_PriorityDomain) GetDomain() string { if x != nil { return x.Domain } return "" } type NameServer_OriginalRule struct { state protoimpl.MessageState `protogen:"open.v1"` Rule string `protobuf:"bytes,1,opt,name=rule,proto3" json:"rule,omitempty"` Size uint32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NameServer_OriginalRule) Reset() { *x = NameServer_OriginalRule{} mi := &file_app_dns_config_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NameServer_OriginalRule) String() string { return protoimpl.X.MessageStringOf(x) } func (*NameServer_OriginalRule) ProtoMessage() {} func (x *NameServer_OriginalRule) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NameServer_OriginalRule.ProtoReflect.Descriptor instead. func (*NameServer_OriginalRule) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{0, 1} } func (x *NameServer_OriginalRule) GetRule() string { if x != nil { return x.Rule } return "" } func (x *NameServer_OriginalRule) GetSize() uint32 { if x != nil { return x.Size } return 0 } type SimplifiedNameServer_PriorityDomain struct { state protoimpl.MessageState `protogen:"open.v1"` Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.dns.DomainMatchingType" json:"type,omitempty"` Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedNameServer_PriorityDomain) Reset() { *x = SimplifiedNameServer_PriorityDomain{} mi := &file_app_dns_config_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedNameServer_PriorityDomain) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedNameServer_PriorityDomain) ProtoMessage() {} func (x *SimplifiedNameServer_PriorityDomain) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedNameServer_PriorityDomain.ProtoReflect.Descriptor instead. func (*SimplifiedNameServer_PriorityDomain) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{5, 0} } func (x *SimplifiedNameServer_PriorityDomain) GetType() DomainMatchingType { if x != nil { return x.Type } return DomainMatchingType_Full } func (x *SimplifiedNameServer_PriorityDomain) GetDomain() string { if x != nil { return x.Domain } return "" } type SimplifiedNameServer_OriginalRule struct { state protoimpl.MessageState `protogen:"open.v1"` Rule string `protobuf:"bytes,1,opt,name=rule,proto3" json:"rule,omitempty"` Size uint32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedNameServer_OriginalRule) Reset() { *x = SimplifiedNameServer_OriginalRule{} mi := &file_app_dns_config_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedNameServer_OriginalRule) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedNameServer_OriginalRule) ProtoMessage() {} func (x *SimplifiedNameServer_OriginalRule) ProtoReflect() protoreflect.Message { mi := &file_app_dns_config_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedNameServer_OriginalRule.ProtoReflect.Descriptor instead. func (*SimplifiedNameServer_OriginalRule) Descriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{5, 1} } func (x *SimplifiedNameServer_OriginalRule) GetRule() string { if x != nil { return x.Rule } return "" } func (x *SimplifiedNameServer_OriginalRule) GetSize() uint32 { if x != nil { return x.Size } return 0 } var File_app_dns_config_proto protoreflect.FileDescriptor const file_app_dns_config_proto_rawDesc = "" + "\n" + "\x14app/dns/config.proto\x12\x12v2ray.core.app.dns\x1a\x18common/net/address.proto\x1a\x1ccommon/net/destination.proto\x1a$app/router/routercommon/common.proto\x1a\x1dapp/dns/fakedns/fakedns.proto\x1a common/protoext/extensions.proto\"\xaa\a\n" + "\n" + "NameServer\x129\n" + "\aaddress\x18\x01 \x01(\v2\x1f.v2ray.core.common.net.EndpointR\aaddress\x12\x1b\n" + "\tclient_ip\x18\x05 \x01(\fR\bclientIp\x12\x10\n" + "\x03tag\x18\a \x01(\tR\x03tag\x12\\\n" + "\x12prioritized_domain\x18\x02 \x03(\v2-.v2ray.core.app.dns.NameServer.PriorityDomainR\x11prioritizedDomain\x12?\n" + "\x05geoip\x18\x03 \x03(\v2).v2ray.core.app.router.routercommon.GeoIPR\x05geoip\x12R\n" + "\x0eoriginal_rules\x18\x04 \x03(\v2+.v2ray.core.app.dns.NameServer.OriginalRuleR\roriginalRules\x12G\n" + "\bfake_dns\x18\v \x01(\v2,.v2ray.core.app.dns.fakedns.FakeDnsPoolMultiR\afakeDns\x12&\n" + "\fskipFallback\x18\x06 \x01(\bB\x02\x18\x01R\fskipFallback\x12M\n" + "\x0equery_strategy\x18\b \x01(\x0e2!.v2ray.core.app.dns.QueryStrategyH\x00R\rqueryStrategy\x88\x01\x01\x12M\n" + "\x0ecache_strategy\x18\t \x01(\x0e2!.v2ray.core.app.dns.CacheStrategyH\x01R\rcacheStrategy\x88\x01\x01\x12V\n" + "\x11fallback_strategy\x18\n" + " \x01(\x0e2$.v2ray.core.app.dns.FallbackStrategyH\x02R\x10fallbackStrategy\x88\x01\x01\x1ad\n" + "\x0ePriorityDomain\x12:\n" + "\x04type\x18\x01 \x01(\x0e2&.v2ray.core.app.dns.DomainMatchingTypeR\x04type\x12\x16\n" + "\x06domain\x18\x02 \x01(\tR\x06domain\x1a6\n" + "\fOriginalRule\x12\x12\n" + "\x04rule\x18\x01 \x01(\tR\x04rule\x12\x12\n" + "\x04size\x18\x02 \x01(\rR\x04sizeB\x11\n" + "\x0f_query_strategyB\x11\n" + "\x0f_cache_strategyB\x14\n" + "\x12_fallback_strategy\"\x98\x01\n" + "\vHostMapping\x12:\n" + "\x04type\x18\x01 \x01(\x0e2&.v2ray.core.app.dns.DomainMatchingTypeR\x04type\x12\x16\n" + "\x06domain\x18\x02 \x01(\tR\x06domain\x12\x0e\n" + "\x02ip\x18\x03 \x03(\fR\x02ip\x12%\n" + "\x0eproxied_domain\x18\x04 \x01(\tR\rproxiedDomain\"\x90\a\n" + "\x06Config\x12E\n" + "\vNameServers\x18\x01 \x03(\v2\x1f.v2ray.core.common.net.EndpointB\x02\x18\x01R\vNameServers\x12?\n" + "\vname_server\x18\x05 \x03(\v2\x1e.v2ray.core.app.dns.NameServerR\n" + "nameServer\x12?\n" + "\x05Hosts\x18\x02 \x03(\v2%.v2ray.core.app.dns.Config.HostsEntryB\x02\x18\x01R\x05Hosts\x12\x1b\n" + "\tclient_ip\x18\x03 \x01(\fR\bclientIp\x12B\n" + "\fstatic_hosts\x18\x04 \x03(\v2\x1f.v2ray.core.app.dns.HostMappingR\vstaticHosts\x12G\n" + "\bfake_dns\x18\x10 \x01(\v2,.v2ray.core.app.dns.fakedns.FakeDnsPoolMultiR\afakeDns\x12\x10\n" + "\x03tag\x18\x06 \x01(\tR\x03tag\x12%\n" + "\x0edomain_matcher\x18\x0f \x01(\tR\rdomainMatcher\x12&\n" + "\fdisableCache\x18\b \x01(\bB\x02\x18\x01R\fdisableCache\x12,\n" + "\x0fdisableFallback\x18\n" + " \x01(\bB\x02\x18\x01R\x0fdisableFallback\x12:\n" + "\x16disableFallbackIfMatch\x18\v \x01(\bB\x02\x18\x01R\x16disableFallbackIfMatch\x12H\n" + "\x0equery_strategy\x18\t \x01(\x0e2!.v2ray.core.app.dns.QueryStrategyR\rqueryStrategy\x12H\n" + "\x0ecache_strategy\x18\f \x01(\x0e2!.v2ray.core.app.dns.CacheStrategyR\rcacheStrategy\x12Q\n" + "\x11fallback_strategy\x18\r \x01(\x0e2$.v2ray.core.app.dns.FallbackStrategyR\x10fallbackStrategy\x1a[\n" + "\n" + "HostsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x127\n" + "\x05value\x18\x02 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\x05value:\x028\x01J\x04\b\a\x10\b\"\xe9\x05\n" + "\x10SimplifiedConfig\x12I\n" + "\vname_server\x18\x05 \x03(\v2(.v2ray.core.app.dns.SimplifiedNameServerR\n" + "nameServer\x12\x1b\n" + "\tclient_ip\x18\x03 \x01(\tR\bclientIp\x12L\n" + "\fstatic_hosts\x18\x04 \x03(\v2).v2ray.core.app.dns.SimplifiedHostMappingR\vstaticHosts\x12G\n" + "\bfake_dns\x18\x10 \x01(\v2,.v2ray.core.app.dns.fakedns.FakeDnsPoolMultiR\afakeDns\x12\x10\n" + "\x03tag\x18\x06 \x01(\tR\x03tag\x12%\n" + "\x0edomain_matcher\x18\x0f \x01(\tR\rdomainMatcher\x12&\n" + "\fdisableCache\x18\b \x01(\bB\x02\x18\x01R\fdisableCache\x12,\n" + "\x0fdisableFallback\x18\n" + " \x01(\bB\x02\x18\x01R\x0fdisableFallback\x12:\n" + "\x16disableFallbackIfMatch\x18\v \x01(\bB\x02\x18\x01R\x16disableFallbackIfMatch\x12H\n" + "\x0equery_strategy\x18\t \x01(\x0e2!.v2ray.core.app.dns.QueryStrategyR\rqueryStrategy\x12H\n" + "\x0ecache_strategy\x18\f \x01(\x0e2!.v2ray.core.app.dns.CacheStrategyR\rcacheStrategy\x12Q\n" + "\x11fallback_strategy\x18\r \x01(\x0e2$.v2ray.core.app.dns.FallbackStrategyR\x10fallbackStrategy:\x12\x82\xb5\x18\x0e\n" + "\aservice\x12\x03dnsJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03J\x04\b\a\x10\b\"\xa2\x01\n" + "\x15SimplifiedHostMapping\x12:\n" + "\x04type\x18\x01 \x01(\x0e2&.v2ray.core.app.dns.DomainMatchingTypeR\x04type\x12\x16\n" + "\x06domain\x18\x02 \x01(\tR\x06domain\x12\x0e\n" + "\x02ip\x18\x03 \x03(\tR\x02ip\x12%\n" + "\x0eproxied_domain\x18\x04 \x01(\tR\rproxiedDomain\"\x96\b\n" + "\x14SimplifiedNameServer\x129\n" + "\aaddress\x18\x01 \x01(\v2\x1f.v2ray.core.common.net.EndpointR\aaddress\x12\x1b\n" + "\tclient_ip\x18\x05 \x01(\tR\bclientIp\x12\x10\n" + "\x03tag\x18\a \x01(\tR\x03tag\x12f\n" + "\x12prioritized_domain\x18\x02 \x03(\v27.v2ray.core.app.dns.SimplifiedNameServer.PriorityDomainR\x11prioritizedDomain\x12?\n" + "\x05geoip\x18\x03 \x03(\v2).v2ray.core.app.router.routercommon.GeoIPR\x05geoip\x12\\\n" + "\x0eoriginal_rules\x18\x04 \x03(\v25.v2ray.core.app.dns.SimplifiedNameServer.OriginalRuleR\roriginalRules\x12G\n" + "\bfake_dns\x18\v \x01(\v2,.v2ray.core.app.dns.fakedns.FakeDnsPoolMultiR\afakeDns\x12&\n" + "\fskipFallback\x18\x06 \x01(\bB\x02\x18\x01R\fskipFallback\x12M\n" + "\x0equery_strategy\x18\b \x01(\x0e2!.v2ray.core.app.dns.QueryStrategyH\x00R\rqueryStrategy\x88\x01\x01\x12M\n" + "\x0ecache_strategy\x18\t \x01(\x0e2!.v2ray.core.app.dns.CacheStrategyH\x01R\rcacheStrategy\x88\x01\x01\x12V\n" + "\x11fallback_strategy\x18\n" + " \x01(\x0e2$.v2ray.core.app.dns.FallbackStrategyH\x02R\x10fallbackStrategy\x88\x01\x01\x12L\n" + "\n" + "geo_domain\x18\xa1\x93\x04 \x03(\v2+.v2ray.core.app.router.routercommon.GeoSiteR\tgeoDomain\x1ad\n" + "\x0ePriorityDomain\x12:\n" + "\x04type\x18\x01 \x01(\x0e2&.v2ray.core.app.dns.DomainMatchingTypeR\x04type\x12\x16\n" + "\x06domain\x18\x02 \x01(\tR\x06domain\x1a6\n" + "\fOriginalRule\x12\x12\n" + "\x04rule\x18\x01 \x01(\tR\x04rule\x12\x12\n" + "\x04size\x18\x02 \x01(\rR\x04sizeB\x11\n" + "\x0f_query_strategyB\x11\n" + "\x0f_cache_strategyB\x14\n" + "\x12_fallback_strategy*E\n" + "\x12DomainMatchingType\x12\b\n" + "\x04Full\x10\x00\x12\r\n" + "\tSubdomain\x10\x01\x12\v\n" + "\aKeyword\x10\x02\x12\t\n" + "\x05Regex\x10\x03*5\n" + "\rQueryStrategy\x12\n" + "\n" + "\x06USE_IP\x10\x00\x12\v\n" + "\aUSE_IP4\x10\x01\x12\v\n" + "\aUSE_IP6\x10\x02*4\n" + "\rCacheStrategy\x12\x10\n" + "\fCacheEnabled\x10\x00\x12\x11\n" + "\rCacheDisabled\x10\x01*E\n" + "\x10FallbackStrategy\x12\v\n" + "\aEnabled\x10\x00\x12\f\n" + "\bDisabled\x10\x01\x12\x16\n" + "\x12DisabledIfAnyMatch\x10\x02BW\n" + "\x16com.v2ray.core.app.dnsP\x01Z&github.com/v2fly/v2ray-core/v5/app/dns\xaa\x02\x12V2Ray.Core.App.Dnsb\x06proto3" var ( file_app_dns_config_proto_rawDescOnce sync.Once file_app_dns_config_proto_rawDescData []byte ) func file_app_dns_config_proto_rawDescGZIP() []byte { file_app_dns_config_proto_rawDescOnce.Do(func() { file_app_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dns_config_proto_rawDesc), len(file_app_dns_config_proto_rawDesc))) }) return file_app_dns_config_proto_rawDescData } var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 4) var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_app_dns_config_proto_goTypes = []any{ (DomainMatchingType)(0), // 0: v2ray.core.app.dns.DomainMatchingType (QueryStrategy)(0), // 1: v2ray.core.app.dns.QueryStrategy (CacheStrategy)(0), // 2: v2ray.core.app.dns.CacheStrategy (FallbackStrategy)(0), // 3: v2ray.core.app.dns.FallbackStrategy (*NameServer)(nil), // 4: v2ray.core.app.dns.NameServer (*HostMapping)(nil), // 5: v2ray.core.app.dns.HostMapping (*Config)(nil), // 6: v2ray.core.app.dns.Config (*SimplifiedConfig)(nil), // 7: v2ray.core.app.dns.SimplifiedConfig (*SimplifiedHostMapping)(nil), // 8: v2ray.core.app.dns.SimplifiedHostMapping (*SimplifiedNameServer)(nil), // 9: v2ray.core.app.dns.SimplifiedNameServer (*NameServer_PriorityDomain)(nil), // 10: v2ray.core.app.dns.NameServer.PriorityDomain (*NameServer_OriginalRule)(nil), // 11: v2ray.core.app.dns.NameServer.OriginalRule nil, // 12: v2ray.core.app.dns.Config.HostsEntry (*SimplifiedNameServer_PriorityDomain)(nil), // 13: v2ray.core.app.dns.SimplifiedNameServer.PriorityDomain (*SimplifiedNameServer_OriginalRule)(nil), // 14: v2ray.core.app.dns.SimplifiedNameServer.OriginalRule (*net.Endpoint)(nil), // 15: v2ray.core.common.net.Endpoint (*routercommon.GeoIP)(nil), // 16: v2ray.core.app.router.routercommon.GeoIP (*fakedns.FakeDnsPoolMulti)(nil), // 17: v2ray.core.app.dns.fakedns.FakeDnsPoolMulti (*routercommon.GeoSite)(nil), // 18: v2ray.core.app.router.routercommon.GeoSite (*net.IPOrDomain)(nil), // 19: v2ray.core.common.net.IPOrDomain } var file_app_dns_config_proto_depIdxs = []int32{ 15, // 0: v2ray.core.app.dns.NameServer.address:type_name -> v2ray.core.common.net.Endpoint 10, // 1: v2ray.core.app.dns.NameServer.prioritized_domain:type_name -> v2ray.core.app.dns.NameServer.PriorityDomain 16, // 2: v2ray.core.app.dns.NameServer.geoip:type_name -> v2ray.core.app.router.routercommon.GeoIP 11, // 3: v2ray.core.app.dns.NameServer.original_rules:type_name -> v2ray.core.app.dns.NameServer.OriginalRule 17, // 4: v2ray.core.app.dns.NameServer.fake_dns:type_name -> v2ray.core.app.dns.fakedns.FakeDnsPoolMulti 1, // 5: v2ray.core.app.dns.NameServer.query_strategy:type_name -> v2ray.core.app.dns.QueryStrategy 2, // 6: v2ray.core.app.dns.NameServer.cache_strategy:type_name -> v2ray.core.app.dns.CacheStrategy 3, // 7: v2ray.core.app.dns.NameServer.fallback_strategy:type_name -> v2ray.core.app.dns.FallbackStrategy 0, // 8: v2ray.core.app.dns.HostMapping.type:type_name -> v2ray.core.app.dns.DomainMatchingType 15, // 9: v2ray.core.app.dns.Config.NameServers:type_name -> v2ray.core.common.net.Endpoint 4, // 10: v2ray.core.app.dns.Config.name_server:type_name -> v2ray.core.app.dns.NameServer 12, // 11: v2ray.core.app.dns.Config.Hosts:type_name -> v2ray.core.app.dns.Config.HostsEntry 5, // 12: v2ray.core.app.dns.Config.static_hosts:type_name -> v2ray.core.app.dns.HostMapping 17, // 13: v2ray.core.app.dns.Config.fake_dns:type_name -> v2ray.core.app.dns.fakedns.FakeDnsPoolMulti 1, // 14: v2ray.core.app.dns.Config.query_strategy:type_name -> v2ray.core.app.dns.QueryStrategy 2, // 15: v2ray.core.app.dns.Config.cache_strategy:type_name -> v2ray.core.app.dns.CacheStrategy 3, // 16: v2ray.core.app.dns.Config.fallback_strategy:type_name -> v2ray.core.app.dns.FallbackStrategy 9, // 17: v2ray.core.app.dns.SimplifiedConfig.name_server:type_name -> v2ray.core.app.dns.SimplifiedNameServer 8, // 18: v2ray.core.app.dns.SimplifiedConfig.static_hosts:type_name -> v2ray.core.app.dns.SimplifiedHostMapping 17, // 19: v2ray.core.app.dns.SimplifiedConfig.fake_dns:type_name -> v2ray.core.app.dns.fakedns.FakeDnsPoolMulti 1, // 20: v2ray.core.app.dns.SimplifiedConfig.query_strategy:type_name -> v2ray.core.app.dns.QueryStrategy 2, // 21: v2ray.core.app.dns.SimplifiedConfig.cache_strategy:type_name -> v2ray.core.app.dns.CacheStrategy 3, // 22: v2ray.core.app.dns.SimplifiedConfig.fallback_strategy:type_name -> v2ray.core.app.dns.FallbackStrategy 0, // 23: v2ray.core.app.dns.SimplifiedHostMapping.type:type_name -> v2ray.core.app.dns.DomainMatchingType 15, // 24: v2ray.core.app.dns.SimplifiedNameServer.address:type_name -> v2ray.core.common.net.Endpoint 13, // 25: v2ray.core.app.dns.SimplifiedNameServer.prioritized_domain:type_name -> v2ray.core.app.dns.SimplifiedNameServer.PriorityDomain 16, // 26: v2ray.core.app.dns.SimplifiedNameServer.geoip:type_name -> v2ray.core.app.router.routercommon.GeoIP 14, // 27: v2ray.core.app.dns.SimplifiedNameServer.original_rules:type_name -> v2ray.core.app.dns.SimplifiedNameServer.OriginalRule 17, // 28: v2ray.core.app.dns.SimplifiedNameServer.fake_dns:type_name -> v2ray.core.app.dns.fakedns.FakeDnsPoolMulti 1, // 29: v2ray.core.app.dns.SimplifiedNameServer.query_strategy:type_name -> v2ray.core.app.dns.QueryStrategy 2, // 30: v2ray.core.app.dns.SimplifiedNameServer.cache_strategy:type_name -> v2ray.core.app.dns.CacheStrategy 3, // 31: v2ray.core.app.dns.SimplifiedNameServer.fallback_strategy:type_name -> v2ray.core.app.dns.FallbackStrategy 18, // 32: v2ray.core.app.dns.SimplifiedNameServer.geo_domain:type_name -> v2ray.core.app.router.routercommon.GeoSite 0, // 33: v2ray.core.app.dns.NameServer.PriorityDomain.type:type_name -> v2ray.core.app.dns.DomainMatchingType 19, // 34: v2ray.core.app.dns.Config.HostsEntry.value:type_name -> v2ray.core.common.net.IPOrDomain 0, // 35: v2ray.core.app.dns.SimplifiedNameServer.PriorityDomain.type:type_name -> v2ray.core.app.dns.DomainMatchingType 36, // [36:36] is the sub-list for method output_type 36, // [36:36] is the sub-list for method input_type 36, // [36:36] is the sub-list for extension type_name 36, // [36:36] is the sub-list for extension extendee 0, // [0:36] is the sub-list for field type_name } func init() { file_app_dns_config_proto_init() } func file_app_dns_config_proto_init() { if File_app_dns_config_proto != nil { return } file_app_dns_config_proto_msgTypes[0].OneofWrappers = []any{} file_app_dns_config_proto_msgTypes[5].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dns_config_proto_rawDesc), len(file_app_dns_config_proto_rawDesc)), NumEnums: 4, NumMessages: 11, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_dns_config_proto_goTypes, DependencyIndexes: file_app_dns_config_proto_depIdxs, EnumInfos: file_app_dns_config_proto_enumTypes, MessageInfos: file_app_dns_config_proto_msgTypes, }.Build() File_app_dns_config_proto = out.File file_app_dns_config_proto_goTypes = nil file_app_dns_config_proto_depIdxs = nil } ================================================ FILE: app/dns/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.dns; option csharp_namespace = "V2Ray.Core.App.Dns"; option go_package = "github.com/v2fly/v2ray-core/v5/app/dns"; option java_package = "com.v2ray.core.app.dns"; option java_multiple_files = true; import "common/net/address.proto"; import "common/net/destination.proto"; import "app/router/routercommon/common.proto"; import "app/dns/fakedns/fakedns.proto"; import "common/protoext/extensions.proto"; message NameServer { v2ray.core.common.net.Endpoint address = 1; bytes client_ip = 5; string tag = 7; message PriorityDomain { DomainMatchingType type = 1; string domain = 2; } message OriginalRule { string rule = 1; uint32 size = 2; } repeated PriorityDomain prioritized_domain = 2; repeated v2ray.core.app.router.routercommon.GeoIP geoip = 3; repeated OriginalRule original_rules = 4; v2ray.core.app.dns.fakedns.FakeDnsPoolMulti fake_dns = 11; // Deprecated. Use fallback_strategy. bool skipFallback = 6 [deprecated = true]; optional QueryStrategy query_strategy = 8; optional CacheStrategy cache_strategy = 9; optional FallbackStrategy fallback_strategy = 10; } enum DomainMatchingType { Full = 0; Subdomain = 1; Keyword = 2; Regex = 3; } enum QueryStrategy { USE_IP = 0; USE_IP4 = 1; USE_IP6 = 2; } enum CacheStrategy { CacheEnabled = 0; CacheDisabled = 1; } enum FallbackStrategy { Enabled = 0; Disabled = 1; DisabledIfAnyMatch = 2; } message HostMapping { DomainMatchingType type = 1; string domain = 2; repeated bytes ip = 3; // ProxiedDomain indicates the mapped domain has the same IP address on this // domain. V2Ray will use this domain for IP queries. string proxied_domain = 4; } message Config { // Nameservers used by this DNS. Only traditional UDP servers are support at // the moment. A special value 'localhost' as a domain address can be set to // use DNS on local system. repeated v2ray.core.common.net.Endpoint NameServers = 1 [deprecated = true]; // NameServer list used by this DNS client. repeated NameServer name_server = 5; // Static hosts. Domain to IP. // Deprecated. Use static_hosts. map Hosts = 2 [deprecated = true]; // Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes // (IPv6). bytes client_ip = 3; // Static domain-ip mapping in DNS server. repeated HostMapping static_hosts = 4; // Global fakedns object. v2ray.core.app.dns.fakedns.FakeDnsPoolMulti fake_dns = 16; // Tag is the inbound tag of DNS client. string tag = 6; reserved 7; // Domain matcher to use string domain_matcher = 15; // DisableCache disables DNS cache // Deprecated. Use cache_strategy. bool disableCache = 8 [deprecated = true]; // Deprecated. Use fallback_strategy. bool disableFallback = 10 [deprecated = true]; // Deprecated. Use fallback_strategy. bool disableFallbackIfMatch = 11 [deprecated = true]; // Default query strategy (IPv4, IPv6, or both) for each name server. QueryStrategy query_strategy = 9; // Default cache strategy for each name server. CacheStrategy cache_strategy = 12; // Default fallback strategy for each name server. FallbackStrategy fallback_strategy = 13; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "dns"; // Nameservers used by this DNS. Only traditional UDP servers are support at // the moment. A special value 'localhost' as a domain address can be set to // use DNS on local system. reserved 1; // NameServer list used by this DNS client. repeated SimplifiedNameServer name_server = 5; // Static hosts. Domain to IP. // Deprecated. Use static_hosts. reserved 2; // Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes // (IPv6). string client_ip = 3; // Static domain-ip mapping in DNS server. repeated SimplifiedHostMapping static_hosts = 4; // Global fakedns object. v2ray.core.app.dns.fakedns.FakeDnsPoolMulti fake_dns = 16; // Tag is the inbound tag of DNS client. string tag = 6; reserved 7; // Domain matcher to use string domain_matcher = 15; // DisableCache disables DNS cache // Deprecated. Use cache_strategy. bool disableCache = 8 [deprecated = true]; // Deprecated. Use fallback_strategy. bool disableFallback = 10 [deprecated = true]; // Deprecated. Use fallback_strategy. bool disableFallbackIfMatch = 11 [deprecated = true]; // Default query strategy (IPv4, IPv6, or both) for each name server. QueryStrategy query_strategy = 9; // Default cache strategy for each name server. CacheStrategy cache_strategy = 12; // Default fallback strategy for each name server. FallbackStrategy fallback_strategy = 13; } message SimplifiedHostMapping { DomainMatchingType type = 1; string domain = 2; repeated string ip = 3; // ProxiedDomain indicates the mapped domain has the same IP address on this // domain. V2Ray will use this domain for IP queries. string proxied_domain = 4; } message SimplifiedNameServer { v2ray.core.common.net.Endpoint address = 1; string client_ip = 5; string tag = 7; message PriorityDomain { DomainMatchingType type = 1; string domain = 2; } message OriginalRule { string rule = 1; uint32 size = 2; } repeated PriorityDomain prioritized_domain = 2; repeated v2ray.core.app.router.routercommon.GeoIP geoip = 3; repeated OriginalRule original_rules = 4; v2ray.core.app.dns.fakedns.FakeDnsPoolMulti fake_dns = 11; // Deprecated. Use fallback_strategy. bool skipFallback = 6 [deprecated = true]; optional QueryStrategy query_strategy = 8; optional CacheStrategy cache_strategy = 9; optional FallbackStrategy fallback_strategy = 10; repeated v2ray.core.app.router.routercommon.GeoSite geo_domain = 68001; } ================================================ FILE: app/dns/dns.go ================================================ //go:build !confonly // +build !confonly // Package dns is an implementation of core.DNS feature. package dns //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "fmt" "strings" "sync" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/strmatcher" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" ) // DNS is a DNS rely server. type DNS struct { sync.Mutex hosts *StaticHosts clients []*Client ctx context.Context clientTags map[string]bool fakeDNSEngine *FakeDNSEngine domainMatcher strmatcher.IndexMatcher matcherInfos []DomainMatcherInfo } // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher type DomainMatcherInfo struct { clientIdx uint16 domainRuleIdx uint16 } // New creates a new DNS server with given configuration. func New(ctx context.Context, config *Config) (*DNS, error) { // Create static hosts hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts) if err != nil { return nil, newError("failed to create hosts").Base(err) } // Create name servers from legacy configs clients := []*Client{} for _, endpoint := range config.NameServers { features.PrintDeprecatedFeatureWarning("simple DNS server") client, err := NewClient(ctx, &NameServer{Address: endpoint}, config) if err != nil { return nil, newError("failed to create client").Base(err) } clients = append(clients, client) } // Create name servers nsClientMap := map[int]int{} for nsIdx, ns := range config.NameServer { client, err := NewClient(ctx, ns, config) if err != nil { return nil, newError("failed to create client").Base(err) } nsClientMap[nsIdx] = len(clients) clients = append(clients, client) } // If there is no DNS client in config, add a `localhost` DNS client if len(clients) == 0 { clients = append(clients, NewLocalDNSClient()) } s := &DNS{ hosts: hosts, clients: clients, ctx: ctx, } // Establish members related to global DNS state s.clientTags = make(map[string]bool) for _, client := range clients { s.clientTags[client.tag] = true } if err := establishDomainRules(s, config, nsClientMap); err != nil { return nil, err } if err := establishExpectedIPs(s, config, nsClientMap); err != nil { return nil, err } if err := establishFakeDNS(s, config, nsClientMap); err != nil { return nil, err } return s, nil } func establishDomainRules(s *DNS, config *Config, nsClientMap map[int]int) error { domainRuleCount := 0 for _, ns := range config.NameServer { domainRuleCount += len(ns.PrioritizedDomain) } // MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1 matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1) var domainMatcher strmatcher.IndexMatcher switch config.DomainMatcher { case "mph", "hybrid": newError("using mph domain matcher").AtDebug().WriteToLog() domainMatcher = strmatcher.NewMphIndexMatcher() case "linear": fallthrough default: newError("using default domain matcher").AtDebug().WriteToLog() domainMatcher = strmatcher.NewLinearIndexMatcher() } for nsIdx, ns := range config.NameServer { clientIdx := nsClientMap[nsIdx] var rules []string ruleCurr := 0 ruleIter := 0 for _, domain := range ns.PrioritizedDomain { domainRule, err := toStrMatcher(domain.Type, domain.Domain) if err != nil { return newError("failed to create prioritized domain").Base(err).AtWarning() } originalRuleIdx := ruleCurr if ruleCurr < len(ns.OriginalRules) { rule := ns.OriginalRules[ruleCurr] if ruleCurr >= len(rules) { rules = append(rules, rule.Rule) } ruleIter++ if ruleIter >= int(rule.Size) { ruleIter = 0 ruleCurr++ } } else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests) rules = append(rules, domainRule.String()) ruleCurr++ } midx := domainMatcher.Add(domainRule) matcherInfos[midx] = DomainMatcherInfo{ clientIdx: uint16(clientIdx), domainRuleIdx: uint16(originalRuleIdx), } if err != nil { return newError("failed to create prioritized domain").Base(err).AtWarning() } } s.clients[clientIdx].domains = rules } if err := domainMatcher.Build(); err != nil { return err } s.domainMatcher = domainMatcher s.matcherInfos = matcherInfos return nil } func establishExpectedIPs(s *DNS, config *Config, nsClientMap map[int]int) error { geoipContainer := router.GeoIPMatcherContainer{} for nsIdx, ns := range config.NameServer { clientIdx := nsClientMap[nsIdx] var matchers []*router.GeoIPMatcher for _, geoip := range ns.Geoip { matcher, err := geoipContainer.Add(geoip) if err != nil { return newError("failed to create ip matcher").Base(err).AtWarning() } matchers = append(matchers, matcher) } s.clients[clientIdx].expectIPs = matchers } return nil } func establishFakeDNS(s *DNS, config *Config, nsClientMap map[int]int) error { fakeHolders := &fakedns.HolderMulti{} fakeDefault := (*fakedns.HolderMulti)(nil) if config.FakeDns != nil { defaultEngine, err := fakeHolders.AddPoolMulti(config.FakeDns) if err != nil { return newError("fail to create fake dns").Base(err).AtWarning() } fakeDefault = defaultEngine } for nsIdx, ns := range config.NameServer { clientIdx := nsClientMap[nsIdx] if ns.FakeDns == nil { continue } engine, err := fakeHolders.AddPoolMulti(ns.FakeDns) if err != nil { return newError("fail to create fake dns").Base(err).AtWarning() } s.clients[clientIdx].fakeDNS = NewFakeDNSServer(engine) s.clients[clientIdx].queryStrategy.FakeEnable = true } // Do not create FakeDNSEngine feature if no FakeDNS server is configured if fakeHolders.IsEmpty() { return nil } // Add FakeDNSEngine feature when DNS feature is added for the first time s.fakeDNSEngine = &FakeDNSEngine{dns: s, fakeHolders: fakeHolders, fakeDefault: fakeDefault} return core.RequireFeatures(s.ctx, func(client dns.Client) error { v := core.MustFromContext(s.ctx) if v.GetFeature(dns.FakeDNSEngineType()) != nil { return nil } if client, ok := client.(dns.ClientWithFakeDNS); ok { return v.AddFeature(client.AsFakeDNSEngine()) } return nil }) } // Type implements common.HasType. func (*DNS) Type() interface{} { return dns.ClientType() } // Start implements common.Runnable. func (s *DNS) Start() error { return nil } // Close implements common.Closable. func (s *DNS) Close() error { return nil } // IsOwnLink implements proxy.dns.ownLinkVerifier func (s *DNS) IsOwnLink(ctx context.Context) bool { inbound := session.InboundFromContext(ctx) return inbound != nil && s.clientTags[inbound.Tag] } // AsFakeDNSClient implements dns.ClientWithFakeDNS. func (s *DNS) AsFakeDNSClient() dns.Client { return &FakeDNSClient{DNS: s} } // AsFakeDNSEngine implements dns.ClientWithFakeDNS. func (s *DNS) AsFakeDNSEngine() dns.FakeDNSEngine { return s.fakeDNSEngine } // LookupIP implements dns.Client. func (s *DNS) LookupIP(domain string) ([]net.IP, error) { return s.lookupIPInternal(domain, dns.IPOption{IPv4Enable: true, IPv6Enable: true, FakeEnable: false}) } // LookupIPv4 implements dns.IPv4Lookup. func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) { return s.lookupIPInternal(domain, dns.IPOption{IPv4Enable: true, FakeEnable: false}) } // LookupIPv6 implements dns.IPv6Lookup. func (s *DNS) LookupIPv6(domain string) ([]net.IP, error) { return s.lookupIPInternal(domain, dns.IPOption{IPv6Enable: true, FakeEnable: false}) } func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, error) { if domain == "" { return nil, newError("empty domain name") } // Normalize the FQDN form query domain = strings.TrimSuffix(domain, ".") // Static host lookup switch addrs := s.hosts.Lookup(domain, option); { case addrs == nil: // Domain not recorded in static host break case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled) return nil, dns.ErrEmptyResponse case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement newError("domain replaced: ", domain, " -> ", addrs[0].Domain()).WriteToLog() domain = addrs[0].Domain() default: // Successfully found ip records in static host newError("returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs).WriteToLog() return toNetIP(addrs) } // Name servers lookup errs := []error{} for _, client := range s.sortClients(domain, option) { ips, err := client.QueryIP(s.ctx, domain, option) if len(ips) > 0 { return ips, nil } if err != nil { errs = append(errs, err) } if err != dns.ErrEmptyResponse { // ErrEmptyResponse is not seen as failure, so no failed log newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog() } if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch { return nil, err // Only continue lookup for certain errors } } if len(errs) == 0 { return nil, dns.ErrEmptyResponse } return nil, newError("returning nil for domain ", domain).Base(errors.Combine(errs...)) } func (s *DNS) sortClients(domain string, option dns.IPOption) []*Client { clients := make([]*Client, 0, len(s.clients)) clientUsed := make([]bool, len(s.clients)) clientIdxs := make([]int, 0, len(s.clients)) domainRules := []string{} // Priority domain matching for _, match := range s.domainMatcher.Match(domain) { info := s.matcherInfos[match] client := s.clients[info.clientIdx] domainRule := client.domains[info.domainRuleIdx] domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx)) switch { case clientUsed[info.clientIdx]: continue case !option.FakeEnable && isFakeDNS(client.server): continue } clientUsed[info.clientIdx] = true clients = append(clients, client) clientIdxs = append(clientIdxs, int(info.clientIdx)) } // Default round-robin query hasDomainMatch := len(clients) > 0 for idx, client := range s.clients { switch { case clientUsed[idx]: continue case !option.FakeEnable && isFakeDNS(client.server): continue case client.fallbackStrategy == FallbackStrategy_Disabled: continue case client.fallbackStrategy == FallbackStrategy_DisabledIfAnyMatch && hasDomainMatch: continue } clientUsed[idx] = true clients = append(clients, client) clientIdxs = append(clientIdxs, idx) } if len(domainRules) > 0 { newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog() } if len(clientIdxs) > 0 { newError("domain ", domain, " will use DNS in order: ", s.formatClientNames(clientIdxs, option), " ", toReqTypes(option)).AtDebug().WriteToLog() } return clients } func (s *DNS) formatClientNames(clientIdxs []int, option dns.IPOption) []string { clientNames := make([]string, 0, len(clientIdxs)) counter := make(map[string]uint, len(clientIdxs)) for _, clientIdx := range clientIdxs { client := s.clients[clientIdx] var name string if option.With(client.queryStrategy).FakeEnable { name = fmt.Sprintf("%s(DNS idx:%d)", client.fakeDNS.Name(), clientIdx) } else { name = client.Name() } counter[name]++ clientNames = append(clientNames, name) } for idx, clientIdx := range clientIdxs { name := clientNames[idx] if counter[name] > 1 { clientNames[idx] = fmt.Sprintf("%s(DNS idx:%d)", name, clientIdx) } } return clientNames } var matcherTypeMap = map[routercommon.Domain_Type]DomainMatchingType{ routercommon.Domain_Plain: DomainMatchingType_Keyword, routercommon.Domain_Regex: DomainMatchingType_Regex, routercommon.Domain_RootDomain: DomainMatchingType_Subdomain, routercommon.Domain_Full: DomainMatchingType_Full, } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { ctx = cfgcommon.NewConfigureLoadingContext(ctx) geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string { return "standard" }) if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil { cfgcommon.SetGeoDataLoader(ctx, loader) } else { return nil, newError("unable to create geo data loader ").Base(err) } cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx) geoLoader := cfgEnv.GetGeoLoader() simplifiedConfig := config.(*SimplifiedConfig) for _, v := range simplifiedConfig.NameServer { for _, geo := range v.Geoip { if geo.Code != "" { filepath := "geoip.dat" if geo.FilePath != "" { filepath = geo.FilePath } else { geo.CountryCode = geo.Code } var err error geo.Cidr, err = geoLoader.LoadIP(filepath, geo.Code) if err != nil { return nil, newError("unable to load geoip").Base(err) } } } for _, geo := range v.GeoDomain { if geo.Code != "" { filepath := "geosite.dat" if geo.FilePath != "" { filepath = geo.FilePath } var err error geo.Domain, err = geoLoader.LoadGeoSiteWithAttr(filepath, geo.Code) if err != nil { return nil, newError("unable to load geodomain").Base(err) } } for _, domain := range geo.Domain { v.PrioritizedDomain = append(v.PrioritizedDomain, &SimplifiedNameServer_PriorityDomain{ Type: matcherTypeMap[domain.Type], Domain: domain.Value, }) } } } var nameservers []*NameServer for _, v := range simplifiedConfig.NameServer { nameserver := &NameServer{ Address: v.Address, ClientIp: net.ParseIP(v.ClientIp), Tag: v.Tag, QueryStrategy: v.QueryStrategy, CacheStrategy: v.CacheStrategy, FallbackStrategy: v.FallbackStrategy, SkipFallback: v.SkipFallback, Geoip: v.Geoip, } for _, prioritizedDomain := range v.PrioritizedDomain { nameserver.PrioritizedDomain = append(nameserver.PrioritizedDomain, &NameServer_PriorityDomain{ Type: prioritizedDomain.Type, Domain: prioritizedDomain.Domain, }) } nameservers = append(nameservers, nameserver) } var statichosts []*HostMapping for _, v := range simplifiedConfig.StaticHosts { statichost := &HostMapping{ Type: v.Type, Domain: v.Domain, ProxiedDomain: v.ProxiedDomain, } for _, ip := range v.Ip { statichost.Ip = append(statichost.Ip, net.ParseIP(ip)) } statichosts = append(statichosts, statichost) } fullConfig := &Config{ StaticHosts: statichosts, NameServer: nameservers, ClientIp: net.ParseIP(simplifiedConfig.ClientIp), Tag: simplifiedConfig.Tag, DomainMatcher: simplifiedConfig.DomainMatcher, QueryStrategy: simplifiedConfig.QueryStrategy, CacheStrategy: simplifiedConfig.CacheStrategy, FallbackStrategy: simplifiedConfig.FallbackStrategy, // Deprecated flags DisableCache: simplifiedConfig.DisableCache, DisableFallback: simplifiedConfig.DisableFallback, DisableFallbackIfMatch: simplifiedConfig.DisableFallbackIfMatch, } return common.CreateObject(ctx, fullConfig) })) } ================================================ FILE: app/dns/dns_test.go ================================================ package dns_test import ( "testing" "time" "github.com/google/go-cmp/cmp" "github.com/miekg/dns" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" . "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/app/policy" "github.com/v2fly/v2ray-core/v5/app/proxyman" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/strmatcher" feature_dns "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" ) type staticHandler struct{} func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { ans := new(dns.Msg) ans.Id = r.Id var clientIP net.IP opt := r.IsEdns0() if opt != nil { for _, o := range opt.Option { if o.Option() == dns.EDNS0SUBNET { subnet := o.(*dns.EDNS0_SUBNET) clientIP = subnet.Address } } } for _, q := range r.Question { switch { case q.Name == "google.com." && q.Qtype == dns.TypeA: if clientIP == nil { rr, _ := dns.NewRR("google.com. IN A 8.8.8.8") ans.Answer = append(ans.Answer, rr) } else { rr, _ := dns.NewRR("google.com. IN A 8.8.4.4") ans.Answer = append(ans.Answer, rr) } case q.Name == "api.google.com." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("api.google.com. IN A 8.8.7.7") ans.Answer = append(ans.Answer, rr) case q.Name == "v2.api.google.com." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("v2.api.google.com. IN A 8.8.7.8") ans.Answer = append(ans.Answer, rr) case q.Name == "facebook.com." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("facebook.com. IN A 9.9.9.9") ans.Answer = append(ans.Answer, rr) case q.Name == "ipv6.google.com." && q.Qtype == dns.TypeA: rr, err := dns.NewRR("ipv6.google.com. IN A 8.8.8.7") common.Must(err) ans.Answer = append(ans.Answer, rr) case q.Name == "ipv6.google.com." && q.Qtype == dns.TypeAAAA: rr, err := dns.NewRR("ipv6.google.com. IN AAAA 2001:4860:4860::8888") common.Must(err) ans.Answer = append(ans.Answer, rr) case q.Name == "notexist.google.com." && q.Qtype == dns.TypeAAAA: ans.MsgHdr.Rcode = dns.RcodeNameError case q.Name == "hostname." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("hostname. IN A 127.0.0.1") ans.Answer = append(ans.Answer, rr) case q.Name == "hostname.local." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("hostname.local. IN A 127.0.0.1") ans.Answer = append(ans.Answer, rr) case q.Name == "hostname.localdomain." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("hostname.localdomain. IN A 127.0.0.1") ans.Answer = append(ans.Answer, rr) case q.Name == "localhost." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("localhost. IN A 127.0.0.2") ans.Answer = append(ans.Answer, rr) case q.Name == "localhost-a." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("localhost-a. IN A 127.0.0.3") ans.Answer = append(ans.Answer, rr) case q.Name == "localhost-b." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("localhost-b. IN A 127.0.0.4") ans.Answer = append(ans.Answer, rr) case q.Name == "Mijia\\ Cloud." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("Mijia\\ Cloud. IN A 127.0.0.1") ans.Answer = append(ans.Answer, rr) case q.Name == "xn--vi8h.ws." /* 🍕.ws */ && q.Qtype == dns.TypeA: rr, err := dns.NewRR("xn--vi8h.ws. IN A 208.100.42.200") common.Must(err) ans.Answer = append(ans.Answer, rr) case q.Name == "xn--l8jaaa.com." /* ああああ.com */ && q.Qtype == dns.TypeA: rr, err := dns.NewRR("xn--l8jaaa.com. IN AAAA a:a:a:a::aaaa") common.Must(err) ans.Answer = append(ans.Answer, rr) } } w.WriteMsg(ans) } func TestUDPServerSubnet(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServers: []*net.Endpoint{ { Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, }, ClientIp: []byte{7, 8, 9, 10}, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 4, 4}}); r != "" { t.Fatal(r) } } func TestUDPServer(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServers: []*net.Endpoint{ { Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 8, 8}}); r != "" { t.Fatal(r) } } { ips, err := client.LookupIP("facebook.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{9, 9, 9, 9}}); r != "" { t.Fatal(r) } } { _, err := client.LookupIP("notexist.google.com") if err == nil { t.Fatal("nil error") } if r := feature_dns.RCodeFromError(err); r != uint16(dns.RcodeNameError) { t.Fatal("expected NameError, but got ", r) } } { clientv6 := client.(feature_dns.IPv6Lookup) ips, err := clientv6.LookupIPv6("ipv4only.google.com") if err != feature_dns.ErrEmptyResponse { t.Fatal("error: ", err) } if len(ips) != 0 { t.Fatal("ips: ", ips) } } { ips, err := client.LookupIP(common.Must2(strmatcher.ToDomain("🍕.ws")).(string)) if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{208, 100, 42, 200}}); r != "" { t.Fatal(r) } } { ips, err := client.LookupIP(common.Must2(strmatcher.ToDomain("ああああ.com")).(string)) if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{0, 0xa, 0, 0xa, 0, 0xa, 0, 0xa, 0, 0, 0, 0, 0, 0, 0xaa, 0xaa}}); r != "" { t.Fatal(r) } } dnsServer.Shutdown() { ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 8, 8}}); r != "" { t.Fatal(r) } } } func TestPrioritizedDomain(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServers: []*net.Endpoint{ { Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: 9999, /* unreachable */ }, }, NameServer: []*NameServer{ { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, PrioritizedDomain: []*NameServer_PriorityDomain{ { Type: DomainMatchingType_Full, Domain: "google.com", }, }, }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) startTime := time.Now() { ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 8, 8}}); r != "" { t.Fatal(r) } } endTime := time.Now() if startTime.After(endTime.Add(time.Second * 2)) { t.Error("DNS query doesn't finish in 2 seconds.") } } func TestUDPServerIPv6(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServers: []*net.Endpoint{ { Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client6 := client.(feature_dns.IPv6Lookup) { ips, err := client6.LookupIPv6("ipv6.google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{32, 1, 72, 96, 72, 96, 0, 0, 0, 0, 0, 0, 0, 0, 136, 136}}); r != "" { t.Fatal(r) } } } func TestStaticHostDomain(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServers: []*net.Endpoint{ { Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, }, StaticHosts: []*HostMapping{ { Type: DomainMatchingType_Full, Domain: "example.com", ProxiedDomain: "google.com", }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { ips, err := client.LookupIP("example.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 8, 8}}); r != "" { t.Fatal(r) } } dnsServer.Shutdown() } func TestIPMatch(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServer: []*NameServer{ // private dns, not match { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, Geoip: []*routercommon.GeoIP{ { CountryCode: "local", Cidr: []*routercommon.CIDR{ { // inner ip, will not match Ip: []byte{192, 168, 11, 1}, Prefix: 32, }, }, }, }, }, // second dns, match ip { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, Geoip: []*routercommon.GeoIP{ { CountryCode: "test", Cidr: []*routercommon.CIDR{ { Ip: []byte{8, 8, 8, 8}, Prefix: 32, }, }, }, { CountryCode: "test", Cidr: []*routercommon.CIDR{ { Ip: []byte{8, 8, 8, 4}, Prefix: 32, }, }, }, }, }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) startTime := time.Now() { ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 8, 8}}); r != "" { t.Fatal(r) } } endTime := time.Now() if startTime.After(endTime.Add(time.Second * 2)) { t.Error("DNS query doesn't finish in 2 seconds.") } } func TestLocalDomain(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServers: []*net.Endpoint{ { Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: 9999, /* unreachable */ }, }, NameServer: []*NameServer{ { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, PrioritizedDomain: []*NameServer_PriorityDomain{ // Equivalent of dotless:localhost {Type: DomainMatchingType_Regex, Domain: "^[^.]*localhost[^.]*$"}, }, Geoip: []*routercommon.GeoIP{ { // Will match localhost, localhost-a and localhost-b, CountryCode: "local", Cidr: []*routercommon.CIDR{ {Ip: []byte{127, 0, 0, 2}, Prefix: 32}, {Ip: []byte{127, 0, 0, 3}, Prefix: 32}, {Ip: []byte{127, 0, 0, 4}, Prefix: 32}, }, }, }, }, { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, PrioritizedDomain: []*NameServer_PriorityDomain{ // Equivalent of dotless: and domain:local {Type: DomainMatchingType_Regex, Domain: "^[^.]*$"}, {Type: DomainMatchingType_Subdomain, Domain: "local"}, {Type: DomainMatchingType_Subdomain, Domain: "localdomain"}, }, }, }, StaticHosts: []*HostMapping{ { Type: DomainMatchingType_Full, Domain: "hostnamestatic", Ip: [][]byte{{127, 0, 0, 53}}, }, { Type: DomainMatchingType_Full, Domain: "hostnamealias", ProxiedDomain: "hostname.localdomain", }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) startTime := time.Now() { // Will match dotless: ips, err := client.LookupIP("hostname") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{127, 0, 0, 1}}); r != "" { t.Fatal(r) } } { // Will match domain:local ips, err := client.LookupIP("hostname.local") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{127, 0, 0, 1}}); r != "" { t.Fatal(r) } } { // Will match static ip ips, err := client.LookupIP("hostnamestatic") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{127, 0, 0, 53}}); r != "" { t.Fatal(r) } } { // Will match domain replacing ips, err := client.LookupIP("hostnamealias") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{127, 0, 0, 1}}); r != "" { t.Fatal(r) } } { // Will match dotless:localhost, but not expectIPs: 127.0.0.2, 127.0.0.3, then matches at dotless: ips, err := client.LookupIP("localhost") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{127, 0, 0, 2}}); r != "" { t.Fatal(r) } } { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 ips, err := client.LookupIP("localhost-a") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{127, 0, 0, 3}}); r != "" { t.Fatal(r) } } { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 ips, err := client.LookupIP("localhost-b") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{127, 0, 0, 4}}); r != "" { t.Fatal(r) } } { // Will match dotless: ips, err := client.LookupIP("Mijia Cloud") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{127, 0, 0, 1}}); r != "" { t.Fatal(r) } } endTime := time.Now() if startTime.After(endTime.Add(time.Second * 2)) { t.Error("DNS query doesn't finish in 2 seconds.") } } func TestMultiMatchPrioritizedDomain(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServers: []*net.Endpoint{ { Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: 9999, /* unreachable */ }, }, NameServer: []*NameServer{ { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, PrioritizedDomain: []*NameServer_PriorityDomain{ { Type: DomainMatchingType_Subdomain, Domain: "google.com", }, }, Geoip: []*routercommon.GeoIP{ { // Will only match 8.8.8.8 and 8.8.4.4 Cidr: []*routercommon.CIDR{ {Ip: []byte{8, 8, 8, 8}, Prefix: 32}, {Ip: []byte{8, 8, 4, 4}, Prefix: 32}, }, }, }, }, { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, PrioritizedDomain: []*NameServer_PriorityDomain{ { Type: DomainMatchingType_Subdomain, Domain: "google.com", }, }, Geoip: []*routercommon.GeoIP{ { // Will match 8.8.8.8 and 8.8.8.7, etc Cidr: []*routercommon.CIDR{ {Ip: []byte{8, 8, 8, 7}, Prefix: 24}, }, }, }, }, { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, PrioritizedDomain: []*NameServer_PriorityDomain{ { Type: DomainMatchingType_Subdomain, Domain: "api.google.com", }, }, Geoip: []*routercommon.GeoIP{ { // Will only match 8.8.7.7 (api.google.com) Cidr: []*routercommon.CIDR{ {Ip: []byte{8, 8, 7, 7}, Prefix: 32}, }, }, }, }, { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, PrioritizedDomain: []*NameServer_PriorityDomain{ { Type: DomainMatchingType_Full, Domain: "v2.api.google.com", }, }, Geoip: []*routercommon.GeoIP{ { // Will only match 8.8.7.8 (v2.api.google.com) Cidr: []*routercommon.CIDR{ {Ip: []byte{8, 8, 7, 8}, Prefix: 32}, }, }, }, }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) startTime := time.Now() { // Will match server 1,2 and server 1 returns expected ip ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 8, 8}}); r != "" { t.Fatal(r) } } { // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one clientv4 := client.(feature_dns.IPv4Lookup) ips, err := clientv4.LookupIPv4("ipv6.google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 8, 7}}); r != "" { t.Fatal(r) } } { // Will match server 3,1,2 and server 3 returns expected one ips, err := client.LookupIP("api.google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 7, 7}}); r != "" { t.Fatal(r) } } { // Will match server 4,3,1,2 and server 4 returns expected one ips, err := client.LookupIP("v2.api.google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 7, 8}}); r != "" { t.Fatal(r) } } endTime := time.Now() if startTime.After(endTime.Add(time.Second * 2)) { t.Error("DNS query doesn't finish in 2 seconds.") } } ================================================ FILE: app/dns/dnscommon.go ================================================ package dns import ( "encoding/binary" "strings" "time" "golang.org/x/net/dns/dnsmessage" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" ) // Fqdn normalizes domain make sure it ends with '.' func Fqdn(domain string) string { if len(domain) > 0 && strings.HasSuffix(domain, ".") { return domain } return domain + "." } type record struct { A *IPRecord AAAA *IPRecord } // IPRecord is a cacheable item for a resolved domain type IPRecord struct { ReqID uint16 IP []net.Address Expire time.Time RCode dnsmessage.RCode } func (r *IPRecord) getIPs() ([]net.Address, error) { if r == nil || r.Expire.Before(time.Now()) { return nil, errRecordNotFound } if r.RCode != dnsmessage.RCodeSuccess { return nil, dns_feature.RCodeError(r.RCode) } return r.IP, nil } func isNewer(baseRec *IPRecord, newRec *IPRecord) bool { if newRec == nil { return false } if baseRec == nil { return true } return baseRec.Expire.Before(newRec.Expire) } var errRecordNotFound = errors.New("record not found") type dnsRequest struct { reqType dnsmessage.Type domain string start time.Time expire time.Time msg *dnsmessage.Message } func genEDNS0Options(clientIP net.IP) *dnsmessage.Resource { if len(clientIP) == 0 { return nil } var netmask int var family uint16 if len(clientIP) == 4 { family = 1 netmask = 24 // 24 for IPV4, 96 for IPv6 } else { family = 2 netmask = 96 } b := make([]byte, 4) binary.BigEndian.PutUint16(b[0:], family) b[2] = byte(netmask) b[3] = 0 switch family { case 1: ip := clientIP.To4().Mask(net.CIDRMask(netmask, net.IPv4len*8)) needLength := (netmask + 8 - 1) / 8 // division rounding up b = append(b, ip[:needLength]...) case 2: ip := clientIP.Mask(net.CIDRMask(netmask, net.IPv6len*8)) needLength := (netmask + 8 - 1) / 8 // division rounding up b = append(b, ip[:needLength]...) } const EDNS0SUBNET = 0x08 opt := new(dnsmessage.Resource) common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true)) opt.Body = &dnsmessage.OPTResource{ Options: []dnsmessage.Option{ { Code: EDNS0SUBNET, Data: b, }, }, } return opt } func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest { qA := dnsmessage.Question{ Name: dnsmessage.MustNewName(domain), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, } qAAAA := dnsmessage.Question{ Name: dnsmessage.MustNewName(domain), Type: dnsmessage.TypeAAAA, Class: dnsmessage.ClassINET, } var reqs []*dnsRequest now := time.Now() if option.IPv4Enable { msg := new(dnsmessage.Message) msg.Header.ID = reqIDGen() msg.Header.RecursionDesired = true msg.Questions = []dnsmessage.Question{qA} if reqOpts != nil { msg.Additionals = append(msg.Additionals, *reqOpts) } reqs = append(reqs, &dnsRequest{ reqType: dnsmessage.TypeA, domain: domain, start: now, msg: msg, }) } if option.IPv6Enable { msg := new(dnsmessage.Message) msg.Header.ID = reqIDGen() msg.Header.RecursionDesired = true msg.Questions = []dnsmessage.Question{qAAAA} if reqOpts != nil { msg.Additionals = append(msg.Additionals, *reqOpts) } reqs = append(reqs, &dnsRequest{ reqType: dnsmessage.TypeAAAA, domain: domain, start: now, msg: msg, }) } return reqs } // parseResponse parses DNS answers from the returned payload func parseResponse(payload []byte) (*IPRecord, error) { var parser dnsmessage.Parser h, err := parser.Start(payload) if err != nil { return nil, newError("failed to parse DNS response").Base(err).AtWarning() } if err := parser.SkipAllQuestions(); err != nil { return nil, newError("failed to skip questions in DNS response").Base(err).AtWarning() } now := time.Now() ipRecord := &IPRecord{ ReqID: h.ID, RCode: h.RCode, Expire: now.Add(time.Second * 600), } L: for { ah, err := parser.AnswerHeader() if err != nil { if err != dnsmessage.ErrSectionDone { newError("failed to parse answer section for domain: ", ah.Name.String()).Base(err).WriteToLog() } break } ttl := ah.TTL if ttl == 0 { ttl = 600 } expire := now.Add(time.Duration(ttl) * time.Second) if ipRecord.Expire.After(expire) { ipRecord.Expire = expire } switch ah.Type { case dnsmessage.TypeA: ans, err := parser.AResource() if err != nil { newError("failed to parse A record for domain: ", ah.Name).Base(err).WriteToLog() break L } ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.A[:])) case dnsmessage.TypeAAAA: ans, err := parser.AAAAResource() if err != nil { newError("failed to parse AAAA record for domain: ", ah.Name).Base(err).WriteToLog() break L } ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.AAAA[:])) default: if err := parser.SkipAnswer(); err != nil { newError("failed to skip answer").Base(err).WriteToLog() break L } continue } } return ipRecord, nil } func filterIP(ips []net.Address, option dns_feature.IPOption) []net.Address { filtered := make([]net.Address, 0, len(ips)) for _, ip := range ips { if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) { filtered = append(filtered, ip) } } return filtered } ================================================ FILE: app/dns/dnscommon_test.go ================================================ package dns import ( "math/rand" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/miekg/dns" "golang.org/x/net/dns/dnsmessage" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" ) func Test_parseResponse(t *testing.T) { var p [][]byte ans := new(dns.Msg) ans.Id = 0 p = append(p, common.Must2(ans.Pack()).([]byte)) p = append(p, []byte{}) ans = new(dns.Msg) ans.Id = 1 ans.Answer = append(ans.Answer, common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")).(dns.RR), common.Must2(dns.NewRR("google.com. IN A 8.8.4.4")).(dns.RR), ) p = append(p, common.Must2(ans.Pack()).([]byte)) ans = new(dns.Msg) ans.Id = 2 ans.Answer = append(ans.Answer, common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME test.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8888")).(dns.RR), common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8844")).(dns.RR), ) p = append(p, common.Must2(ans.Pack()).([]byte)) tests := []struct { name string want *IPRecord wantErr bool }{ { "empty", &IPRecord{0, []net.Address(nil), time.Time{}, dnsmessage.RCodeSuccess}, false, }, { "error", nil, true, }, { "a record", &IPRecord{ 1, []net.Address{net.ParseAddress("8.8.8.8"), net.ParseAddress("8.8.4.4")}, time.Time{}, dnsmessage.RCodeSuccess, }, false, }, { "aaaa record", &IPRecord{2, []net.Address{net.ParseAddress("2001::123:8888"), net.ParseAddress("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess}, false, }, } for i, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := parseResponse(p[i]) if (err != nil) != tt.wantErr { t.Errorf("handleResponse() error = %v, wantErr %v", err, tt.wantErr) return } if got != nil { // reset the time got.Expire = time.Time{} } if cmp.Diff(got, tt.want) != "" { t.Errorf("%v", cmp.Diff(got, tt.want)) // t.Errorf("handleResponse() = %#v, want %#v", got, tt.want) } }) } } func Test_buildReqMsgs(t *testing.T) { stubID := func() uint16 { return uint16(rand.Uint32()) } type args struct { domain string option dns_feature.IPOption reqOpts *dnsmessage.Resource } tests := []struct { name string args args want int }{ {"dual stack", args{"test.com", dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, }, nil}, 2}, {"ipv4 only", args{"test.com", dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: false, FakeEnable: false, }, nil}, 1}, {"ipv6 only", args{"test.com", dns_feature.IPOption{ IPv4Enable: false, IPv6Enable: true, FakeEnable: false, }, nil}, 1}, {"none/error", args{"test.com", dns_feature.IPOption{ IPv4Enable: false, IPv6Enable: false, FakeEnable: false, }, nil}, 0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) { t.Errorf("buildReqMsgs() = %v, want %v", got, tt.want) } }) } } func Test_genEDNS0Options(t *testing.T) { type args struct { clientIP net.IP } tests := []struct { name string args args want *dnsmessage.Resource }{ // TODO: Add test cases. {"ipv4", args{net.ParseIP("4.3.2.1")}, nil}, {"ipv6", args{net.ParseIP("2001::4321")}, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := genEDNS0Options(tt.args.clientIP); got == nil { t.Errorf("genEDNS0Options() = %v, want %v", got, tt.want) } }) } } func TestFqdn(t *testing.T) { type args struct { domain string } tests := []struct { name string args args want string }{ {"with fqdn", args{"www.v2fly.org."}, "www.v2fly.org."}, {"without fqdn", args{"www.v2fly.org"}, "www.v2fly.org."}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Fqdn(tt.args.domain); got != tt.want { t.Errorf("Fqdn() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: app/dns/errors.generated.go ================================================ package dns import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/dns/fakedns/errors.generated.go ================================================ package fakedns import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/dns/fakedns/fake.go ================================================ //go:build !confonly // +build !confonly package fakedns import ( "context" "math" "math/big" gonet "net" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/cache" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/dns" ) type Holder struct { domainToIP cache.Lru nextIP *big.Int mu *sync.Mutex ipRange *gonet.IPNet config *FakeDnsPool } func (fkdns *Holder) IsIPInIPPool(ip net.Address) bool { if ip.Family().IsDomain() { return false } return fkdns.ipRange.Contains(ip.IP()) } func (fkdns *Holder) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address { isIPv6 := fkdns.ipRange.IP.To4() == nil if (isIPv6 && ipv6) || (!isIPv6 && ipv4) { return fkdns.GetFakeIPForDomain(domain) } return []net.Address{} } func (*Holder) Type() interface{} { return dns.FakeDNSEngineType() } func (fkdns *Holder) Start() error { if fkdns.config != nil && fkdns.config.IpPool != "" && fkdns.config.LruSize != 0 { return fkdns.initializeFromConfig() } return newError("invalid fakeDNS setting") } func (fkdns *Holder) Close() error { fkdns.domainToIP = nil fkdns.nextIP = nil fkdns.ipRange = nil fkdns.mu = nil return nil } func NewFakeDNSHolder() (*Holder, error) { var fkdns *Holder var err error if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil { return nil, newError("Unable to create Fake Dns Engine").Base(err).AtError() } err = fkdns.initialize("198.18.0.0/15", 65535) if err != nil { return nil, err } return fkdns, nil } func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) { return &Holder{nil, nil, nil, nil, conf}, nil } func (fkdns *Holder) initializeFromConfig() error { return fkdns.initialize(fkdns.config.IpPool, int(fkdns.config.LruSize)) } func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error { var ipRange *gonet.IPNet var ipaddr gonet.IP var currentIP *big.Int var err error if ipaddr, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil { return newError("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError() } currentIP = big.NewInt(0).SetBytes(ipaddr) if ipaddr.To4() != nil { currentIP = big.NewInt(0).SetBytes(ipaddr.To4()) } ones, bits := ipRange.Mask.Size() rooms := bits - ones if math.Log2(float64(lruSize)) >= float64(rooms) { return newError("LRU size is bigger than subnet size").AtError() } fkdns.domainToIP = cache.NewLru(lruSize) fkdns.ipRange = ipRange fkdns.nextIP = currentIP fkdns.mu = new(sync.Mutex) return nil } // GetFakeIPForDomain checks and generate a fake IP for a domain name func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address { fkdns.mu.Lock() defer fkdns.mu.Unlock() if v, ok := fkdns.domainToIP.Get(domain); ok { return []net.Address{v.(net.Address)} } var ip net.Address for { ip = net.IPAddress(fkdns.nextIP.Bytes()) fkdns.nextIP = fkdns.nextIP.Add(fkdns.nextIP, big.NewInt(1)) if !fkdns.ipRange.Contains(fkdns.nextIP.Bytes()) { fkdns.nextIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP) } // if we run for a long time, we may go back to beginning and start seeing the IP in use if _, ok := fkdns.domainToIP.GetKeyFromValue(ip); !ok { break } } fkdns.domainToIP.Put(domain, ip) return []net.Address{ip} } // GetDomainFromFakeDNS checks if an IP is a fake IP and have corresponding domain name func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string { if !ip.Family().IsIP() || !fkdns.ipRange.Contains(ip.IP()) { return "" } if k, ok := fkdns.domainToIP.GetKeyFromValue(ip); ok { return k.(string) } return "" } type HolderMulti struct { holders []*Holder } func (h *HolderMulti) IsIPInIPPool(ip net.Address) bool { if ip.Family().IsDomain() { return false } for _, v := range h.holders { if v.IsIPInIPPool(ip) { return true } } return false } func (h *HolderMulti) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address { var ret []net.Address for _, v := range h.holders { ret = append(ret, v.GetFakeIPForDomain3(domain, ipv4, ipv6)...) } return ret } func (h *HolderMulti) GetFakeIPForDomain(domain string) []net.Address { var ret []net.Address for _, v := range h.holders { ret = append(ret, v.GetFakeIPForDomain(domain)...) } return ret } func (h *HolderMulti) GetDomainFromFakeDNS(ip net.Address) string { for _, v := range h.holders { if domain := v.GetDomainFromFakeDNS(ip); domain != "" { return domain } } return "" } func (h *HolderMulti) IsEmpty() bool { return len(h.holders) == 0 } func (h *HolderMulti) AddPool(poolConfig *FakeDnsPool) (*Holder, error) { _, newIPRange, err := gonet.ParseCIDR(poolConfig.IpPool) if err != nil { return nil, err } running := false for _, v := range h.holders { var ipRange *gonet.IPNet if v.ipRange != nil { ipRange = v.ipRange running = true } else { _, ipRange, err = gonet.ParseCIDR(v.config.IpPool) if err != nil { return nil, err } } if ipRange.String() == newIPRange.String() { return v, nil } if ipRange.Contains(newIPRange.IP) || newIPRange.Contains(ipRange.IP) { return nil, newError("Trying to add ip pool ", newIPRange, " that overlaps with existing ip pool ", ipRange) } } holder, err := NewFakeDNSHolderConfigOnly(poolConfig) if err != nil { return nil, err } if running { if err := holder.Start(); err != nil { return nil, err } } h.holders = append(h.holders, holder) return holder, nil } func (h *HolderMulti) AddPoolMulti(poolMultiConfig *FakeDnsPoolMulti) (*HolderMulti, error) { holderMulti := &HolderMulti{} for _, poolConfig := range poolMultiConfig.Pools { pool, err := h.AddPool(poolConfig) if err != nil { return nil, err } holderMulti.holders = append(holderMulti.holders, pool) } return holderMulti, nil // Returned holderMulti holds references to pools managed by `h` } func (h *HolderMulti) Type() interface{} { return dns.FakeDNSEngineType() } func (h *HolderMulti) Start() error { for _, v := range h.holders { if err := v.Start(); err != nil { return newError("Cannot start all fake dns pools").Base(err) } } return nil } func (h *HolderMulti) Close() error { for _, v := range h.holders { if err := v.Close(); err != nil { return newError("Cannot close all fake dns pools").Base(err) } } return nil } func (h *HolderMulti) createHolderGroups(conf *FakeDnsPoolMulti) error { for _, pool := range conf.Pools { _, err := h.AddPool(pool) if err != nil { return err } } return nil } func NewFakeDNSHolderMulti(conf *FakeDnsPoolMulti) (*HolderMulti, error) { holderMulti := &HolderMulti{} if err := holderMulti.createHolderGroups(conf); err != nil { return nil, err } return holderMulti, nil } func init() { common.Must(common.RegisterConfig((*FakeDnsPool)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { var f *Holder var err error if f, err = NewFakeDNSHolderConfigOnly(config.(*FakeDnsPool)); err != nil { return nil, err } return f, nil })) common.Must(common.RegisterConfig((*FakeDnsPoolMulti)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { var f *HolderMulti var err error if f, err = NewFakeDNSHolderMulti(config.(*FakeDnsPoolMulti)); err != nil { return nil, err } return f, nil })) } ================================================ FILE: app/dns/fakedns/fakedns.go ================================================ //go:build !confonly // +build !confonly package fakedns //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/dns/fakedns/fakedns.pb.go ================================================ package fakedns import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type FakeDnsPool struct { state protoimpl.MessageState `protogen:"open.v1"` IpPool string `protobuf:"bytes,1,opt,name=ip_pool,json=ipPool,proto3" json:"ip_pool,omitempty"` //CIDR of IP pool used as fake DNS IP LruSize int64 `protobuf:"varint,2,opt,name=lruSize,proto3" json:"lruSize,omitempty"` //Size of Pool for remembering relationship between domain name and IP address unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FakeDnsPool) Reset() { *x = FakeDnsPool{} mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FakeDnsPool) String() string { return protoimpl.X.MessageStringOf(x) } func (*FakeDnsPool) ProtoMessage() {} func (x *FakeDnsPool) ProtoReflect() protoreflect.Message { mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FakeDnsPool.ProtoReflect.Descriptor instead. func (*FakeDnsPool) Descriptor() ([]byte, []int) { return file_app_dns_fakedns_fakedns_proto_rawDescGZIP(), []int{0} } func (x *FakeDnsPool) GetIpPool() string { if x != nil { return x.IpPool } return "" } func (x *FakeDnsPool) GetLruSize() int64 { if x != nil { return x.LruSize } return 0 } type FakeDnsPoolMulti struct { state protoimpl.MessageState `protogen:"open.v1"` Pools []*FakeDnsPool `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FakeDnsPoolMulti) Reset() { *x = FakeDnsPoolMulti{} mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FakeDnsPoolMulti) String() string { return protoimpl.X.MessageStringOf(x) } func (*FakeDnsPoolMulti) ProtoMessage() {} func (x *FakeDnsPoolMulti) ProtoReflect() protoreflect.Message { mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FakeDnsPoolMulti.ProtoReflect.Descriptor instead. func (*FakeDnsPoolMulti) Descriptor() ([]byte, []int) { return file_app_dns_fakedns_fakedns_proto_rawDescGZIP(), []int{1} } func (x *FakeDnsPoolMulti) GetPools() []*FakeDnsPool { if x != nil { return x.Pools } return nil } var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor const file_app_dns_fakedns_fakedns_proto_rawDesc = "" + "\n" + "\x1dapp/dns/fakedns/fakedns.proto\x12\x1av2ray.core.app.dns.fakedns\x1a common/protoext/extensions.proto\"X\n" + "\vFakeDnsPool\x12\x17\n" + "\aip_pool\x18\x01 \x01(\tR\x06ipPool\x12\x18\n" + "\alruSize\x18\x02 \x01(\x03R\alruSize:\x16\x82\xb5\x18\x12\n" + "\aservice\x12\afakeDns\"n\n" + "\x10FakeDnsPoolMulti\x12=\n" + "\x05pools\x18\x01 \x03(\v2'.v2ray.core.app.dns.fakedns.FakeDnsPoolR\x05pools:\x1b\x82\xb5\x18\x17\n" + "\aservice\x12\ffakeDnsMultiBo\n" + "\x1ecom.v2ray.core.app.dns.fakednsP\x01Z.github.com/v2fly/v2ray-core/v5/app/dns/fakedns\xaa\x02\x1aV2Ray.Core.App.Dns.Fakednsb\x06proto3" var ( file_app_dns_fakedns_fakedns_proto_rawDescOnce sync.Once file_app_dns_fakedns_fakedns_proto_rawDescData []byte ) func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte { file_app_dns_fakedns_fakedns_proto_rawDescOnce.Do(func() { file_app_dns_fakedns_fakedns_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dns_fakedns_fakedns_proto_rawDesc), len(file_app_dns_fakedns_fakedns_proto_rawDesc))) }) return file_app_dns_fakedns_fakedns_proto_rawDescData } var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_app_dns_fakedns_fakedns_proto_goTypes = []any{ (*FakeDnsPool)(nil), // 0: v2ray.core.app.dns.fakedns.FakeDnsPool (*FakeDnsPoolMulti)(nil), // 1: v2ray.core.app.dns.fakedns.FakeDnsPoolMulti } var file_app_dns_fakedns_fakedns_proto_depIdxs = []int32{ 0, // 0: v2ray.core.app.dns.fakedns.FakeDnsPoolMulti.pools:type_name -> v2ray.core.app.dns.fakedns.FakeDnsPool 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_app_dns_fakedns_fakedns_proto_init() } func file_app_dns_fakedns_fakedns_proto_init() { if File_app_dns_fakedns_fakedns_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dns_fakedns_fakedns_proto_rawDesc), len(file_app_dns_fakedns_fakedns_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_dns_fakedns_fakedns_proto_goTypes, DependencyIndexes: file_app_dns_fakedns_fakedns_proto_depIdxs, MessageInfos: file_app_dns_fakedns_fakedns_proto_msgTypes, }.Build() File_app_dns_fakedns_fakedns_proto = out.File file_app_dns_fakedns_fakedns_proto_goTypes = nil file_app_dns_fakedns_fakedns_proto_depIdxs = nil } ================================================ FILE: app/dns/fakedns/fakedns.proto ================================================ syntax = "proto3"; package v2ray.core.app.dns.fakedns; option csharp_namespace = "V2Ray.Core.App.Dns.Fakedns"; option go_package = "github.com/v2fly/v2ray-core/v5/app/dns/fakedns"; option java_package = "com.v2ray.core.app.dns.fakedns"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message FakeDnsPool{ option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "fakeDns"; string ip_pool = 1; //CIDR of IP pool used as fake DNS IP int64 lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address } message FakeDnsPoolMulti{ option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "fakeDnsMulti"; repeated FakeDnsPool pools = 1; } ================================================ FILE: app/dns/fakedns/fakedns_test.go ================================================ package fakedns import ( gonet "net" "strconv" "testing" "github.com/stretchr/testify/assert" "golang.org/x/sync/errgroup" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/uuid" ) func TestNewFakeDnsHolder(_ *testing.T) { _, err := NewFakeDNSHolder() common.Must(err) } func TestFakeDnsHolderCreateMapping(t *testing.T) { fkdns, err := NewFakeDNSHolder() common.Must(err) addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") assert.Equal(t, "198.18.0.0", addr[0].IP().String()) } func TestFakeDnsHolderCreateMappingMany(t *testing.T) { fkdns, err := NewFakeDNSHolder() common.Must(err) addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") assert.Equal(t, "198.18.0.0", addr[0].IP().String()) addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org") assert.Equal(t, "198.18.0.1", addr2[0].IP().String()) } func TestFakeDnsHolderCreateMappingManyAndResolve(t *testing.T) { fkdns, err := NewFakeDNSHolder() common.Must(err) { addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") assert.Equal(t, "198.18.0.0", addr[0].IP().String()) } { addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org") assert.Equal(t, "198.18.0.1", addr2[0].IP().String()) } { result := fkdns.GetDomainFromFakeDNS(net.ParseAddress("198.18.0.0")) assert.Equal(t, "fakednstest.v2fly.org", result) } { result := fkdns.GetDomainFromFakeDNS(net.ParseAddress("198.18.0.1")) assert.Equal(t, "fakednstest2.v2fly.org", result) } } func TestFakeDnsHolderCreateMappingManySingleDomain(t *testing.T) { fkdns, err := NewFakeDNSHolder() common.Must(err) addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") assert.Equal(t, "198.18.0.0", addr[0].IP().String()) addr2 := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") assert.Equal(t, "198.18.0.0", addr2[0].IP().String()) } func TestGetFakeIPForDomainConcurrently(t *testing.T) { fkdns, err := NewFakeDNSHolder() common.Must(err) total := 200 addr := make([][]net.Address, total+1) var errg errgroup.Group for i := 0; i < total; i++ { errg.Go(testGetFakeIP(i, addr, fkdns)) } errg.Wait() for i := 0; i < total; i++ { for j := i + 1; j < total; j++ { assert.NotEqual(t, addr[i][0].IP().String(), addr[j][0].IP().String()) } } } func testGetFakeIP(index int, addr [][]net.Address, fkdns *Holder) func() error { return func() error { addr[index] = fkdns.GetFakeIPForDomain("fakednstest" + strconv.Itoa(index) + ".example.com") return nil } } func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) { fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{ IpPool: "240.0.0.0/12", LruSize: 256, }) common.Must(err) err = fkdns.Start() common.Must(err) { addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") assert.Equal(t, "240.0.0.0", addr[0].IP().String()) } { addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org") assert.Equal(t, "240.0.0.1", addr2[0].IP().String()) } for i := 0; i <= 8192; i++ { { result := fkdns.GetDomainFromFakeDNS(net.ParseAddress("240.0.0.0")) assert.Equal(t, "fakednstest.v2fly.org", result) } { result := fkdns.GetDomainFromFakeDNS(net.ParseAddress("240.0.0.1")) assert.Equal(t, "fakednstest2.v2fly.org", result) } { uuid := uuid.New() domain := uuid.String() + ".fakednstest.v2fly.org" addr := fkdns.GetFakeIPForDomain(domain) rsaddr := addr[0].IP().String() result := fkdns.GetDomainFromFakeDNS(net.ParseAddress(rsaddr)) assert.Equal(t, domain, result) } } } func TestFakeDNSMulti(t *testing.T) { fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{ Pools: []*FakeDnsPool{{ IpPool: "240.0.0.0/12", LruSize: 256, }, { IpPool: "fddd:c5b4:ff5f:f4f0::/64", LruSize: 256, }}, }, ) common.Must(err) err = fakeMulti.Start() common.Must(err) assert.Nil(t, err, "Should not throw error") _ = fakeMulti t.Run("checkInRange", func(t *testing.T) { t.Run("ipv4", func(t *testing.T) { inPool := fakeMulti.IsIPInIPPool(net.IPAddress([]byte{240, 0, 0, 5})) assert.True(t, inPool) }) t.Run("ipv6", func(t *testing.T) { ip, err := gonet.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5") assert.Nil(t, err) inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP)) assert.True(t, inPool) }) t.Run("ipv4_inverse", func(t *testing.T) { inPool := fakeMulti.IsIPInIPPool(net.IPAddress([]byte{241, 0, 0, 5})) assert.False(t, inPool) }) t.Run("ipv6_inverse", func(t *testing.T) { ip, err := gonet.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5") assert.Nil(t, err) inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP)) assert.False(t, inPool) }) }) t.Run("allocateTwoAddressForTwoPool", func(t *testing.T) { address := fakeMulti.GetFakeIPForDomain("fakednstest.v2fly.org") assert.Len(t, address, 2, "should be 2 address one for each pool") t.Run("eachOfThemShouldResolve:0", func(t *testing.T) { domain := fakeMulti.GetDomainFromFakeDNS(address[0]) assert.Equal(t, "fakednstest.v2fly.org", domain) }) t.Run("eachOfThemShouldResolve:1", func(t *testing.T) { domain := fakeMulti.GetDomainFromFakeDNS(address[1]) assert.Equal(t, "fakednstest.v2fly.org", domain) }) }) t.Run("understandIPTypeSelector", func(t *testing.T) { t.Run("ipv4", func(t *testing.T) { address := fakeMulti.GetFakeIPForDomain3("fakednstestipv4.v2fly.org", true, false) assert.Len(t, address, 1, "should be 1 address") assert.True(t, address[0].Family().IsIPv4()) }) t.Run("ipv6", func(t *testing.T) { address := fakeMulti.GetFakeIPForDomain3("fakednstestipv6.v2fly.org", false, true) assert.Len(t, address, 1, "should be 1 address") assert.True(t, address[0].Family().IsIPv6()) }) t.Run("ipv46", func(t *testing.T) { address := fakeMulti.GetFakeIPForDomain3("fakednstestipv46.v2fly.org", true, true) assert.Len(t, address, 2, "should be 2 address") assert.True(t, address[0].Family().IsIPv4()) assert.True(t, address[1].Family().IsIPv6()) }) }) } func TestFakeDNSMultiAddPool(t *testing.T) { runTest := func(runTestBeforeStart bool) { fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{ Pools: []*FakeDnsPool{{ IpPool: "240.0.0.0/12", LruSize: 256, }, { IpPool: "fddd:c5b4:ff5f:f4f0::/64", LruSize: 256, }}, }) common.Must(err) if !runTestBeforeStart { err = fakeMulti.Start() common.Must(err) } t.Run("ipv4_return_existing", func(t *testing.T) { pool, err := fakeMulti.AddPool(&FakeDnsPool{ IpPool: "240.0.0.1/12", LruSize: 256, }) common.Must(err) if pool != fakeMulti.holders[0] { t.Error("HolderMulti.AddPool not returning same holder for existing IPv4 pool") } }) t.Run("ipv6_return_existing", func(t *testing.T) { pool, err := fakeMulti.AddPool(&FakeDnsPool{ IpPool: "fddd:c5b4:ff5f:f4f0::1/64", LruSize: 256, }) common.Must(err) if pool != fakeMulti.holders[1] { t.Error("HolderMulti.AddPool not returning same holder for existing IPv6 pool") } }) t.Run("ipv4_reject_overlap", func(t *testing.T) { _, err := fakeMulti.AddPool(&FakeDnsPool{ IpPool: "240.8.0.0/13", LruSize: 256, }) if err == nil { t.Error("HolderMulti.AddPool not rejecting IPv4 pool that is subnet of existing ones") } _, err = fakeMulti.AddPool(&FakeDnsPool{ IpPool: "240.0.0.0/11", LruSize: 256, }) if err == nil { t.Error("HolderMulti.AddPool not rejecting IPv4 pool that contains existing ones") } }) t.Run("new_pool", func(t *testing.T) { pool, err := fakeMulti.AddPool(&FakeDnsPool{ IpPool: "192.168.168.0/16", LruSize: 256, }) common.Must(err) if pool != fakeMulti.holders[2] { t.Error("HolderMulti.AddPool not creating new holder for new IPv4 pool") } }) t.Run("add_pool_multi", func(t *testing.T) { pools, err := fakeMulti.AddPoolMulti(&FakeDnsPoolMulti{ Pools: []*FakeDnsPool{{ IpPool: "192.168.168.0/16", LruSize: 256, }, { IpPool: "2001:1111::/64", LruSize: 256, }}, }) common.Must(err) if len(pools.holders) != 2 { t.Error("HolderMulti.AddPoolMutli not returning holderMulti that has the same length as passed PoolMulti config") } if pools.holders[0] != fakeMulti.holders[2] { t.Error("HolderMulti.AddPoolMulti not returning same holder for existing IPv4 pool 192.168.168.0/16") } if pools.holders[1] != fakeMulti.holders[3] { t.Error("HolderMulti.AddPoolMulti not creating new holder for new IPv6 pool 2001:1111::/64") } }) if runTestBeforeStart { err = fakeMulti.Start() common.Must(err) } } t.Run("addPoolBeforeStart", func(t *testing.T) { runTest(true) }) t.Run("addPoolAfterStart", func(t *testing.T) { runTest(false) }) } ================================================ FILE: app/dns/fakedns.go ================================================ //go:build !confonly // +build !confonly package dns import ( fakedns "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/dns" ) // FakeDNSClient is an implementation of dns.Client with FakeDNS enabled. type FakeDNSClient struct { *DNS } // LookupIP implements dns.Client. func (s *FakeDNSClient) LookupIP(domain string) ([]net.IP, error) { return s.lookupIPInternal(domain, dns.IPOption{IPv4Enable: true, IPv6Enable: true, FakeEnable: true}) } // LookupIPv4 implements dns.IPv4Lookup. func (s *FakeDNSClient) LookupIPv4(domain string) ([]net.IP, error) { return s.lookupIPInternal(domain, dns.IPOption{IPv4Enable: true, FakeEnable: true}) } // LookupIPv6 implements dns.IPv6Lookup. func (s *FakeDNSClient) LookupIPv6(domain string) ([]net.IP, error) { return s.lookupIPInternal(domain, dns.IPOption{IPv6Enable: true, FakeEnable: true}) } // FakeDNSEngine is an implementation of dns.FakeDNSEngine based on a fully functional DNS. type FakeDNSEngine struct { dns *DNS fakeHolders *fakedns.HolderMulti fakeDefault *fakedns.HolderMulti } // Type implements common.HasType. func (*FakeDNSEngine) Type() interface{} { return dns.FakeDNSEngineType() } // Start implements common.Runnable. func (f *FakeDNSEngine) Start() error { return f.fakeHolders.Start() } // Close implements common.Closable. func (f *FakeDNSEngine) Close() error { return f.fakeHolders.Close() } // GetFakeIPForDomain implements dns.FakeDNSEngine. func (f *FakeDNSEngine) GetFakeIPForDomain(domain string) []net.Address { return f.GetFakeIPForDomain3(domain, true, true) } // GetDomainFromFakeDNS implements dns.FakeDNSEngine. func (f *FakeDNSEngine) GetDomainFromFakeDNS(ip net.Address) string { return f.fakeHolders.GetDomainFromFakeDNS(ip) } // IsIPInIPPool implements dns.FakeDNSEngineRev0. func (f *FakeDNSEngine) IsIPInIPPool(ip net.Address) bool { return f.fakeHolders.IsIPInIPPool(ip) } // GetFakeIPForDomain3 implements dns.FakeDNSEngineRev0. func (f *FakeDNSEngine) GetFakeIPForDomain3(domain string, IPv4 bool, IPv6 bool) []net.Address { // nolint: gocritic option := dns.IPOption{IPv4Enable: IPv4, IPv6Enable: IPv6, FakeEnable: true} for _, client := range f.dns.sortClients(domain, option) { fakeServer, ok := client.fakeDNS.(*FakeDNSServer) if !ok { continue } fakeEngine, ok := fakeServer.fakeDNSEngine.(dns.FakeDNSEngineRev0) if !ok { return filterIP(fakeServer.fakeDNSEngine.GetFakeIPForDomain(domain), option) } return fakeEngine.GetFakeIPForDomain3(domain, IPv4, IPv6) } if f.fakeDefault != nil { return f.fakeDefault.GetFakeIPForDomain3(domain, IPv4, IPv6) } return nil } ================================================ FILE: app/dns/fakedns_test.go ================================================ package dns_test import ( "testing" "time" "github.com/google/go-cmp/cmp" "github.com/miekg/dns" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" . "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" "github.com/v2fly/v2ray-core/v5/app/policy" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" feature_dns "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" ) func TestFakeDNS(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } go dnsServer.ListenAndServe() time.Sleep(time.Second) config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServer: []*NameServer{ { // "fakedns" Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Domain{ Domain: "fakedns", }, }, Port: uint32(53), }, }, { // { "address": "127.0.0.1", "port": "", "domains": ["domain:google.com"], "fakedns": "198.19.0.0/16", "fallbackStrategy": "disabled" } Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, PrioritizedDomain: []*NameServer_PriorityDomain{ {Type: DomainMatchingType_Subdomain, Domain: "google.com"}, }, FakeDns: &fakedns.FakeDnsPoolMulti{ Pools: []*fakedns.FakeDnsPool{ {IpPool: "198.19.0.0/16", LruSize: 256}, }, }, FallbackStrategy: FallbackStrategy_Disabled.Enum(), }, }, FakeDns: &fakedns.FakeDnsPoolMulti{ // "fakedns": "198.18.0.0/16" Pools: []*fakedns.FakeDnsPool{ {IpPool: "198.18.0.0/16", LruSize: 256}, }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) common.Must(v.Start()) dnsClient := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) fakeClient := dnsClient.(feature_dns.ClientWithFakeDNS).AsFakeDNSClient() var fakeIPForFacebook net.IP var fakeIPForGoogle net.IP { // Lookup facebook.com with Fake Client will return 198.18.0.0/16 (global fake pool) ips, err := fakeClient.LookupIP("facebook.com") if err != nil { t.Fatal("unexpected error: ", err) } for _, ip := range ips { if !(&net.IPNet{IP: net.IP{198, 18, 0, 0}, Mask: net.CIDRMask(16, 8*net.IPv4len)}).Contains(ip) { t.Fatal("Lookup facebook.com with fake client not in global pool 198.18.0.0/16") } } fakeIPForFacebook = ips[0] } { // Lookup facebook.com with Normal Client with return empty record (because UDP server matching "domain:google.com" are configured with fallback disabled) _, err := dnsClient.LookupIP("facebook.com") if err != feature_dns.ErrEmptyResponse { t.Fatal("Lookup facebook.com with normal client not returning empty response") } } { // Lookup google.com with Fake Client will return 198.19.0.0/16 (local fake pool) ips, err := fakeClient.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } for _, ip := range ips { if !(&net.IPNet{IP: net.IP{198, 19, 0, 0}, Mask: net.CIDRMask(16, 8*net.IPv4len)}).Contains(ip) { t.Fatal("Lookup google.com with fake client not in global pool 198.19.0.0/16") } } fakeIPForGoogle = ips[0] } { // Lookup google.com with Normal Client will return 8.8.8.8 ips, err := dnsClient.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } if r := cmp.Diff(ips, []net.IP{{8, 8, 8, 8}}); r != "" { t.Fatal("Lookup google.com with normal client not returning 8.8.8.8") } } fakeEngine := dnsClient.(feature_dns.ClientWithFakeDNS).AsFakeDNSEngine().(feature_dns.FakeDNSEngineRev0) { if !fakeEngine.IsIPInIPPool(net.IPAddress(fakeIPForFacebook)) { t.Fatal("Fake IP of domain facebook.com not in FakeDNSEngine's pool.") } if !fakeEngine.IsIPInIPPool(net.IPAddress(fakeIPForGoogle)) { t.Fatal("Fake IP of domain google.com not in FakeDNSEngine's pool.") } } { if domain := fakeEngine.GetDomainFromFakeDNS(net.IPAddress(fakeIPForFacebook)); domain != "facebook.com" { t.Fatal("Recover fake IP to get domain facebook.com failed.") } if domain := fakeEngine.GetDomainFromFakeDNS(net.IPAddress(fakeIPForGoogle)); domain != "google.com" { t.Fatal("Recover fake IP to get domain google.com failed.") } } { ips := fakeEngine.GetFakeIPForDomain("api.google.com") for _, ip := range ips { if !(&net.IPNet{IP: net.IP{198, 19, 0, 0}, Mask: net.CIDRMask(16, 8*net.IPv4len)}).Contains(ip.IP()) { t.Fatal("Fake IP for api.google.com not in local pool 198.19.0.0/16") } } } { ips := fakeEngine.GetFakeIPForDomain3("v2fly.org", true, false) for _, ip := range ips { if !(&net.IPNet{IP: net.IP{198, 18, 0, 0}, Mask: net.CIDRMask(16, 8*net.IPv4len)}).Contains(ip.IP()) { t.Fatal("Fake IP for v2fly.org not in global pool 198.18.0.0/16") } } } } func TestFakeDNSEmptyGlobalConfig(t *testing.T) { config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&Config{ NameServer: []*NameServer{ { // "fakedns" Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Domain{ Domain: "fakedns", }, }, }, QueryStrategy: QueryStrategy_USE_IP4.Enum(), }, { // "localhost" Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Domain{ Domain: "localhost", }, }, }, QueryStrategy: QueryStrategy_USE_IP6.Enum(), PrioritizedDomain: []*NameServer_PriorityDomain{ {Type: DomainMatchingType_Subdomain, Domain: "google.com"}, }, FakeDns: &fakedns.FakeDnsPoolMulti{Pools: []*fakedns.FakeDnsPool{}}, // "fakedns": true }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } v, err := core.New(config) common.Must(err) common.Must(v.Start()) dnsClient := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) fakeClient := dnsClient.(feature_dns.ClientWithFakeDNS).AsFakeDNSClient() { // Lookup facebook.com will return 198.18.0.0/15 (default IPv4 pool) ips, err := fakeClient.LookupIP("facebook.com") if err != nil { t.Fatal("unexpected error: ", err) } for _, ip := range ips { if !(&net.IPNet{IP: net.IP{198, 18, 0, 0}, Mask: net.CIDRMask(15, 8*net.IPv4len)}).Contains(ip) { t.Fatal("Lookup facebook.com with fake client not in default IPv4 pool 198.18.0.0/15") } } } { // Lookup google.com will return fc00::/18 (default IPv6 pool) ips, err := fakeClient.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } for _, ip := range ips { if !(&net.IPNet{IP: net.IP{0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Mask: net.CIDRMask(18, 8*net.IPv6len)}).Contains(ip) { t.Fatal("Lookup google.com with fake client not in default IPv6 pool fc00::/18") } } } } ================================================ FILE: app/dns/hosts.go ================================================ package dns import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/strmatcher" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/dns" ) // StaticHosts represents static domain-ip mapping in DNS server. type StaticHosts struct { ips [][]net.Address matchers *strmatcher.LinearIndexMatcher } // NewStaticHosts creates a new StaticHosts instance. func NewStaticHosts(hosts []*HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) { g := new(strmatcher.LinearIndexMatcher) sh := &StaticHosts{ ips: make([][]net.Address, len(hosts)+len(legacy)+16), matchers: g, } if legacy != nil { features.PrintDeprecatedFeatureWarning("simple host mapping") for domain, ip := range legacy { matcher, err := strmatcher.Full.New(domain) common.Must(err) id := g.Add(matcher) address := ip.AsAddress() if address.Family().IsDomain() { return nil, newError("invalid domain address in static hosts: ", address.Domain()).AtWarning() } sh.ips[id] = []net.Address{address} } } for _, mapping := range hosts { matcher, err := toStrMatcher(mapping.Type, mapping.Domain) if err != nil { return nil, newError("failed to create domain matcher").Base(err) } id := g.Add(matcher) ips := make([]net.Address, 0, len(mapping.Ip)+1) switch { case len(mapping.ProxiedDomain) > 0: ips = append(ips, net.DomainAddress(mapping.ProxiedDomain)) case len(mapping.Ip) > 0: for _, ip := range mapping.Ip { addr := net.IPAddress(ip) if addr == nil { return nil, newError("invalid IP address in static hosts: ", ip).AtWarning() } ips = append(ips, addr) } default: return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning() } sh.ips[id] = ips } return sh, nil } func (h *StaticHosts) lookupInternal(domain string) []net.Address { var ips []net.Address for _, id := range h.matchers.Match(domain) { ips = append(ips, h.ips[id]...) } return ips } func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address { switch addrs := h.lookupInternal(domain); { case len(addrs) == 0: // Not recorded in static hosts, return nil return nil case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain newError("found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it").AtDebug().WriteToLog() if maxDepth > 0 { unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1) if unwrapped != nil { return unwrapped } } return addrs default: // IP record found, return a non-nil IP array return filterIP(addrs, option) } } // Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts. func (h *StaticHosts) Lookup(domain string, option dns.IPOption) []net.Address { return h.lookup(domain, option, 5) } ================================================ FILE: app/dns/hosts_test.go ================================================ package dns_test import ( "testing" "github.com/google/go-cmp/cmp" . "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/dns" ) func TestStaticHosts(t *testing.T) { pb := []*HostMapping{ { Type: DomainMatchingType_Full, Domain: "v2fly.org", Ip: [][]byte{ {1, 1, 1, 1}, }, }, { Type: DomainMatchingType_Full, Domain: "proxy.v2fly.org", Ip: [][]byte{ {1, 2, 3, 4}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, }, ProxiedDomain: "another-proxy.v2fly.org", }, { Type: DomainMatchingType_Full, Domain: "proxy2.v2fly.org", ProxiedDomain: "proxy.v2fly.org", }, { Type: DomainMatchingType_Subdomain, Domain: "v2ray.cn", Ip: [][]byte{ {2, 2, 2, 2}, }, }, { Type: DomainMatchingType_Subdomain, Domain: "baidu.com", Ip: [][]byte{ {127, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, }, }, } hosts, err := NewStaticHosts(pb, nil) common.Must(err) { ips := hosts.Lookup("v2fly.org", dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }) if len(ips) != 1 { t.Error("expect 1 IP, but got ", len(ips)) } if diff := cmp.Diff([]byte(ips[0].IP()), []byte{1, 1, 1, 1}); diff != "" { t.Error(diff) } } { domain := hosts.Lookup("proxy.v2fly.org", dns.IPOption{ IPv4Enable: true, IPv6Enable: false, }) if len(domain) != 1 { t.Error("expect 1 domain, but got ", len(domain)) } if diff := cmp.Diff(domain[0].Domain(), "another-proxy.v2fly.org"); diff != "" { t.Error(diff) } } { domain := hosts.Lookup("proxy2.v2fly.org", dns.IPOption{ IPv4Enable: true, IPv6Enable: false, }) if len(domain) != 1 { t.Error("expect 1 domain, but got ", len(domain)) } if diff := cmp.Diff(domain[0].Domain(), "another-proxy.v2fly.org"); diff != "" { t.Error(diff) } } { ips := hosts.Lookup("www.v2ray.cn", dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }) if len(ips) != 1 { t.Error("expect 1 IP, but got ", len(ips)) } if diff := cmp.Diff([]byte(ips[0].IP()), []byte{2, 2, 2, 2}); diff != "" { t.Error(diff) } } { ips := hosts.Lookup("baidu.com", dns.IPOption{ IPv4Enable: false, IPv6Enable: true, }) if len(ips) != 1 { t.Error("expect 1 IP, but got ", len(ips)) } if diff := cmp.Diff([]byte(ips[0].IP()), []byte(net.LocalHostIPv6.IP())); diff != "" { t.Error(diff) } } } ================================================ FILE: app/dns/nameserver.go ================================================ package dns import ( "context" "net/url" "strings" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/routing" ) // Server is the interface for Name Server. type Server interface { // Name of the Client. Name() string // QueryIP sends IP queries to its configured server. QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error) } // Client is the interface for DNS client. type Client struct { server Server clientIP net.IP tag string queryStrategy dns.IPOption cacheStrategy CacheStrategy fallbackStrategy FallbackStrategy domains []string expectIPs []*router.GeoIPMatcher fakeDNS Server } var errExpectedIPNonMatch = errors.New("expectIPs not match") // NewServer creates a name server object according to the network destination url. func NewServer(ctx context.Context, dest net.Destination, onCreated func(Server) error) error { onCreatedWithError := func(server Server, err error) error { if err != nil { return err } return onCreated(server) } if address := dest.Address; address.Family().IsDomain() { u, err := url.Parse(address.Domain()) if err != nil { return err } switch { case strings.EqualFold(u.String(), "localhost"): return onCreated(NewLocalNameServer()) case strings.EqualFold(u.String(), "fakedns"): return core.RequireFeatures(ctx, func(fakedns dns.FakeDNSEngine) error { return onCreated(NewFakeDNSServer(fakedns)) }) case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreatedWithError(NewDoHNameServer(u, dispatcher)) }) case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode return onCreated(NewDoHLocalNameServer(u)) case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreatedWithError(NewTCPNameServer(u, dispatcher)) }) case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode return onCreatedWithError(NewTCPLocalNameServer(u)) case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode return onCreatedWithError(NewQUICNameServer(u)) } } if dest.Network == net.Network_Unknown { dest.Network = net.Network_UDP } if dest.Network == net.Network_UDP { // UDP classic DNS mode return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreated(NewClassicNameServer(dest, dispatcher)) }) } return newError("No available name server could be created from ", dest).AtWarning() } // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs. func NewClient(ctx context.Context, ns *NameServer, dns *Config) (*Client, error) { client := &Client{} // Create DNS server instance err := NewServer(ctx, ns.Address.AsDestination(), func(server Server) error { client.server = server return nil }) if err != nil { return nil, err } // Initialize fields with default values if len(ns.Tag) == 0 { ns.Tag = dns.Tag if len(ns.Tag) == 0 { ns.Tag = generateRandomTag() } } if len(ns.ClientIp) == 0 { ns.ClientIp = dns.ClientIp } if ns.QueryStrategy == nil { ns.QueryStrategy = &dns.QueryStrategy } if ns.CacheStrategy == nil { ns.CacheStrategy = new(CacheStrategy) switch { case dns.CacheStrategy != CacheStrategy_CacheEnabled: *ns.CacheStrategy = dns.CacheStrategy case dns.DisableCache: features.PrintDeprecatedFeatureWarning("DNS disableCache settings") *ns.CacheStrategy = CacheStrategy_CacheDisabled } } if ns.FallbackStrategy == nil { ns.FallbackStrategy = new(FallbackStrategy) switch { case ns.SkipFallback: features.PrintDeprecatedFeatureWarning("DNS server skipFallback settings") *ns.FallbackStrategy = FallbackStrategy_Disabled case dns.FallbackStrategy != FallbackStrategy_Enabled: *ns.FallbackStrategy = dns.FallbackStrategy case dns.DisableFallback: features.PrintDeprecatedFeatureWarning("DNS disableFallback settings") *ns.FallbackStrategy = FallbackStrategy_Disabled case dns.DisableFallbackIfMatch: features.PrintDeprecatedFeatureWarning("DNS disableFallbackIfMatch settings") *ns.FallbackStrategy = FallbackStrategy_DisabledIfAnyMatch } } if (ns.FakeDns != nil && len(ns.FakeDns.Pools) == 0) || // Use globally configured fake ip pool if: 1. `fakedns` config is set, but empty(represents { "fakedns": true } in JSON settings); ns.FakeDns == nil && strings.EqualFold(ns.Address.Address.GetDomain(), "fakedns") { // 2. `fakedns` config not set, but server address is `fakedns`(represents { "address": "fakedns" } in JSON settings). if dns.FakeDns != nil { ns.FakeDns = dns.FakeDns } else { ns.FakeDns = &fakedns.FakeDnsPoolMulti{} queryStrategy := toIPOption(*ns.QueryStrategy) if queryStrategy.IPv4Enable { ns.FakeDns.Pools = append(ns.FakeDns.Pools, &fakedns.FakeDnsPool{ IpPool: "198.18.0.0/15", LruSize: 65535, }) } if queryStrategy.IPv6Enable { ns.FakeDns.Pools = append(ns.FakeDns.Pools, &fakedns.FakeDnsPool{ IpPool: "fc00::/18", LruSize: 65535, }) } } } // Priotize local domains with specific TLDs or without any dot to local DNS if strings.EqualFold(ns.Address.Address.GetDomain(), "localhost") { ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...) ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule) } if len(ns.ClientIp) > 0 { newError("DNS: client ", ns.Address.Address.AsAddress(), " uses clientIP ", net.IP(ns.ClientIp).String()).AtInfo().WriteToLog() } client.clientIP = ns.ClientIp client.tag = ns.Tag client.queryStrategy = toIPOption(*ns.QueryStrategy) client.cacheStrategy = *ns.CacheStrategy client.fallbackStrategy = *ns.FallbackStrategy return client, nil } // Name returns the server name the client manages. func (c *Client) Name() string { return c.server.Name() } // QueryIP send DNS query to the name server with the client's IP and IP options. func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, error) { queryOption := option.With(c.queryStrategy) if !queryOption.IsValid() { newError(c.server.Name(), " returns empty answer: ", domain, ". ", toReqTypes(option)).AtInfo().WriteToLog() return nil, dns.ErrEmptyResponse } server := c.server if queryOption.FakeEnable && c.fakeDNS != nil { server = c.fakeDNS } disableCache := c.cacheStrategy == CacheStrategy_CacheDisabled ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: c.tag}) ctx, cancel := context.WithTimeout(ctx, 4*time.Second) ips, err := server.QueryIP(ctx, domain, c.clientIP, queryOption, disableCache) cancel() if err != nil || queryOption.FakeEnable { return ips, err } return c.MatchExpectedIPs(domain, ips) } // MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones. func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) { if len(c.expectIPs) == 0 { return ips, nil } newIps := []net.IP{} for _, ip := range ips { for _, matcher := range c.expectIPs { if matcher.Match(ip) { newIps = append(newIps, ip) break } } } if len(newIps) == 0 { return nil, errExpectedIPNonMatch } newError("domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()).AtDebug().WriteToLog() return newIps, nil } ================================================ FILE: app/dns/nameserver_doh.go ================================================ //go:build !confonly // +build !confonly package dns import ( "bytes" "context" "fmt" "io" "net/http" "net/url" "sync" "time" "golang.org/x/net/dns/dnsmessage" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/dns" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/pubsub" "github.com/v2fly/v2ray-core/v5/common/task" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet" ) // DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format, // which is compatible with traditional dns over udp(RFC1035), // thus most of the DOH implementation is copied from udpns.go type DoHNameServer struct { sync.RWMutex ips map[string]record pub *pubsub.Service cleanup *task.Periodic httpClient *http.Client dohURL string name string } // NewDoHNameServer creates DOH server object for remote resolving. func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServer, error) { newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog() s := baseDOHNameServer(url, "DOH") // Dispatched connection will be closed (interrupted) after each request // This makes DOH inefficient without a keep-alived connection // See: core/app/proxyman/outbound/handler.go:113 // Using mux (https request wrapped in a stream layer) improves the situation. // Recommend to use NewDoHLocalNameServer (DOHL:) if v2ray instance is running on // a normal network eg. the server side of v2ray tr := &http.Transport{ MaxIdleConns: 30, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 30 * time.Second, ForceAttemptHTTP2: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { dest, err := net.ParseDestination(network + ":" + addr) if err != nil { return nil, err } link, err := dispatcher.Dispatch(ctx, dest) if err != nil { return nil, err } return net.NewConnection( net.ConnectionInputMulti(link.Writer), net.ConnectionOutputMulti(link.Reader), ), nil }, } dispatchedClient := &http.Client{ Transport: tr, Timeout: 60 * time.Second, } s.httpClient = dispatchedClient return s, nil } // NewDoHLocalNameServer creates DOH client object for local resolving func NewDoHLocalNameServer(url *url.URL) *DoHNameServer { url.Scheme = "https" s := baseDOHNameServer(url, "DOHL") tr := &http.Transport{ IdleConnTimeout: 90 * time.Second, ForceAttemptHTTP2: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { dest, err := net.ParseDestination(network + ":" + addr) if err != nil { return nil, err } conn, err := internet.DialSystem(ctx, dest, nil) if err != nil { return nil, err } return conn, nil }, } s.httpClient = &http.Client{ Timeout: time.Second * 180, Transport: tr, } newError("DNS: created Local DOH client for ", url.String()).AtInfo().WriteToLog() return s } func baseDOHNameServer(url *url.URL, prefix string) *DoHNameServer { s := &DoHNameServer{ ips: make(map[string]record), pub: pubsub.NewService(), name: prefix + "//" + url.Host, dohURL: url.String(), } s.cleanup = &task.Periodic{ Interval: time.Minute, Execute: s.Cleanup, } return s } // Name implements Server. func (s *DoHNameServer) Name() string { return s.name } // Cleanup clears expired items from cache func (s *DoHNameServer) Cleanup() error { now := time.Now() s.Lock() defer s.Unlock() if len(s.ips) == 0 { return newError("nothing to do. stopping...") } for domain, record := range s.ips { if record.A != nil && record.A.Expire.Before(now) { record.A = nil } if record.AAAA != nil && record.AAAA.Expire.Before(now) { record.AAAA = nil } if record.A == nil && record.AAAA == nil { newError(s.name, " cleanup ", domain).AtDebug().WriteToLog() delete(s.ips, domain) } else { s.ips[domain] = record } } if len(s.ips) == 0 { s.ips = make(map[string]record) } return nil } func (s *DoHNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) { elapsed := time.Since(req.start) s.Lock() rec := s.ips[req.domain] updated := false switch req.reqType { case dnsmessage.TypeA: if isNewer(rec.A, ipRec) { rec.A = ipRec updated = true } case dnsmessage.TypeAAAA: addr := make([]net.Address, 0) for _, ip := range ipRec.IP { if len(ip.IP()) == net.IPv6len { addr = append(addr, ip) } } ipRec.IP = addr if isNewer(rec.AAAA, ipRec) { rec.AAAA = ipRec updated = true } } newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog() if updated { s.ips[req.domain] = rec } switch req.reqType { case dnsmessage.TypeA: s.pub.Publish(req.domain+"4", nil) case dnsmessage.TypeAAAA: s.pub.Publish(req.domain+"6", nil) } s.Unlock() common.Must(s.cleanup.Start()) } func (s *DoHNameServer) newReqID() uint16 { return 0 } func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) { newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx)) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) var deadline time.Time if d, ok := ctx.Deadline(); ok { deadline = d } else { deadline = time.Now().Add(time.Second * 5) } for _, req := range reqs { go func(r *dnsRequest) { // generate new context for each req, using same context // may cause reqs all aborted if any one encounter an error dnsCtx := ctx // reserve internal dns server requested Inbound if inbound := session.InboundFromContext(ctx); inbound != nil { dnsCtx = session.ContextWithInbound(dnsCtx, inbound) } dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{ Protocol: "tls", SkipDNSResolve: true, }) // forced to use mux for DOH dnsCtx = session.ContextWithMuxPrefered(dnsCtx, true) var cancel context.CancelFunc dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline) defer cancel() b, err := dns.PackMessage(r.msg) if err != nil { newError("failed to pack dns query").Base(err).AtError().WriteToLog() return } resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes()) if err != nil { newError("failed to retrieve response").Base(err).AtError().WriteToLog() return } rec, err := parseResponse(resp) if err != nil { newError("failed to handle DOH response").Base(err).AtError().WriteToLog() return } s.updateIP(r, rec) }(req) } } func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte, error) { body := bytes.NewBuffer(b) req, err := http.NewRequest("POST", s.dohURL, body) if err != nil { return nil, err } req.Header.Add("Accept", "application/dns-message") req.Header.Add("Content-Type", "application/dns-message") resp, err := s.httpClient.Do(req.WithContext(ctx)) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { io.Copy(io.Discard, resp.Body) // flush resp.Body so that the conn is reusable return nil, fmt.Errorf("DOH server returned code %d", resp.StatusCode) } return io.ReadAll(resp.Body) } func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { s.RLock() record, found := s.ips[domain] s.RUnlock() if !found { return nil, errRecordNotFound } var ips []net.Address var lastErr error if option.IPv4Enable { a, err := record.A.getIPs() if err != nil { lastErr = err } ips = append(ips, a...) } if option.IPv6Enable { aaaa, err := record.AAAA.getIPs() if err != nil { lastErr = err } ips = append(ips, aaaa...) } if len(ips) > 0 { return toNetIP(ips) } if lastErr != nil { return nil, lastErr } return nil, dns_feature.ErrEmptyResponse } // QueryIP implements Server. func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl fqdn := Fqdn(domain) if disableCache { newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } else { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() return ips, err } } // ipv4 and ipv6 belong to different subscription groups var sub4, sub6 *pubsub.Subscriber if option.IPv4Enable { sub4 = s.pub.Subscribe(fqdn + "4") defer sub4.Close() } if option.IPv6Enable { sub6 = s.pub.Subscribe(fqdn + "6") defer sub6.Close() } done := make(chan interface{}) go func() { if sub4 != nil { select { case <-sub4.Wait(): case <-ctx.Done(): } } if sub6 != nil { select { case <-sub6.Wait(): case <-ctx.Done(): } } close(done) }() s.sendQuery(ctx, fqdn, clientIP, option) for { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { return ips, err } select { case <-ctx.Done(): return nil, ctx.Err() case <-done: } } } ================================================ FILE: app/dns/nameserver_doh_test.go ================================================ package dns_test import ( "context" "net/url" "testing" "time" "github.com/google/go-cmp/cmp" . "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" ) func TestDoHLocalNameServer(t *testing.T) { url, err := url.Parse("https+local://1.1.1.1/dns-query") common.Must(err) s := NewDoHLocalNameServer(url) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) cancel() common.Must(err) if len(ips) == 0 { t.Error("expect some ips, but got 0") } } func TestDoHLocalNameServerWithCache(t *testing.T) { url, err := url.Parse("https+local://1.1.1.1/dns-query") common.Must(err) s := NewDoHLocalNameServer(url) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) cancel() common.Must(err) if len(ips) == 0 { t.Error("expect some ips, but got 0") } ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, true) cancel() common.Must(err) if r := cmp.Diff(ips2, ips); r != "" { t.Fatal(r) } } ================================================ FILE: app/dns/nameserver_fakedns.go ================================================ //go:build !confonly // +build !confonly package dns import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/dns" ) type FakeDNSServer struct { fakeDNSEngine dns.FakeDNSEngine } func NewFakeDNSServer(fakeDNSEngine dns.FakeDNSEngine) *FakeDNSServer { return &FakeDNSServer{fakeDNSEngine: fakeDNSEngine} } func (FakeDNSServer) Name() string { return "fakedns" } func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, opt dns.IPOption, _ bool) ([]net.IP, error) { if !opt.FakeEnable { return nil, nil // Returning empty ip record with no error will continue DNS lookup, effectively indicating that this server is disabled. } if f.fakeDNSEngine == nil { if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) { f.fakeDNSEngine = fd }); err != nil { return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError() } } var ips []net.Address if fkr0, ok := f.fakeDNSEngine.(dns.FakeDNSEngineRev0); ok { ips = fkr0.GetFakeIPForDomain3(domain, opt.IPv4Enable, opt.IPv6Enable) } else { ips = filterIP(f.fakeDNSEngine.GetFakeIPForDomain(domain), opt) } netIP, err := toNetIP(ips) if err != nil { return nil, newError("Unable to convert IP to net ip").Base(err).AtError() } newError(f.Name(), " got answer: ", domain, " -> ", ips).AtInfo().WriteToLog() if len(netIP) > 0 { return netIP, nil } return nil, dns.ErrEmptyResponse } func isFakeDNS(server Server) bool { _, ok := server.(*FakeDNSServer) return ok } ================================================ FILE: app/dns/nameserver_local.go ================================================ //go:build !confonly // +build !confonly package dns import ( "context" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/dns/localdns" ) // LocalNameServer is an wrapper over local DNS feature. type LocalNameServer struct { client *localdns.Client } // QueryIP implements Server. func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) ([]net.IP, error) { var ips []net.IP var err error switch { case option.IPv4Enable && option.IPv6Enable: ips, err = s.client.LookupIP(domain) case option.IPv4Enable: ips, err = s.client.LookupIPv4(domain) case option.IPv6Enable: ips, err = s.client.LookupIPv6(domain) } if len(ips) > 0 { newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog() } return ips, err } // Name implements Server. func (s *LocalNameServer) Name() string { return "localhost" } // NewLocalNameServer creates localdns server object for directly lookup in system DNS. func NewLocalNameServer() *LocalNameServer { newError("DNS: created localhost client").AtInfo().WriteToLog() return &LocalNameServer{ client: localdns.New(), } } // NewLocalDNSClient creates localdns client object for directly lookup in system DNS. func NewLocalDNSClient() *Client { return &Client{server: NewLocalNameServer()} } ================================================ FILE: app/dns/nameserver_local_test.go ================================================ package dns_test import ( "context" "testing" "time" . "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/dns" ) func TestLocalNameServer(t *testing.T) { s := NewLocalNameServer() ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) cancel() common.Must(err) if len(ips) == 0 { t.Error("expect some ips, but got 0") } } ================================================ FILE: app/dns/nameserver_quic.go ================================================ package dns import ( "bytes" "context" "encoding/binary" "net/url" "sync" "time" "github.com/quic-go/quic-go" "golang.org/x/net/dns/dnsmessage" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/dns" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/pubsub" "github.com/v2fly/v2ray-core/v5/common/task" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) // NextProtoDQ - During connection establishment, DNS/QUIC support is indicated // by selecting the ALPN token "doq" in the crypto handshake. const NextProtoDQ = "doq" const handshakeIdleTimeout = time.Second * 8 // QUICNameServer implemented DNS over QUIC type QUICNameServer struct { sync.RWMutex ips map[string]record pub *pubsub.Service cleanup *task.Periodic name string destination net.Destination connection *quic.Conn } // NewQUICNameServer creates DNS-over-QUIC client object for local resolving func NewQUICNameServer(url *url.URL) (*QUICNameServer, error) { newError("DNS: created Local DNS-over-QUIC client for ", url.String()).AtInfo().WriteToLog() var err error port := net.Port(853) if url.Port() != "" { port, err = net.PortFromString(url.Port()) if err != nil { return nil, err } } dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port) s := &QUICNameServer{ ips: make(map[string]record), pub: pubsub.NewService(), name: url.String(), destination: dest, } s.cleanup = &task.Periodic{ Interval: time.Minute, Execute: s.Cleanup, } return s, nil } // Name returns client name func (s *QUICNameServer) Name() string { return s.name } // Cleanup clears expired items from cache func (s *QUICNameServer) Cleanup() error { now := time.Now() s.Lock() defer s.Unlock() if len(s.ips) == 0 { return newError("nothing to do. stopping...") } for domain, record := range s.ips { if record.A != nil && record.A.Expire.Before(now) { record.A = nil } if record.AAAA != nil && record.AAAA.Expire.Before(now) { record.AAAA = nil } if record.A == nil && record.AAAA == nil { newError(s.name, " cleanup ", domain).AtDebug().WriteToLog() delete(s.ips, domain) } else { s.ips[domain] = record } } if len(s.ips) == 0 { s.ips = make(map[string]record) } return nil } func (s *QUICNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) { elapsed := time.Since(req.start) s.Lock() rec := s.ips[req.domain] updated := false switch req.reqType { case dnsmessage.TypeA: if isNewer(rec.A, ipRec) { rec.A = ipRec updated = true } case dnsmessage.TypeAAAA: addr := make([]net.Address, 0) for _, ip := range ipRec.IP { if len(ip.IP()) == net.IPv6len { addr = append(addr, ip) } } ipRec.IP = addr if isNewer(rec.AAAA, ipRec) { rec.AAAA = ipRec updated = true } } newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog() if updated { s.ips[req.domain] = rec } switch req.reqType { case dnsmessage.TypeA: s.pub.Publish(req.domain+"4", nil) case dnsmessage.TypeAAAA: s.pub.Publish(req.domain+"6", nil) } s.Unlock() common.Must(s.cleanup.Start()) } func (s *QUICNameServer) newReqID() uint16 { return 0 } func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) { newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx)) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) var deadline time.Time if d, ok := ctx.Deadline(); ok { deadline = d } else { deadline = time.Now().Add(time.Second * 5) } for _, req := range reqs { go func(r *dnsRequest) { // generate new context for each req, using same context // may cause reqs all aborted if any one encounter an error dnsCtx := ctx // reserve internal dns server requested Inbound if inbound := session.InboundFromContext(ctx); inbound != nil { dnsCtx = session.ContextWithInbound(dnsCtx, inbound) } dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{ Protocol: "quic", SkipDNSResolve: true, }) var cancel context.CancelFunc dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline) defer cancel() b, err := dns.PackMessage(r.msg) if err != nil { newError("failed to pack dns query").Base(err).AtError().WriteToLog() return } dnsReqBuf := buf.New() binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) dnsReqBuf.Write(b.Bytes()) b.Release() conn, err := s.openStream(dnsCtx) if err != nil { newError("failed to open quic connection").Base(err).AtError().WriteToLog() return } _, err = conn.Write(dnsReqBuf.Bytes()) if err != nil { newError("failed to send query").Base(err).AtError().WriteToLog() return } _ = conn.Close() respBuf := buf.New() defer respBuf.Release() n, err := respBuf.ReadFullFrom(conn, 2) if err != nil && n == 0 { newError("failed to read response length").Base(err).AtError().WriteToLog() return } var length int16 err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length) if err != nil { newError("failed to parse response length").Base(err).AtError().WriteToLog() return } respBuf.Clear() n, err = respBuf.ReadFullFrom(conn, int32(length)) if err != nil && n == 0 { newError("failed to read response length").Base(err).AtError().WriteToLog() return } rec, err := parseResponse(respBuf.Bytes()) if err != nil { newError("failed to handle response").Base(err).AtError().WriteToLog() return } s.updateIP(r, rec) }(req) } } func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { s.RLock() record, found := s.ips[domain] s.RUnlock() if !found { return nil, errRecordNotFound } var ips []net.Address var lastErr error if option.IPv4Enable { a, err := record.A.getIPs() if err != nil { lastErr = err } ips = append(ips, a...) } if option.IPv6Enable { aaaa, err := record.AAAA.getIPs() if err != nil { lastErr = err } ips = append(ips, aaaa...) } if len(ips) > 0 { return toNetIP(ips) } if lastErr != nil { return nil, lastErr } return nil, dns_feature.ErrEmptyResponse } // QueryIP is called from dns.Server->queryIPTimeout func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { fqdn := Fqdn(domain) if disableCache { newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } else { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() return ips, err } } // ipv4 and ipv6 belong to different subscription groups var sub4, sub6 *pubsub.Subscriber if option.IPv4Enable { sub4 = s.pub.Subscribe(fqdn + "4") defer sub4.Close() } if option.IPv6Enable { sub6 = s.pub.Subscribe(fqdn + "6") defer sub6.Close() } done := make(chan interface{}) go func() { if sub4 != nil { select { case <-sub4.Wait(): case <-ctx.Done(): } } if sub6 != nil { select { case <-sub6.Wait(): case <-ctx.Done(): } } close(done) }() s.sendQuery(ctx, fqdn, clientIP, option) for { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { return ips, err } select { case <-ctx.Done(): return nil, ctx.Err() case <-done: } } } func isActive(s *quic.Conn) bool { select { case <-s.Context().Done(): return false default: return true } } func (s *QUICNameServer) getConnection(ctx context.Context) (*quic.Conn, error) { var conn *quic.Conn s.RLock() conn = s.connection if conn != nil && isActive(conn) { s.RUnlock() return conn, nil } if conn != nil { // we're recreating the connection, let's create a new one _ = conn.CloseWithError(0, "") } s.RUnlock() s.Lock() defer s.Unlock() var err error conn, err = s.openConnection(ctx) if err != nil { // This does not look too nice, but QUIC (or maybe quic-go) // doesn't seem stable enough. // Maybe retransmissions aren't fully implemented in quic-go? // Anyways, the simple solution is to make a second try when // it fails to open the QUIC connection. conn, err = s.openConnection(ctx) if err != nil { return nil, err } } s.connection = conn return conn, nil } func (s *QUICNameServer) openConnection(ctx context.Context) (*quic.Conn, error) { tlsConfig := tls.Config{ ServerName: func() string { switch s.destination.Address.Family() { case net.AddressFamilyIPv4, net.AddressFamilyIPv6: return s.destination.Address.IP().String() case net.AddressFamilyDomain: return s.destination.Address.Domain() default: panic("unknown address family") } }(), } quicConfig := &quic.Config{ HandshakeIdleTimeout: handshakeIdleTimeout, } conn, err := quic.DialAddr(ctx, s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto(NextProtoDQ)), quicConfig) if err != nil { return nil, err } return conn, nil } func (s *QUICNameServer) openStream(ctx context.Context) (*quic.Stream, error) { conn, err := s.getConnection(ctx) if err != nil { return nil, err } // open a new stream return conn.OpenStreamSync(ctx) } ================================================ FILE: app/dns/nameserver_quic_test.go ================================================ package dns_test import ( "context" "net/url" "testing" "time" "github.com/google/go-cmp/cmp" . "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" ) func TestQUICNameServer(t *testing.T) { url, err := url.Parse("quic://dns.adguard.com") common.Must(err) s, err := NewQUICNameServer(url) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) cancel() common.Must(err) if len(ips) == 0 { t.Error("expect some ips, but got 0") } } func TestQUICNameServerWithCache(t *testing.T) { url, err := url.Parse("quic://dns.adguard.com") common.Must(err) s, err := NewQUICNameServer(url) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) cancel() common.Must(err) if len(ips) == 0 { t.Error("expect some ips, but got 0") } ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, true) cancel() common.Must(err) if r := cmp.Diff(ips2, ips); r != "" { t.Fatal(r) } } ================================================ FILE: app/dns/nameserver_tcp.go ================================================ //go:build !confonly // +build !confonly package dns import ( "bytes" "context" "encoding/binary" "net/url" "sync" "sync/atomic" "time" "golang.org/x/net/dns/dnsmessage" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/dns" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/pubsub" "github.com/v2fly/v2ray-core/v5/common/task" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet" ) // TCPNameServer implemented DNS over TCP (RFC7766). type TCPNameServer struct { sync.RWMutex name string destination net.Destination ips map[string]record pub *pubsub.Service cleanup *task.Periodic reqID uint32 dial func(context.Context) (net.Conn, error) } // NewTCPNameServer creates DNS over TCP server object for remote resolving. func NewTCPNameServer(url *url.URL, dispatcher routing.Dispatcher) (*TCPNameServer, error) { s, err := baseTCPNameServer(url, "TCP") if err != nil { return nil, err } s.dial = func(ctx context.Context) (net.Conn, error) { link, err := dispatcher.Dispatch(ctx, s.destination) if err != nil { return nil, err } return net.NewConnection( net.ConnectionInputMulti(link.Writer), net.ConnectionOutputMulti(link.Reader), ), nil } return s, nil } // NewTCPLocalNameServer creates DNS over TCP client object for local resolving func NewTCPLocalNameServer(url *url.URL) (*TCPNameServer, error) { s, err := baseTCPNameServer(url, "TCPL") if err != nil { return nil, err } s.dial = func(ctx context.Context) (net.Conn, error) { return internet.DialSystem(ctx, s.destination, nil) } return s, nil } func baseTCPNameServer(url *url.URL, prefix string) (*TCPNameServer, error) { var err error port := net.Port(53) if url.Port() != "" { port, err = net.PortFromString(url.Port()) if err != nil { return nil, err } } dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port) s := &TCPNameServer{ destination: dest, ips: make(map[string]record), pub: pubsub.NewService(), name: prefix + "//" + dest.NetAddr(), } s.cleanup = &task.Periodic{ Interval: time.Minute, Execute: s.Cleanup, } return s, nil } // Name implements Server. func (s *TCPNameServer) Name() string { return s.name } // Cleanup clears expired items from cache func (s *TCPNameServer) Cleanup() error { now := time.Now() s.Lock() defer s.Unlock() if len(s.ips) == 0 { return newError("nothing to do. stopping...") } for domain, record := range s.ips { if record.A != nil && record.A.Expire.Before(now) { record.A = nil } if record.AAAA != nil && record.AAAA.Expire.Before(now) { record.AAAA = nil } if record.A == nil && record.AAAA == nil { newError(s.name, " cleanup ", domain).AtDebug().WriteToLog() delete(s.ips, domain) } else { s.ips[domain] = record } } if len(s.ips) == 0 { s.ips = make(map[string]record) } return nil } func (s *TCPNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) { elapsed := time.Since(req.start) s.Lock() rec := s.ips[req.domain] updated := false switch req.reqType { case dnsmessage.TypeA: if isNewer(rec.A, ipRec) { rec.A = ipRec updated = true } case dnsmessage.TypeAAAA: addr := make([]net.Address, 0) for _, ip := range ipRec.IP { if len(ip.IP()) == net.IPv6len { addr = append(addr, ip) } } ipRec.IP = addr if isNewer(rec.AAAA, ipRec) { rec.AAAA = ipRec updated = true } } newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog() if updated { s.ips[req.domain] = rec } switch req.reqType { case dnsmessage.TypeA: s.pub.Publish(req.domain+"4", nil) case dnsmessage.TypeAAAA: s.pub.Publish(req.domain+"6", nil) } s.Unlock() common.Must(s.cleanup.Start()) } func (s *TCPNameServer) newReqID() uint16 { return uint16(atomic.AddUint32(&s.reqID, 1)) } func (s *TCPNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) { newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx)) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) var deadline time.Time if d, ok := ctx.Deadline(); ok { deadline = d } else { deadline = time.Now().Add(time.Second * 5) } for _, req := range reqs { go func(r *dnsRequest) { dnsCtx := ctx if inbound := session.InboundFromContext(ctx); inbound != nil { dnsCtx = session.ContextWithInbound(dnsCtx, inbound) } dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{ Protocol: "dns", SkipDNSResolve: true, }) var cancel context.CancelFunc dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline) defer cancel() b, err := dns.PackMessage(r.msg) if err != nil { newError("failed to pack dns query").Base(err).AtError().WriteToLog() return } conn, err := s.dial(dnsCtx) if err != nil { newError("failed to dial namesever").Base(err).AtError().WriteToLog() return } defer conn.Close() dnsReqBuf := buf.New() binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) dnsReqBuf.Write(b.Bytes()) b.Release() _, err = conn.Write(dnsReqBuf.Bytes()) if err != nil { newError("failed to send query").Base(err).AtError().WriteToLog() return } dnsReqBuf.Release() respBuf := buf.New() defer respBuf.Release() n, err := respBuf.ReadFullFrom(conn, 2) if err != nil && n == 0 { newError("failed to read response length").Base(err).AtError().WriteToLog() return } var length int16 err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length) if err != nil { newError("failed to parse response length").Base(err).AtError().WriteToLog() return } respBuf.Clear() n, err = respBuf.ReadFullFrom(conn, int32(length)) if err != nil && n == 0 { newError("failed to read response length").Base(err).AtError().WriteToLog() return } rec, err := parseResponse(respBuf.Bytes()) if err != nil { newError("failed to parse DNS over TCP response").Base(err).AtError().WriteToLog() return } s.updateIP(r, rec) }(req) } } func (s *TCPNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { s.RLock() record, found := s.ips[domain] s.RUnlock() if !found { return nil, errRecordNotFound } var ips []net.Address var lastErr error if option.IPv4Enable { a, err := record.A.getIPs() if err != nil { lastErr = err } ips = append(ips, a...) } if option.IPv6Enable { aaaa, err := record.AAAA.getIPs() if err != nil { lastErr = err } ips = append(ips, aaaa...) } if len(ips) > 0 { return toNetIP(ips) } if lastErr != nil { return nil, lastErr } return nil, dns_feature.ErrEmptyResponse } // QueryIP implements Server. func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { fqdn := Fqdn(domain) if disableCache { newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } else { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() return ips, err } } // ipv4 and ipv6 belong to different subscription groups var sub4, sub6 *pubsub.Subscriber if option.IPv4Enable { sub4 = s.pub.Subscribe(fqdn + "4") defer sub4.Close() } if option.IPv6Enable { sub6 = s.pub.Subscribe(fqdn + "6") defer sub6.Close() } done := make(chan interface{}) go func() { if sub4 != nil { select { case <-sub4.Wait(): case <-ctx.Done(): } } if sub6 != nil { select { case <-sub6.Wait(): case <-ctx.Done(): } } close(done) }() s.sendQuery(ctx, fqdn, clientIP, option) for { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { return ips, err } select { case <-ctx.Done(): return nil, ctx.Err() case <-done: } } } ================================================ FILE: app/dns/nameserver_tcp_test.go ================================================ package dns_test import ( "context" "net/url" "testing" "time" "github.com/google/go-cmp/cmp" . "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" ) func TestTCPLocalNameServer(t *testing.T) { url, err := url.Parse("tcp+local://8.8.8.8") common.Must(err) s, err := NewTCPLocalNameServer(url) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) cancel() common.Must(err) if len(ips) == 0 { t.Error("expect some ips, but got 0") } } func TestTCPLocalNameServerWithCache(t *testing.T) { url, err := url.Parse("tcp+local://8.8.8.8") common.Must(err) s, err := NewTCPLocalNameServer(url) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) cancel() common.Must(err) if len(ips) == 0 { t.Error("expect some ips, but got 0") } ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, true) cancel() common.Must(err) if r := cmp.Diff(ips2, ips); r != "" { t.Fatal(r) } } ================================================ FILE: app/dns/nameserver_udp.go ================================================ //go:build !confonly // +build !confonly package dns import ( "context" "strings" "sync" "sync/atomic" "time" "golang.org/x/net/dns/dnsmessage" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/dns" udp_proto "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/pubsub" "github.com/v2fly/v2ray-core/v5/common/task" dns_feature "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) // ClassicNameServer implemented traditional UDP DNS. type ClassicNameServer struct { sync.RWMutex name string address net.Destination ips map[string]record requests map[uint16]dnsRequest pub *pubsub.Service udpServer udp.DispatcherI cleanup *task.Periodic reqID uint32 } // NewClassicNameServer creates udp server object for remote resolving. func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher) *ClassicNameServer { // default to 53 if unspecific if address.Port == 0 { address.Port = net.Port(53) } s := &ClassicNameServer{ address: address, ips: make(map[string]record), requests: make(map[uint16]dnsRequest), pub: pubsub.NewService(), name: strings.ToUpper(address.String()), } s.cleanup = &task.Periodic{ Interval: time.Minute, Execute: s.Cleanup, } s.udpServer = udp.NewSplitDispatcher(dispatcher, s.HandleResponse) newError("DNS: created UDP client initialized for ", address.NetAddr()).AtInfo().WriteToLog() return s } // Name implements Server. func (s *ClassicNameServer) Name() string { return s.name } // Cleanup clears expired items from cache func (s *ClassicNameServer) Cleanup() error { now := time.Now() s.Lock() defer s.Unlock() if len(s.ips) == 0 && len(s.requests) == 0 { return newError(s.name, " nothing to do. stopping...") } for domain, record := range s.ips { if record.A != nil && record.A.Expire.Before(now) { record.A = nil } if record.AAAA != nil && record.AAAA.Expire.Before(now) { record.AAAA = nil } if record.A == nil && record.AAAA == nil { delete(s.ips, domain) } else { s.ips[domain] = record } } if len(s.ips) == 0 { s.ips = make(map[string]record) } for id, req := range s.requests { if req.expire.Before(now) { delete(s.requests, id) } } if len(s.requests) == 0 { s.requests = make(map[uint16]dnsRequest) } return nil } // HandleResponse handles udp response packet from remote DNS server. func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) { ipRec, err := parseResponse(packet.Payload.Bytes()) if err != nil { newError(s.name, " fail to parse responded DNS udp").AtError().WriteToLog() return } s.Lock() id := ipRec.ReqID req, ok := s.requests[id] if ok { // remove the pending request delete(s.requests, id) } s.Unlock() if !ok { newError(s.name, " cannot find the pending request").AtError().WriteToLog() return } var rec record switch req.reqType { case dnsmessage.TypeA: rec.A = ipRec case dnsmessage.TypeAAAA: rec.AAAA = ipRec } elapsed := time.Since(req.start) newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog() if len(req.domain) > 0 && (rec.A != nil || rec.AAAA != nil) { s.updateIP(req.domain, rec) } } func (s *ClassicNameServer) updateIP(domain string, newRec record) { s.Lock() newError(s.name, " updating IP records for domain:", domain).AtDebug().WriteToLog() rec := s.ips[domain] updated := false if isNewer(rec.A, newRec.A) { rec.A = newRec.A updated = true } if isNewer(rec.AAAA, newRec.AAAA) { rec.AAAA = newRec.AAAA updated = true } if updated { s.ips[domain] = rec } if newRec.A != nil { s.pub.Publish(domain+"4", nil) } if newRec.AAAA != nil { s.pub.Publish(domain+"6", nil) } s.Unlock() common.Must(s.cleanup.Start()) } func (s *ClassicNameServer) newReqID() uint16 { return uint16(atomic.AddUint32(&s.reqID, 1)) } func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) { s.Lock() defer s.Unlock() id := req.msg.ID req.expire = time.Now().Add(time.Second * 8) s.requests[id] = *req } func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) { newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx)) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) for _, req := range reqs { s.addPendingRequest(req) b, _ := dns.PackMessage(req.msg) udpCtx := core.ToBackgroundDetachedContext(ctx) if inbound := session.InboundFromContext(ctx); inbound != nil { udpCtx = session.ContextWithInbound(udpCtx, inbound) } udpCtx = session.ContextWithContent(udpCtx, &session.Content{ Protocol: "dns", }) s.udpServer.Dispatch(udpCtx, s.address, b) } } func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { s.RLock() record, found := s.ips[domain] s.RUnlock() if !found { return nil, errRecordNotFound } var ips []net.Address var lastErr error if option.IPv4Enable { a, err := record.A.getIPs() if err != nil { lastErr = err } ips = append(ips, a...) } if option.IPv6Enable { aaaa, err := record.AAAA.getIPs() if err != nil { lastErr = err } ips = append(ips, aaaa...) } if len(ips) > 0 { return toNetIP(ips) } if lastErr != nil { return nil, lastErr } return nil, dns_feature.ErrEmptyResponse } // QueryIP implements Server. func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { fqdn := Fqdn(domain) if disableCache { newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } else { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() return ips, err } } // ipv4 and ipv6 belong to different subscription groups var sub4, sub6 *pubsub.Subscriber if option.IPv4Enable { sub4 = s.pub.Subscribe(fqdn + "4") defer sub4.Close() } if option.IPv6Enable { sub6 = s.pub.Subscribe(fqdn + "6") defer sub6.Close() } done := make(chan interface{}) go func() { if sub4 != nil { select { case <-sub4.Wait(): case <-ctx.Done(): } } if sub6 != nil { select { case <-sub6.Wait(): case <-ctx.Done(): } } close(done) }() s.sendQuery(ctx, fqdn, clientIP, option) for { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { return ips, err } select { case <-ctx.Done(): return nil, ctx.Err() case <-done: } } } ================================================ FILE: app/instman/command/command.go ================================================ package command import ( "context" "encoding/base64" "google.golang.org/grpc" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/extension" ) type service struct { UnimplementedInstanceManagementServiceServer instman extension.InstanceManagement } func (s service) ListInstance(ctx context.Context, req *ListInstanceReq) (*ListInstanceResp, error) { instanceNames, err := s.instman.ListInstance(ctx) if err != nil { return nil, err } return &ListInstanceResp{Name: instanceNames}, nil } func (s service) AddInstance(ctx context.Context, req *AddInstanceReq) (*AddInstanceResp, error) { configContent, err := base64.StdEncoding.DecodeString(req.ConfigContentB64) if err != nil { return nil, err } err = s.instman.AddInstance(ctx, req.Name, configContent, req.ConfigType) if err != nil { return nil, err } return &AddInstanceResp{}, nil } func (s service) StartInstance(ctx context.Context, req *StartInstanceReq) (*StartInstanceResp, error) { err := s.instman.StartInstance(ctx, req.Name) if err != nil { return nil, err } return &StartInstanceResp{}, nil } func (s service) Register(server *grpc.Server) { RegisterInstanceManagementServiceServer(server, s) } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { s := core.MustFromContext(ctx) sv := &service{} err := s.RequireFeatures(func(instman extension.InstanceManagement) { sv.instman = instman }) if err != nil { return nil, err } return sv, nil })) } ================================================ FILE: app/instman/command/command.pb.go ================================================ package command import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ListInstanceReq struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListInstanceReq) Reset() { *x = ListInstanceReq{} mi := &file_app_instman_command_command_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListInstanceReq) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListInstanceReq) ProtoMessage() {} func (x *ListInstanceReq) ProtoReflect() protoreflect.Message { mi := &file_app_instman_command_command_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListInstanceReq.ProtoReflect.Descriptor instead. func (*ListInstanceReq) Descriptor() ([]byte, []int) { return file_app_instman_command_command_proto_rawDescGZIP(), []int{0} } type ListInstanceResp struct { state protoimpl.MessageState `protogen:"open.v1"` Name []string `protobuf:"bytes,1,rep,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListInstanceResp) Reset() { *x = ListInstanceResp{} mi := &file_app_instman_command_command_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListInstanceResp) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListInstanceResp) ProtoMessage() {} func (x *ListInstanceResp) ProtoReflect() protoreflect.Message { mi := &file_app_instman_command_command_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListInstanceResp.ProtoReflect.Descriptor instead. func (*ListInstanceResp) Descriptor() ([]byte, []int) { return file_app_instman_command_command_proto_rawDescGZIP(), []int{1} } func (x *ListInstanceResp) GetName() []string { if x != nil { return x.Name } return nil } type AddInstanceReq struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` ConfigType string `protobuf:"bytes,2,opt,name=configType,proto3" json:"configType,omitempty"` ConfigContentB64 string `protobuf:"bytes,3,opt,name=configContentB64,proto3" json:"configContentB64,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddInstanceReq) Reset() { *x = AddInstanceReq{} mi := &file_app_instman_command_command_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddInstanceReq) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddInstanceReq) ProtoMessage() {} func (x *AddInstanceReq) ProtoReflect() protoreflect.Message { mi := &file_app_instman_command_command_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddInstanceReq.ProtoReflect.Descriptor instead. func (*AddInstanceReq) Descriptor() ([]byte, []int) { return file_app_instman_command_command_proto_rawDescGZIP(), []int{2} } func (x *AddInstanceReq) GetName() string { if x != nil { return x.Name } return "" } func (x *AddInstanceReq) GetConfigType() string { if x != nil { return x.ConfigType } return "" } func (x *AddInstanceReq) GetConfigContentB64() string { if x != nil { return x.ConfigContentB64 } return "" } type AddInstanceResp struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddInstanceResp) Reset() { *x = AddInstanceResp{} mi := &file_app_instman_command_command_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddInstanceResp) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddInstanceResp) ProtoMessage() {} func (x *AddInstanceResp) ProtoReflect() protoreflect.Message { mi := &file_app_instman_command_command_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddInstanceResp.ProtoReflect.Descriptor instead. func (*AddInstanceResp) Descriptor() ([]byte, []int) { return file_app_instman_command_command_proto_rawDescGZIP(), []int{3} } type StartInstanceReq struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StartInstanceReq) Reset() { *x = StartInstanceReq{} mi := &file_app_instman_command_command_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StartInstanceReq) String() string { return protoimpl.X.MessageStringOf(x) } func (*StartInstanceReq) ProtoMessage() {} func (x *StartInstanceReq) ProtoReflect() protoreflect.Message { mi := &file_app_instman_command_command_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StartInstanceReq.ProtoReflect.Descriptor instead. func (*StartInstanceReq) Descriptor() ([]byte, []int) { return file_app_instman_command_command_proto_rawDescGZIP(), []int{4} } func (x *StartInstanceReq) GetName() string { if x != nil { return x.Name } return "" } type StartInstanceResp struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StartInstanceResp) Reset() { *x = StartInstanceResp{} mi := &file_app_instman_command_command_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StartInstanceResp) String() string { return protoimpl.X.MessageStringOf(x) } func (*StartInstanceResp) ProtoMessage() {} func (x *StartInstanceResp) ProtoReflect() protoreflect.Message { mi := &file_app_instman_command_command_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StartInstanceResp.ProtoReflect.Descriptor instead. func (*StartInstanceResp) Descriptor() ([]byte, []int) { return file_app_instman_command_command_proto_rawDescGZIP(), []int{5} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_instman_command_command_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_instman_command_command_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_instman_command_command_proto_rawDescGZIP(), []int{6} } var File_app_instman_command_command_proto protoreflect.FileDescriptor const file_app_instman_command_command_proto_rawDesc = "" + "\n" + "!app/instman/command/command.proto\x12\x1ev2ray.core.app.instman.command\x1a common/protoext/extensions.proto\"\x11\n" + "\x0fListInstanceReq\"&\n" + "\x10ListInstanceResp\x12\x12\n" + "\x04name\x18\x01 \x03(\tR\x04name\"p\n" + "\x0eAddInstanceReq\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1e\n" + "\n" + "configType\x18\x02 \x01(\tR\n" + "configType\x12*\n" + "\x10configContentB64\x18\x03 \x01(\tR\x10configContentB64\"\x11\n" + "\x0fAddInstanceResp\"&\n" + "\x10StartInstanceReq\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\"\x13\n" + "\x11StartInstanceResp\"$\n" + "\x06Config:\x1a\x82\xb5\x18\x16\n" + "\vgrpcservice\x12\ainstman2\xf4\x02\n" + "\x19InstanceManagementService\x12q\n" + "\fListInstance\x12/.v2ray.core.app.instman.command.ListInstanceReq\x1a0.v2ray.core.app.instman.command.ListInstanceResp\x12n\n" + "\vAddInstance\x12..v2ray.core.app.instman.command.AddInstanceReq\x1a/.v2ray.core.app.instman.command.AddInstanceResp\x12t\n" + "\rStartInstance\x120.v2ray.core.app.instman.command.StartInstanceReq\x1a1.v2ray.core.app.instman.command.StartInstanceRespB\x7f\n" + "&com.v2ray.core.app.observatory.instmanP\x01Z2github.com/v2fly/v2ray-core/v5/app/instman/command\xaa\x02\x1eV2Ray.Core.App.Instman.Commandb\x06proto3" var ( file_app_instman_command_command_proto_rawDescOnce sync.Once file_app_instman_command_command_proto_rawDescData []byte ) func file_app_instman_command_command_proto_rawDescGZIP() []byte { file_app_instman_command_command_proto_rawDescOnce.Do(func() { file_app_instman_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_instman_command_command_proto_rawDesc), len(file_app_instman_command_command_proto_rawDesc))) }) return file_app_instman_command_command_proto_rawDescData } var file_app_instman_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_app_instman_command_command_proto_goTypes = []any{ (*ListInstanceReq)(nil), // 0: v2ray.core.app.instman.command.ListInstanceReq (*ListInstanceResp)(nil), // 1: v2ray.core.app.instman.command.ListInstanceResp (*AddInstanceReq)(nil), // 2: v2ray.core.app.instman.command.AddInstanceReq (*AddInstanceResp)(nil), // 3: v2ray.core.app.instman.command.AddInstanceResp (*StartInstanceReq)(nil), // 4: v2ray.core.app.instman.command.StartInstanceReq (*StartInstanceResp)(nil), // 5: v2ray.core.app.instman.command.StartInstanceResp (*Config)(nil), // 6: v2ray.core.app.instman.command.Config } var file_app_instman_command_command_proto_depIdxs = []int32{ 0, // 0: v2ray.core.app.instman.command.InstanceManagementService.ListInstance:input_type -> v2ray.core.app.instman.command.ListInstanceReq 2, // 1: v2ray.core.app.instman.command.InstanceManagementService.AddInstance:input_type -> v2ray.core.app.instman.command.AddInstanceReq 4, // 2: v2ray.core.app.instman.command.InstanceManagementService.StartInstance:input_type -> v2ray.core.app.instman.command.StartInstanceReq 1, // 3: v2ray.core.app.instman.command.InstanceManagementService.ListInstance:output_type -> v2ray.core.app.instman.command.ListInstanceResp 3, // 4: v2ray.core.app.instman.command.InstanceManagementService.AddInstance:output_type -> v2ray.core.app.instman.command.AddInstanceResp 5, // 5: v2ray.core.app.instman.command.InstanceManagementService.StartInstance:output_type -> v2ray.core.app.instman.command.StartInstanceResp 3, // [3:6] is the sub-list for method output_type 0, // [0:3] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_app_instman_command_command_proto_init() } func file_app_instman_command_command_proto_init() { if File_app_instman_command_command_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_instman_command_command_proto_rawDesc), len(file_app_instman_command_command_proto_rawDesc)), NumEnums: 0, NumMessages: 7, NumExtensions: 0, NumServices: 1, }, GoTypes: file_app_instman_command_command_proto_goTypes, DependencyIndexes: file_app_instman_command_command_proto_depIdxs, MessageInfos: file_app_instman_command_command_proto_msgTypes, }.Build() File_app_instman_command_command_proto = out.File file_app_instman_command_command_proto_goTypes = nil file_app_instman_command_command_proto_depIdxs = nil } ================================================ FILE: app/instman/command/command.proto ================================================ syntax = "proto3"; package v2ray.core.app.instman.command; option csharp_namespace = "V2Ray.Core.App.Instman.Command"; option go_package = "github.com/v2fly/v2ray-core/v5/app/instman/command"; option java_package = "com.v2ray.core.app.observatory.instman"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message ListInstanceReq{} message ListInstanceResp{ repeated string name = 1; } message AddInstanceReq{ string name = 1; string configType = 2; string configContentB64 = 3; } message AddInstanceResp{} message StartInstanceReq{ string name = 1; } message StartInstanceResp{ } service InstanceManagementService { rpc ListInstance(ListInstanceReq) returns (ListInstanceResp); rpc AddInstance(AddInstanceReq) returns (AddInstanceResp); rpc StartInstance(StartInstanceReq) returns (StartInstanceResp); } message Config { option (v2ray.core.common.protoext.message_opt).type = "grpcservice"; option (v2ray.core.common.protoext.message_opt).short_name = "instman"; } ================================================ FILE: app/instman/command/command_grpc.pb.go ================================================ package command import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( InstanceManagementService_ListInstance_FullMethodName = "/v2ray.core.app.instman.command.InstanceManagementService/ListInstance" InstanceManagementService_AddInstance_FullMethodName = "/v2ray.core.app.instman.command.InstanceManagementService/AddInstance" InstanceManagementService_StartInstance_FullMethodName = "/v2ray.core.app.instman.command.InstanceManagementService/StartInstance" ) // InstanceManagementServiceClient is the client API for InstanceManagementService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type InstanceManagementServiceClient interface { ListInstance(ctx context.Context, in *ListInstanceReq, opts ...grpc.CallOption) (*ListInstanceResp, error) AddInstance(ctx context.Context, in *AddInstanceReq, opts ...grpc.CallOption) (*AddInstanceResp, error) StartInstance(ctx context.Context, in *StartInstanceReq, opts ...grpc.CallOption) (*StartInstanceResp, error) } type instanceManagementServiceClient struct { cc grpc.ClientConnInterface } func NewInstanceManagementServiceClient(cc grpc.ClientConnInterface) InstanceManagementServiceClient { return &instanceManagementServiceClient{cc} } func (c *instanceManagementServiceClient) ListInstance(ctx context.Context, in *ListInstanceReq, opts ...grpc.CallOption) (*ListInstanceResp, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListInstanceResp) err := c.cc.Invoke(ctx, InstanceManagementService_ListInstance_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *instanceManagementServiceClient) AddInstance(ctx context.Context, in *AddInstanceReq, opts ...grpc.CallOption) (*AddInstanceResp, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AddInstanceResp) err := c.cc.Invoke(ctx, InstanceManagementService_AddInstance_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *instanceManagementServiceClient) StartInstance(ctx context.Context, in *StartInstanceReq, opts ...grpc.CallOption) (*StartInstanceResp, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(StartInstanceResp) err := c.cc.Invoke(ctx, InstanceManagementService_StartInstance_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // InstanceManagementServiceServer is the server API for InstanceManagementService service. // All implementations must embed UnimplementedInstanceManagementServiceServer // for forward compatibility. type InstanceManagementServiceServer interface { ListInstance(context.Context, *ListInstanceReq) (*ListInstanceResp, error) AddInstance(context.Context, *AddInstanceReq) (*AddInstanceResp, error) StartInstance(context.Context, *StartInstanceReq) (*StartInstanceResp, error) mustEmbedUnimplementedInstanceManagementServiceServer() } // UnimplementedInstanceManagementServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedInstanceManagementServiceServer struct{} func (UnimplementedInstanceManagementServiceServer) ListInstance(context.Context, *ListInstanceReq) (*ListInstanceResp, error) { return nil, status.Errorf(codes.Unimplemented, "method ListInstance not implemented") } func (UnimplementedInstanceManagementServiceServer) AddInstance(context.Context, *AddInstanceReq) (*AddInstanceResp, error) { return nil, status.Errorf(codes.Unimplemented, "method AddInstance not implemented") } func (UnimplementedInstanceManagementServiceServer) StartInstance(context.Context, *StartInstanceReq) (*StartInstanceResp, error) { return nil, status.Errorf(codes.Unimplemented, "method StartInstance not implemented") } func (UnimplementedInstanceManagementServiceServer) mustEmbedUnimplementedInstanceManagementServiceServer() { } func (UnimplementedInstanceManagementServiceServer) testEmbeddedByValue() {} // UnsafeInstanceManagementServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to InstanceManagementServiceServer will // result in compilation errors. type UnsafeInstanceManagementServiceServer interface { mustEmbedUnimplementedInstanceManagementServiceServer() } func RegisterInstanceManagementServiceServer(s grpc.ServiceRegistrar, srv InstanceManagementServiceServer) { // If the following call pancis, it indicates UnimplementedInstanceManagementServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&InstanceManagementService_ServiceDesc, srv) } func _InstanceManagementService_ListInstance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListInstanceReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(InstanceManagementServiceServer).ListInstance(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: InstanceManagementService_ListInstance_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(InstanceManagementServiceServer).ListInstance(ctx, req.(*ListInstanceReq)) } return interceptor(ctx, in, info, handler) } func _InstanceManagementService_AddInstance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AddInstanceReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(InstanceManagementServiceServer).AddInstance(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: InstanceManagementService_AddInstance_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(InstanceManagementServiceServer).AddInstance(ctx, req.(*AddInstanceReq)) } return interceptor(ctx, in, info, handler) } func _InstanceManagementService_StartInstance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(StartInstanceReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(InstanceManagementServiceServer).StartInstance(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: InstanceManagementService_StartInstance_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(InstanceManagementServiceServer).StartInstance(ctx, req.(*StartInstanceReq)) } return interceptor(ctx, in, info, handler) } // InstanceManagementService_ServiceDesc is the grpc.ServiceDesc for InstanceManagementService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var InstanceManagementService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "v2ray.core.app.instman.command.InstanceManagementService", HandlerType: (*InstanceManagementServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "ListInstance", Handler: _InstanceManagementService_ListInstance_Handler, }, { MethodName: "AddInstance", Handler: _InstanceManagementService_AddInstance_Handler, }, { MethodName: "StartInstance", Handler: _InstanceManagementService_StartInstance_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "app/instman/command/command.proto", } ================================================ FILE: app/instman/config.pb.go ================================================ package instman import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_instman_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_instman_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_instman_config_proto_rawDescGZIP(), []int{0} } var File_app_instman_config_proto protoreflect.FileDescriptor const file_app_instman_config_proto_rawDesc = "" + "\n" + "\x18app/instman/config.proto\x12\x16v2ray.core.app.instman\x1a common/protoext/extensions.proto\" \n" + "\x06Config:\x16\x82\xb5\x18\x12\n" + "\aservice\x12\ainstmanBc\n" + "\x1acom.v2ray.core.app.instmanP\x01Z*github.com/v2fly/v2ray-core/v5/app/instman\xaa\x02\x16V2Ray.Core.App.Instmanb\x06proto3" var ( file_app_instman_config_proto_rawDescOnce sync.Once file_app_instman_config_proto_rawDescData []byte ) func file_app_instman_config_proto_rawDescGZIP() []byte { file_app_instman_config_proto_rawDescOnce.Do(func() { file_app_instman_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_instman_config_proto_rawDesc), len(file_app_instman_config_proto_rawDesc))) }) return file_app_instman_config_proto_rawDescData } var file_app_instman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_app_instman_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.instman.Config } var file_app_instman_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_app_instman_config_proto_init() } func file_app_instman_config_proto_init() { if File_app_instman_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_instman_config_proto_rawDesc), len(file_app_instman_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_instman_config_proto_goTypes, DependencyIndexes: file_app_instman_config_proto_depIdxs, MessageInfos: file_app_instman_config_proto_msgTypes, }.Build() File_app_instman_config_proto = out.File file_app_instman_config_proto_goTypes = nil file_app_instman_config_proto_depIdxs = nil } ================================================ FILE: app/instman/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.instman; option csharp_namespace = "V2Ray.Core.App.Instman"; option go_package = "github.com/v2fly/v2ray-core/v5/app/instman"; option java_package = "com.v2ray.core.app.instman"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "instman"; } ================================================ FILE: app/instman/errors.generated.go ================================================ package instman import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/instman/instman.go ================================================ package instman import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/extension" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type InstanceMgr struct { config *Config // nolint: structcheck instances map[string]*core.Instance } func (i InstanceMgr) Type() interface{} { return extension.InstanceManagementType() } func (i InstanceMgr) Start() error { return nil } func (i InstanceMgr) Close() error { return nil } func (i InstanceMgr) ListInstance(ctx context.Context) ([]string, error) { var instanceNames []string for k := range i.instances { instanceNames = append(instanceNames, k) } return instanceNames, nil } func (i InstanceMgr) AddInstance(ctx context.Context, name string, config []byte, configType string) error { coreConfig, err := core.LoadConfig(configType, config) if err != nil { return newError("unable to load config").Base(err) } instance, err := core.New(coreConfig) if err != nil { return newError("unable to create instance").Base(err) } i.instances[name] = instance return nil } func (i InstanceMgr) StartInstance(ctx context.Context, name string) error { err := i.instances[name].Start() if err != nil { return newError("failed to start instance").Base(err) } return nil } func (i InstanceMgr) StopInstance(ctx context.Context, name string) error { err := i.instances[name].Close() if err != nil { return newError("failed to stop instance").Base(err) } return nil } func (i InstanceMgr) UntrackInstance(ctx context.Context, name string) error { delete(i.instances, name) return nil } func NewInstanceMgr(ctx context.Context, config *Config) (extension.InstanceManagement, error) { return InstanceMgr{instances: map[string]*core.Instance{}}, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { var f extension.InstanceManagement var err error if f, err = NewInstanceMgr(ctx, config.(*Config)); err != nil { return nil, err } return f, nil })) } ================================================ FILE: app/log/command/command.go ================================================ package command //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" grpc "google.golang.org/grpc" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/common" cmlog "github.com/v2fly/v2ray-core/v5/common/log" ) // LoggerServer is the implemention of LoggerService type LoggerServer struct { V *core.Instance } // RestartLogger implements LoggerService. func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) { logger := s.V.GetFeature((*log.Instance)(nil)) if logger == nil { return nil, newError("unable to get logger instance") } if err := logger.Close(); err != nil { return nil, newError("failed to close logger").Base(err) } if err := logger.Start(); err != nil { return nil, newError("failed to start logger").Base(err) } return &RestartLoggerResponse{}, nil } // FollowLog implements LoggerService. func (s *LoggerServer) FollowLog(_ *FollowLogRequest, stream LoggerService_FollowLogServer) error { logger := s.V.GetFeature((*log.Instance)(nil)) if logger == nil { return newError("unable to get logger instance") } follower, ok := logger.(cmlog.Follower) if !ok { return newError("logger not support following") } ctx, cancel := context.WithCancel(stream.Context()) defer cancel() f := func(msg cmlog.Message) { err := stream.Send(&FollowLogResponse{ Message: msg.String(), }) if err != nil { cancel() } } follower.AddFollower(f) defer follower.RemoveFollower(f) <-ctx.Done() return nil } func (s *LoggerServer) mustEmbedUnimplementedLoggerServiceServer() {} type service struct { v *core.Instance } func (s *service) Register(server *grpc.Server) { RegisterLoggerServiceServer(server, &LoggerServer{ V: s.v, }) } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { s := core.MustFromContext(ctx) return &service{v: s}, nil })) } ================================================ FILE: app/log/command/command_test.go ================================================ package command_test import ( "context" "testing" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/log" . "github.com/v2fly/v2ray-core/v5/app/log/command" "github.com/v2fly/v2ray-core/v5/app/proxyman" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/inbound" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/serial" ) func TestLoggerRestart(t *testing.T) { v, err := core.New(&core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{}), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, }) common.Must(err) common.Must(v.Start()) server := &LoggerServer{ V: v, } common.Must2(server.RestartLogger(context.Background(), &RestartLoggerRequest{})) } ================================================ FILE: app/log/command/config.pb.go ================================================ package command import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_log_command_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_log_command_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_log_command_config_proto_rawDescGZIP(), []int{0} } type RestartLoggerRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RestartLoggerRequest) Reset() { *x = RestartLoggerRequest{} mi := &file_app_log_command_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RestartLoggerRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RestartLoggerRequest) ProtoMessage() {} func (x *RestartLoggerRequest) ProtoReflect() protoreflect.Message { mi := &file_app_log_command_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RestartLoggerRequest.ProtoReflect.Descriptor instead. func (*RestartLoggerRequest) Descriptor() ([]byte, []int) { return file_app_log_command_config_proto_rawDescGZIP(), []int{1} } type RestartLoggerResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RestartLoggerResponse) Reset() { *x = RestartLoggerResponse{} mi := &file_app_log_command_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RestartLoggerResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*RestartLoggerResponse) ProtoMessage() {} func (x *RestartLoggerResponse) ProtoReflect() protoreflect.Message { mi := &file_app_log_command_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RestartLoggerResponse.ProtoReflect.Descriptor instead. func (*RestartLoggerResponse) Descriptor() ([]byte, []int) { return file_app_log_command_config_proto_rawDescGZIP(), []int{2} } type FollowLogRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FollowLogRequest) Reset() { *x = FollowLogRequest{} mi := &file_app_log_command_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FollowLogRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*FollowLogRequest) ProtoMessage() {} func (x *FollowLogRequest) ProtoReflect() protoreflect.Message { mi := &file_app_log_command_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FollowLogRequest.ProtoReflect.Descriptor instead. func (*FollowLogRequest) Descriptor() ([]byte, []int) { return file_app_log_command_config_proto_rawDescGZIP(), []int{3} } type FollowLogResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FollowLogResponse) Reset() { *x = FollowLogResponse{} mi := &file_app_log_command_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FollowLogResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*FollowLogResponse) ProtoMessage() {} func (x *FollowLogResponse) ProtoReflect() protoreflect.Message { mi := &file_app_log_command_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FollowLogResponse.ProtoReflect.Descriptor instead. func (*FollowLogResponse) Descriptor() ([]byte, []int) { return file_app_log_command_config_proto_rawDescGZIP(), []int{4} } func (x *FollowLogResponse) GetMessage() string { if x != nil { return x.Message } return "" } var File_app_log_command_config_proto protoreflect.FileDescriptor const file_app_log_command_config_proto_rawDesc = "" + "\n" + "\x1capp/log/command/config.proto\x12\x1av2ray.core.app.log.command\"\b\n" + "\x06Config\"\x16\n" + "\x14RestartLoggerRequest\"\x17\n" + "\x15RestartLoggerResponse\"\x12\n" + "\x10FollowLogRequest\"-\n" + "\x11FollowLogResponse\x12\x18\n" + "\amessage\x18\x01 \x01(\tR\amessage2\xf5\x01\n" + "\rLoggerService\x12v\n" + "\rRestartLogger\x120.v2ray.core.app.log.command.RestartLoggerRequest\x1a1.v2ray.core.app.log.command.RestartLoggerResponse\"\x00\x12l\n" + "\tFollowLog\x12,.v2ray.core.app.log.command.FollowLogRequest\x1a-.v2ray.core.app.log.command.FollowLogResponse\"\x000\x01Bo\n" + "\x1ecom.v2ray.core.app.log.commandP\x01Z.github.com/v2fly/v2ray-core/v5/app/log/command\xaa\x02\x1aV2Ray.Core.App.Log.Commandb\x06proto3" var ( file_app_log_command_config_proto_rawDescOnce sync.Once file_app_log_command_config_proto_rawDescData []byte ) func file_app_log_command_config_proto_rawDescGZIP() []byte { file_app_log_command_config_proto_rawDescOnce.Do(func() { file_app_log_command_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_log_command_config_proto_rawDesc), len(file_app_log_command_config_proto_rawDesc))) }) return file_app_log_command_config_proto_rawDescData } var file_app_log_command_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_app_log_command_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.log.command.Config (*RestartLoggerRequest)(nil), // 1: v2ray.core.app.log.command.RestartLoggerRequest (*RestartLoggerResponse)(nil), // 2: v2ray.core.app.log.command.RestartLoggerResponse (*FollowLogRequest)(nil), // 3: v2ray.core.app.log.command.FollowLogRequest (*FollowLogResponse)(nil), // 4: v2ray.core.app.log.command.FollowLogResponse } var file_app_log_command_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.app.log.command.LoggerService.RestartLogger:input_type -> v2ray.core.app.log.command.RestartLoggerRequest 3, // 1: v2ray.core.app.log.command.LoggerService.FollowLog:input_type -> v2ray.core.app.log.command.FollowLogRequest 2, // 2: v2ray.core.app.log.command.LoggerService.RestartLogger:output_type -> v2ray.core.app.log.command.RestartLoggerResponse 4, // 3: v2ray.core.app.log.command.LoggerService.FollowLog:output_type -> v2ray.core.app.log.command.FollowLogResponse 2, // [2:4] is the sub-list for method output_type 0, // [0:2] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_app_log_command_config_proto_init() } func file_app_log_command_config_proto_init() { if File_app_log_command_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_log_command_config_proto_rawDesc), len(file_app_log_command_config_proto_rawDesc)), NumEnums: 0, NumMessages: 5, NumExtensions: 0, NumServices: 1, }, GoTypes: file_app_log_command_config_proto_goTypes, DependencyIndexes: file_app_log_command_config_proto_depIdxs, MessageInfos: file_app_log_command_config_proto_msgTypes, }.Build() File_app_log_command_config_proto = out.File file_app_log_command_config_proto_goTypes = nil file_app_log_command_config_proto_depIdxs = nil } ================================================ FILE: app/log/command/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.log.command; option csharp_namespace = "V2Ray.Core.App.Log.Command"; option go_package = "github.com/v2fly/v2ray-core/v5/app/log/command"; option java_package = "com.v2ray.core.app.log.command"; option java_multiple_files = true; message Config {} message RestartLoggerRequest {} message RestartLoggerResponse {} message FollowLogRequest {} message FollowLogResponse { string message = 1; } service LoggerService { rpc RestartLogger(RestartLoggerRequest) returns (RestartLoggerResponse) {} //Unstable interface rpc FollowLog(FollowLogRequest) returns (stream FollowLogResponse) {}; } ================================================ FILE: app/log/command/config_grpc.pb.go ================================================ package command import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( LoggerService_RestartLogger_FullMethodName = "/v2ray.core.app.log.command.LoggerService/RestartLogger" LoggerService_FollowLog_FullMethodName = "/v2ray.core.app.log.command.LoggerService/FollowLog" ) // LoggerServiceClient is the client API for LoggerService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type LoggerServiceClient interface { RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) // Unstable interface FollowLog(ctx context.Context, in *FollowLogRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FollowLogResponse], error) } type loggerServiceClient struct { cc grpc.ClientConnInterface } func NewLoggerServiceClient(cc grpc.ClientConnInterface) LoggerServiceClient { return &loggerServiceClient{cc} } func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RestartLoggerResponse) err := c.cc.Invoke(ctx, LoggerService_RestartLogger_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *loggerServiceClient) FollowLog(ctx context.Context, in *FollowLogRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FollowLogResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &LoggerService_ServiceDesc.Streams[0], LoggerService_FollowLog_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[FollowLogRequest, FollowLogResponse]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type LoggerService_FollowLogClient = grpc.ServerStreamingClient[FollowLogResponse] // LoggerServiceServer is the server API for LoggerService service. // All implementations must embed UnimplementedLoggerServiceServer // for forward compatibility. type LoggerServiceServer interface { RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) // Unstable interface FollowLog(*FollowLogRequest, grpc.ServerStreamingServer[FollowLogResponse]) error mustEmbedUnimplementedLoggerServiceServer() } // UnimplementedLoggerServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedLoggerServiceServer struct{} func (UnimplementedLoggerServiceServer) RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) { return nil, status.Error(codes.Unimplemented, "method RestartLogger not implemented") } func (UnimplementedLoggerServiceServer) FollowLog(*FollowLogRequest, grpc.ServerStreamingServer[FollowLogResponse]) error { return status.Error(codes.Unimplemented, "method FollowLog not implemented") } func (UnimplementedLoggerServiceServer) mustEmbedUnimplementedLoggerServiceServer() {} func (UnimplementedLoggerServiceServer) testEmbeddedByValue() {} // UnsafeLoggerServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to LoggerServiceServer will // result in compilation errors. type UnsafeLoggerServiceServer interface { mustEmbedUnimplementedLoggerServiceServer() } func RegisterLoggerServiceServer(s grpc.ServiceRegistrar, srv LoggerServiceServer) { // If the following call panics, it indicates UnimplementedLoggerServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&LoggerService_ServiceDesc, srv) } func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RestartLoggerRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(LoggerServiceServer).RestartLogger(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: LoggerService_RestartLogger_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest)) } return interceptor(ctx, in, info, handler) } func _LoggerService_FollowLog_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(FollowLogRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(LoggerServiceServer).FollowLog(m, &grpc.GenericServerStream[FollowLogRequest, FollowLogResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type LoggerService_FollowLogServer = grpc.ServerStreamingServer[FollowLogResponse] // LoggerService_ServiceDesc is the grpc.ServiceDesc for LoggerService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var LoggerService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "v2ray.core.app.log.command.LoggerService", HandlerType: (*LoggerServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "RestartLogger", Handler: _LoggerService_RestartLogger_Handler, }, }, Streams: []grpc.StreamDesc{ { StreamName: "FollowLog", Handler: _LoggerService_FollowLog_Handler, ServerStreams: true, }, }, Metadata: "app/log/command/config.proto", } ================================================ FILE: app/log/command/errors.generated.go ================================================ package command import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/log/config.pb.go ================================================ package log import ( log "github.com/v2fly/v2ray-core/v5/common/log" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type LogType int32 const ( LogType_None LogType = 0 LogType_Console LogType = 1 LogType_File LogType = 2 LogType_Event LogType = 3 ) // Enum value maps for LogType. var ( LogType_name = map[int32]string{ 0: "None", 1: "Console", 2: "File", 3: "Event", } LogType_value = map[string]int32{ "None": 0, "Console": 1, "File": 2, "Event": 3, } ) func (x LogType) Enum() *LogType { p := new(LogType) *p = x return p } func (x LogType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (LogType) Descriptor() protoreflect.EnumDescriptor { return file_app_log_config_proto_enumTypes[0].Descriptor() } func (LogType) Type() protoreflect.EnumType { return &file_app_log_config_proto_enumTypes[0] } func (x LogType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use LogType.Descriptor instead. func (LogType) EnumDescriptor() ([]byte, []int) { return file_app_log_config_proto_rawDescGZIP(), []int{0} } type LogSpecification struct { state protoimpl.MessageState `protogen:"open.v1"` Type LogType `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.log.LogType" json:"type,omitempty"` Level log.Severity `protobuf:"varint,2,opt,name=level,proto3,enum=v2ray.core.common.log.Severity" json:"level,omitempty"` Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LogSpecification) Reset() { *x = LogSpecification{} mi := &file_app_log_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LogSpecification) String() string { return protoimpl.X.MessageStringOf(x) } func (*LogSpecification) ProtoMessage() {} func (x *LogSpecification) ProtoReflect() protoreflect.Message { mi := &file_app_log_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LogSpecification.ProtoReflect.Descriptor instead. func (*LogSpecification) Descriptor() ([]byte, []int) { return file_app_log_config_proto_rawDescGZIP(), []int{0} } func (x *LogSpecification) GetType() LogType { if x != nil { return x.Type } return LogType_None } func (x *LogSpecification) GetLevel() log.Severity { if x != nil { return x.Level } return log.Severity(0) } func (x *LogSpecification) GetPath() string { if x != nil { return x.Path } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Error *LogSpecification `protobuf:"bytes,6,opt,name=error,proto3" json:"error,omitempty"` Access *LogSpecification `protobuf:"bytes,7,opt,name=access,proto3" json:"access,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_log_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_log_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_log_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetError() *LogSpecification { if x != nil { return x.Error } return nil } func (x *Config) GetAccess() *LogSpecification { if x != nil { return x.Access } return nil } var File_app_log_config_proto protoreflect.FileDescriptor const file_app_log_config_proto_rawDesc = "" + "\n" + "\x14app/log/config.proto\x12\x12v2ray.core.app.log\x1a\x14common/log/log.proto\x1a common/protoext/extensions.proto\"\x8e\x01\n" + "\x10LogSpecification\x12/\n" + "\x04type\x18\x01 \x01(\x0e2\x1b.v2ray.core.app.log.LogTypeR\x04type\x125\n" + "\x05level\x18\x02 \x01(\x0e2\x1f.v2ray.core.common.log.SeverityR\x05level\x12\x12\n" + "\x04path\x18\x03 \x01(\tR\x04path\"\xb4\x01\n" + "\x06Config\x12:\n" + "\x05error\x18\x06 \x01(\v2$.v2ray.core.app.log.LogSpecificationR\x05error\x12<\n" + "\x06access\x18\a \x01(\v2$.v2ray.core.app.log.LogSpecificationR\x06access:\x12\x82\xb5\x18\x0e\n" + "\aservice\x12\x03logJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03J\x04\b\x03\x10\x04J\x04\b\x04\x10\x05J\x04\b\x05\x10\x06*5\n" + "\aLogType\x12\b\n" + "\x04None\x10\x00\x12\v\n" + "\aConsole\x10\x01\x12\b\n" + "\x04File\x10\x02\x12\t\n" + "\x05Event\x10\x03BW\n" + "\x16com.v2ray.core.app.logP\x01Z&github.com/v2fly/v2ray-core/v5/app/log\xaa\x02\x12V2Ray.Core.App.Logb\x06proto3" var ( file_app_log_config_proto_rawDescOnce sync.Once file_app_log_config_proto_rawDescData []byte ) func file_app_log_config_proto_rawDescGZIP() []byte { file_app_log_config_proto_rawDescOnce.Do(func() { file_app_log_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_log_config_proto_rawDesc), len(file_app_log_config_proto_rawDesc))) }) return file_app_log_config_proto_rawDescData } var file_app_log_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_app_log_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_app_log_config_proto_goTypes = []any{ (LogType)(0), // 0: v2ray.core.app.log.LogType (*LogSpecification)(nil), // 1: v2ray.core.app.log.LogSpecification (*Config)(nil), // 2: v2ray.core.app.log.Config (log.Severity)(0), // 3: v2ray.core.common.log.Severity } var file_app_log_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.app.log.LogSpecification.type:type_name -> v2ray.core.app.log.LogType 3, // 1: v2ray.core.app.log.LogSpecification.level:type_name -> v2ray.core.common.log.Severity 1, // 2: v2ray.core.app.log.Config.error:type_name -> v2ray.core.app.log.LogSpecification 1, // 3: v2ray.core.app.log.Config.access:type_name -> v2ray.core.app.log.LogSpecification 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_app_log_config_proto_init() } func file_app_log_config_proto_init() { if File_app_log_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_log_config_proto_rawDesc), len(file_app_log_config_proto_rawDesc)), NumEnums: 1, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_log_config_proto_goTypes, DependencyIndexes: file_app_log_config_proto_depIdxs, EnumInfos: file_app_log_config_proto_enumTypes, MessageInfos: file_app_log_config_proto_msgTypes, }.Build() File_app_log_config_proto = out.File file_app_log_config_proto_goTypes = nil file_app_log_config_proto_depIdxs = nil } ================================================ FILE: app/log/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.log; option csharp_namespace = "V2Ray.Core.App.Log"; option go_package = "github.com/v2fly/v2ray-core/v5/app/log"; option java_package = "com.v2ray.core.app.log"; option java_multiple_files = true; import "common/log/log.proto"; import "common/protoext/extensions.proto"; enum LogType { None = 0; Console = 1; File = 2; Event = 3; } message LogSpecification { LogType type = 1; v2ray.core.common.log.Severity level = 2; string path = 3; } message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "log"; reserved 1,2,3,4,5; LogSpecification error = 6; LogSpecification access = 7; } ================================================ FILE: app/log/errors.generated.go ================================================ package log import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/log/log.go ================================================ package log //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "reflect" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/log" ) // Instance is a log.Handler that handles logs. type Instance struct { sync.RWMutex config *Config accessLogger log.Handler errorLogger log.Handler followers map[reflect.Value]func(msg log.Message) active bool } // New creates a new log.Instance based on the given config. func New(ctx context.Context, config *Config) (*Instance, error) { if config.Error == nil { config.Error = &LogSpecification{Type: LogType_Console, Level: log.Severity_Warning} } if config.Access == nil { config.Access = &LogSpecification{Type: LogType_None} } g := &Instance{ config: config, active: false, } log.RegisterHandler(g) // start logger instantly on inited // other modules would log during init if err := g.startInternal(); err != nil { return nil, err } newError("Logger started").AtDebug().WriteToLog() return g, nil } func (g *Instance) initAccessLogger() error { handler, err := createHandler(g.config.Access.Type, HandlerCreatorOptions{ Path: g.config.Access.Path, }) if err != nil { return err } g.accessLogger = handler return nil } func (g *Instance) initErrorLogger() error { handler, err := createHandler(g.config.Error.Type, HandlerCreatorOptions{ Path: g.config.Error.Path, }) if err != nil { return err } g.errorLogger = handler return nil } // Type implements common.HasType. func (*Instance) Type() interface{} { return (*Instance)(nil) } func (g *Instance) startInternal() error { g.Lock() defer g.Unlock() if g.active { return nil } g.active = true if err := g.initAccessLogger(); err != nil { return newError("failed to initialize access logger").Base(err).AtWarning() } if err := g.initErrorLogger(); err != nil { return newError("failed to initialize error logger").Base(err).AtWarning() } return nil } // Start implements common.Runnable.Start(). func (g *Instance) Start() error { return g.startInternal() } // AddFollower implements log.Follower. func (g *Instance) AddFollower(f func(msg log.Message)) { g.Lock() defer g.Unlock() if g.followers == nil { g.followers = make(map[reflect.Value]func(msg log.Message)) } g.followers[reflect.ValueOf(f)] = f } // RemoveFollower implements log.Follower. func (g *Instance) RemoveFollower(f func(msg log.Message)) { g.Lock() defer g.Unlock() delete(g.followers, reflect.ValueOf(f)) } // Handle implements log.Handler. func (g *Instance) Handle(msg log.Message) { g.RLock() defer g.RUnlock() if !g.active { return } for _, f := range g.followers { f(msg) } switch msg := msg.(type) { case *log.AccessMessage: if g.accessLogger != nil { g.accessLogger.Handle(msg) } case *log.GeneralMessage: if g.errorLogger != nil && msg.Severity <= g.config.Error.Level { g.errorLogger.Handle(msg) } default: // Swallow } } // Close implements common.Closable.Close(). func (g *Instance) Close() error { newError("Logger closing").AtDebug().WriteToLog() g.Lock() defer g.Unlock() if !g.active { return nil } g.active = false common.Close(g.accessLogger) g.accessLogger = nil common.Close(g.errorLogger) g.errorLogger = nil return nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) } ================================================ FILE: app/log/log_creator.go ================================================ package log import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/log" ) type HandlerCreatorOptions struct { Path string } type HandlerCreator func(LogType, HandlerCreatorOptions) (log.Handler, error) var handlerCreatorMap = make(map[LogType]HandlerCreator) func RegisterHandlerCreator(logType LogType, f HandlerCreator) error { if f == nil { return newError("nil HandlerCreator") } handlerCreatorMap[logType] = f return nil } func createHandler(logType LogType, options HandlerCreatorOptions) (log.Handler, error) { creator, found := handlerCreatorMap[logType] if !found { return nil, newError("unable to create log handler for ", logType) } return creator(logType, options) } func init() { common.Must(RegisterHandlerCreator(LogType_Console, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) { return log.NewLogger(log.CreateStdoutLogWriter()), nil })) common.Must(RegisterHandlerCreator(LogType_File, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) { creator, err := log.CreateFileLogWriter(options.Path) if err != nil { return nil, err } return log.NewLogger(creator), nil })) common.Must(RegisterHandlerCreator(LogType_None, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) { return nil, nil })) } ================================================ FILE: app/log/log_test.go ================================================ package log_test import ( "context" "testing" "github.com/golang/mock/gomock" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/testing/mocks" ) func TestCustomLogHandler(t *testing.T) { mockCtl := gomock.NewController(t) defer mockCtl.Finish() var loggedValue []string mockHandler := mocks.NewLogHandler(mockCtl) mockHandler.EXPECT().Handle(gomock.Any()).AnyTimes().DoAndReturn(func(msg clog.Message) { loggedValue = append(loggedValue, msg.String()) }) log.RegisterHandlerCreator(log.LogType_Console, func(lt log.LogType, options log.HandlerCreatorOptions) (clog.Handler, error) { return mockHandler, nil }) logger, err := log.New(context.Background(), &log.Config{ Error: &log.LogSpecification{Type: log.LogType_Console, Level: clog.Severity_Debug}, Access: &log.LogSpecification{Type: log.LogType_None}, }) common.Must(err) common.Must(logger.Start()) clog.Record(&clog.GeneralMessage{ Severity: clog.Severity_Debug, Content: "test", }) if len(loggedValue) < 2 { t.Fatal("expected 2 log messages, but actually ", loggedValue) } if loggedValue[1] != "[Debug] test" { t.Fatal("expected '[Debug] test', but actually ", loggedValue[1]) } common.Must(logger.Close()) } ================================================ FILE: app/observatory/burst/burst.go ================================================ package burst import ( "math" "time" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const ( rttFailed = time.Duration(math.MaxInt64 - iota) rttUntested // nolint: varcheck rttUnqualified // nolint: varcheck ) ================================================ FILE: app/observatory/burst/burstobserver.go ================================================ package burst import ( "context" "sync" "github.com/golang/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/observatory" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/features/extension" "github.com/v2fly/v2ray-core/v5/features/outbound" ) type Observer struct { config *Config ctx context.Context statusLock sync.Mutex // nolint: structcheck hp *HealthPing finished *done.Instance ohm outbound.Manager } func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) { return &observatory.ObservationResult{Status: o.createResult()}, nil } func (o *Observer) createResult() []*observatory.OutboundStatus { var result []*observatory.OutboundStatus o.hp.access.Lock() defer o.hp.access.Unlock() for name, value := range o.hp.Results { status := observatory.OutboundStatus{ Alive: value.getStatistics().All != value.getStatistics().Fail, Delay: value.getStatistics().Average.Milliseconds(), LastErrorReason: "", OutboundTag: name, LastSeenTime: 0, LastTryTime: 0, HealthPing: &observatory.HealthPingMeasurementResult{ All: int64(value.getStatistics().All), Fail: int64(value.getStatistics().Fail), Deviation: int64(value.getStatistics().Deviation), Average: int64(value.getStatistics().Average), Max: int64(value.getStatistics().Max), Min: int64(value.getStatistics().Min), }, } result = append(result, &status) } return result } func (o *Observer) Type() interface{} { return extension.ObservatoryType() } func (o *Observer) Start() error { if o.config != nil && len(o.config.SubjectSelector) != 0 { o.finished = done.New() o.hp.StartScheduler(func() ([]string, error) { hs, ok := o.ohm.(outbound.HandlerSelector) if !ok { return nil, newError("outbound.Manager is not a HandlerSelector") } outbounds := hs.Select(o.config.SubjectSelector) return outbounds, nil }) } return nil } func (o *Observer) Close() error { if o.finished != nil { o.hp.StopScheduler() return o.finished.Close() } return nil } func New(ctx context.Context, config *Config) (*Observer, error) { var outboundManager outbound.Manager err := core.RequireFeatures(ctx, func(om outbound.Manager) { outboundManager = om }) if err != nil { return nil, newError("Cannot get depended features").Base(err) } hp := NewHealthPing(ctx, config.PingConfig) return &Observer{ config: config, ctx: ctx, ohm: outboundManager, hp: hp, }, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) } ================================================ FILE: app/observatory/burst/config.pb.go ================================================ package burst import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // @Document The selectors for outbound under observation SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"` PingConfig *HealthPingConfig `protobuf:"bytes,3,opt,name=ping_config,json=pingConfig,proto3" json:"ping_config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_observatory_burst_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_burst_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_observatory_burst_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetSubjectSelector() []string { if x != nil { return x.SubjectSelector } return nil } func (x *Config) GetPingConfig() *HealthPingConfig { if x != nil { return x.PingConfig } return nil } type HealthPingConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // destination url, need 204 for success return // default https://connectivitycheck.gstatic.com/generate_204 Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` // connectivity check url Connectivity string `protobuf:"bytes,2,opt,name=connectivity,proto3" json:"connectivity,omitempty"` // health check interval, int64 values of time.Duration Interval int64 `protobuf:"varint,3,opt,name=interval,proto3" json:"interval,omitempty"` // sampling count is the amount of recent ping results which are kept for calculation SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"` // ping timeout, int64 values of time.Duration Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HealthPingConfig) Reset() { *x = HealthPingConfig{} mi := &file_app_observatory_burst_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HealthPingConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*HealthPingConfig) ProtoMessage() {} func (x *HealthPingConfig) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_burst_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HealthPingConfig.ProtoReflect.Descriptor instead. func (*HealthPingConfig) Descriptor() ([]byte, []int) { return file_app_observatory_burst_config_proto_rawDescGZIP(), []int{1} } func (x *HealthPingConfig) GetDestination() string { if x != nil { return x.Destination } return "" } func (x *HealthPingConfig) GetConnectivity() string { if x != nil { return x.Connectivity } return "" } func (x *HealthPingConfig) GetInterval() int64 { if x != nil { return x.Interval } return 0 } func (x *HealthPingConfig) GetSamplingCount() int32 { if x != nil { return x.SamplingCount } return 0 } func (x *HealthPingConfig) GetTimeout() int64 { if x != nil { return x.Timeout } return 0 } var File_app_observatory_burst_config_proto protoreflect.FileDescriptor const file_app_observatory_burst_config_proto_rawDesc = "" + "\n" + "\"app/observatory/burst/config.proto\x12 v2ray.core.app.observatory.burst\x1a common/protoext/extensions.proto\"\xa9\x01\n" + "\x06Config\x12)\n" + "\x10subject_selector\x18\x02 \x03(\tR\x0fsubjectSelector\x12S\n" + "\vping_config\x18\x03 \x01(\v22.v2ray.core.app.observatory.burst.HealthPingConfigR\n" + "pingConfig:\x1f\x82\xb5\x18\x1b\n" + "\aservice\x12\x10burstObservatory\"\xb4\x01\n" + "\x10HealthPingConfig\x12 \n" + "\vdestination\x18\x01 \x01(\tR\vdestination\x12\"\n" + "\fconnectivity\x18\x02 \x01(\tR\fconnectivity\x12\x1a\n" + "\binterval\x18\x03 \x01(\x03R\binterval\x12$\n" + "\rsamplingCount\x18\x04 \x01(\x05R\rsamplingCount\x12\x18\n" + "\atimeout\x18\x05 \x01(\x03R\atimeoutB\x81\x01\n" + "$com.v2ray.core.app.observatory.burstP\x01Z4github.com/v2fly/v2ray-core/v5/app/observatory/burst\xaa\x02 V2Ray.Core.App.Observatory.Burstb\x06proto3" var ( file_app_observatory_burst_config_proto_rawDescOnce sync.Once file_app_observatory_burst_config_proto_rawDescData []byte ) func file_app_observatory_burst_config_proto_rawDescGZIP() []byte { file_app_observatory_burst_config_proto_rawDescOnce.Do(func() { file_app_observatory_burst_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_burst_config_proto_rawDesc), len(file_app_observatory_burst_config_proto_rawDesc))) }) return file_app_observatory_burst_config_proto_rawDescData } var file_app_observatory_burst_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_app_observatory_burst_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.observatory.burst.Config (*HealthPingConfig)(nil), // 1: v2ray.core.app.observatory.burst.HealthPingConfig } var file_app_observatory_burst_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.app.observatory.burst.Config.ping_config:type_name -> v2ray.core.app.observatory.burst.HealthPingConfig 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_app_observatory_burst_config_proto_init() } func file_app_observatory_burst_config_proto_init() { if File_app_observatory_burst_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_burst_config_proto_rawDesc), len(file_app_observatory_burst_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_observatory_burst_config_proto_goTypes, DependencyIndexes: file_app_observatory_burst_config_proto_depIdxs, MessageInfos: file_app_observatory_burst_config_proto_msgTypes, }.Build() File_app_observatory_burst_config_proto = out.File file_app_observatory_burst_config_proto_goTypes = nil file_app_observatory_burst_config_proto_depIdxs = nil } ================================================ FILE: app/observatory/burst/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.observatory.burst; option csharp_namespace = "V2Ray.Core.App.Observatory.Burst"; option go_package = "github.com/v2fly/v2ray-core/v5/app/observatory/burst"; option java_package = "com.v2ray.core.app.observatory.burst"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "burstObservatory"; /* @Document The selectors for outbound under observation */ repeated string subject_selector = 2; HealthPingConfig ping_config = 3; } message HealthPingConfig { // destination url, need 204 for success return // default https://connectivitycheck.gstatic.com/generate_204 string destination = 1; // connectivity check url string connectivity = 2; // health check interval, int64 values of time.Duration int64 interval = 3; // sampling count is the amount of recent ping results which are kept for calculation int32 samplingCount = 4; // ping timeout, int64 values of time.Duration int64 timeout = 5; } ================================================ FILE: app/observatory/burst/errors.generated.go ================================================ package burst import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/observatory/burst/healthping.go ================================================ package burst import ( "context" "fmt" "strings" "sync" "time" "github.com/v2fly/v2ray-core/v5/common/dice" ) // HealthPingSettings holds settings for health Checker type HealthPingSettings struct { Destination string `json:"destination"` Connectivity string `json:"connectivity"` Interval time.Duration `json:"interval"` SamplingCount int `json:"sampling"` Timeout time.Duration `json:"timeout"` } // HealthPing is the health checker for balancers type HealthPing struct { ctx context.Context access sync.Mutex ticker *time.Ticker tickerClose chan struct{} Settings *HealthPingSettings Results map[string]*HealthPingRTTS } // NewHealthPing creates a new HealthPing with settings func NewHealthPing(ctx context.Context, config *HealthPingConfig) *HealthPing { settings := &HealthPingSettings{} if config != nil { settings = &HealthPingSettings{ Connectivity: strings.TrimSpace(config.Connectivity), Destination: strings.TrimSpace(config.Destination), Interval: time.Duration(config.Interval), SamplingCount: int(config.SamplingCount), Timeout: time.Duration(config.Timeout), } } if settings.Destination == "" { // Destination URL, need 204 for success return default to chromium // https://github.com/chromium/chromium/blob/main/components/safety_check/url_constants.cc#L10 // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/safety_check/url_constants.cc#10 settings.Destination = "https://connectivitycheck.gstatic.com/generate_204" } if settings.Interval == 0 { settings.Interval = time.Duration(1) * time.Minute } else if settings.Interval < 10 { newError("health check interval is too small, 10s is applied").AtWarning().WriteToLog() settings.Interval = time.Duration(10) * time.Second } if settings.SamplingCount <= 0 { settings.SamplingCount = 10 } if settings.Timeout <= 0 { // results are saved after all health pings finish, // a larger timeout could possibly makes checks run longer settings.Timeout = time.Duration(5) * time.Second } return &HealthPing{ ctx: ctx, Settings: settings, Results: nil, } } // StartScheduler implements the HealthChecker func (h *HealthPing) StartScheduler(selector func() ([]string, error)) { if h.ticker != nil { return } interval := h.Settings.Interval * time.Duration(h.Settings.SamplingCount) ticker := time.NewTicker(interval) tickerClose := make(chan struct{}) h.ticker = ticker h.tickerClose = tickerClose go func() { for { go func() { tags, err := selector() if err != nil { newError("error select outbounds for scheduled health check: ", err).AtWarning().WriteToLog() return } h.doCheck(tags, interval, h.Settings.SamplingCount) h.Cleanup(tags) }() select { case <-ticker.C: continue case <-tickerClose: return } } }() } // StopScheduler implements the HealthChecker func (h *HealthPing) StopScheduler() { if h.ticker == nil { return } h.ticker.Stop() h.ticker = nil close(h.tickerClose) h.tickerClose = nil } // Check implements the HealthChecker func (h *HealthPing) Check(tags []string) error { if len(tags) == 0 { return nil } newError("perform one-time health check for tags ", tags).AtInfo().WriteToLog() h.doCheck(tags, 0, 1) return nil } type rtt struct { handler string value time.Duration } // doCheck performs the 'rounds' amount checks in given 'duration'. You should make // sure all tags are valid for current balancer func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int) { count := len(tags) * rounds if count == 0 { return } ch := make(chan *rtt, count) for _, tag := range tags { handler := tag client := newPingClient( h.ctx, h.Settings.Destination, h.Settings.Timeout, handler, ) for i := 0; i < rounds; i++ { delay := time.Duration(0) if duration > 0 { delay = time.Duration(dice.Roll(int(duration))) } time.AfterFunc(delay, func() { newError("checking ", handler).AtDebug().WriteToLog() delay, err := client.MeasureDelay() if err == nil { ch <- &rtt{ handler: handler, value: delay, } return } if !h.checkConnectivity() { newError("network is down").AtWarning().WriteToLog() ch <- &rtt{ handler: handler, value: 0, } return } newError(fmt.Sprintf( "error ping %s with %s: %s", h.Settings.Destination, handler, err, )).AtWarning().WriteToLog() ch <- &rtt{ handler: handler, value: rttFailed, } }) } } for i := 0; i < count; i++ { rtt := <-ch if rtt.value > 0 { // should not put results when network is down h.PutResult(rtt.handler, rtt.value) } } } // PutResult puts a ping rtt to results func (h *HealthPing) PutResult(tag string, rtt time.Duration) { h.access.Lock() defer h.access.Unlock() if h.Results == nil { h.Results = make(map[string]*HealthPingRTTS) } r, ok := h.Results[tag] if !ok { // validity is 2 times to sampling period, since the check are // distributed in the time line randomly, in extreme cases, // previous checks are distributed on the left, and latters // on the right validity := h.Settings.Interval * time.Duration(h.Settings.SamplingCount) * 2 r = NewHealthPingResult(h.Settings.SamplingCount, validity) h.Results[tag] = r } r.Put(rtt) } // Cleanup removes results of removed handlers, // tags should be all valid tags of the Balancer now func (h *HealthPing) Cleanup(tags []string) { h.access.Lock() defer h.access.Unlock() for tag := range h.Results { found := false for _, v := range tags { if tag == v { found = true break } } if !found { delete(h.Results, tag) } } } // checkConnectivity checks the network connectivity, it returns // true if network is good or "connectivity check url" not set func (h *HealthPing) checkConnectivity() bool { if h.Settings.Connectivity == "" { return true } tester := newDirectPingClient( h.Settings.Connectivity, h.Settings.Timeout, ) if _, err := tester.MeasureDelay(); err != nil { return false } return true } ================================================ FILE: app/observatory/burst/healthping_result.go ================================================ package burst import ( "math" "time" ) // HealthPingStats is the statistics of HealthPingRTTS type HealthPingStats struct { All int Fail int Deviation time.Duration Average time.Duration Max time.Duration Min time.Duration } // HealthPingRTTS holds ping rtts for health Checker type HealthPingRTTS struct { idx int cap int validity time.Duration rtts []*pingRTT lastUpdateAt time.Time stats *HealthPingStats } type pingRTT struct { time time.Time value time.Duration } // NewHealthPingResult returns a *HealthPingResult with specified capacity func NewHealthPingResult(cap int, validity time.Duration) *HealthPingRTTS { return &HealthPingRTTS{cap: cap, validity: validity} } // Get gets statistics of the HealthPingRTTS func (h *HealthPingRTTS) Get() *HealthPingStats { return h.getStatistics() } // GetWithCache get statistics and write cache for next call // Make sure use Mutex.Lock() before calling it, RWMutex.RLock() // is not an option since it writes cache func (h *HealthPingRTTS) GetWithCache() *HealthPingStats { lastPutAt := h.rtts[h.idx].time now := time.Now() if h.stats == nil || h.lastUpdateAt.Before(lastPutAt) || h.findOutdated(now) >= 0 { h.stats = h.getStatistics() h.lastUpdateAt = now } return h.stats } // Put puts a new rtt to the HealthPingResult func (h *HealthPingRTTS) Put(d time.Duration) { if h.rtts == nil { h.rtts = make([]*pingRTT, h.cap) for i := 0; i < h.cap; i++ { h.rtts[i] = &pingRTT{} } h.idx = -1 } h.idx = h.calcIndex(1) now := time.Now() h.rtts[h.idx].time = now h.rtts[h.idx].value = d } func (h *HealthPingRTTS) calcIndex(step int) int { idx := h.idx idx += step if idx >= h.cap { idx %= h.cap } return idx } func (h *HealthPingRTTS) getStatistics() *HealthPingStats { stats := &HealthPingStats{} stats.Fail = 0 stats.Max = 0 stats.Min = rttFailed sum := time.Duration(0) cnt := 0 validRTTs := make([]time.Duration, 0) for _, rtt := range h.rtts { switch { case rtt.value == 0 || time.Since(rtt.time) > h.validity: continue case rtt.value == rttFailed: stats.Fail++ continue } cnt++ sum += rtt.value validRTTs = append(validRTTs, rtt.value) if stats.Max < rtt.value { stats.Max = rtt.value } if stats.Min > rtt.value { stats.Min = rtt.value } } stats.All = cnt + stats.Fail if cnt == 0 { stats.Min = 0 return stats } stats.Average = time.Duration(int(sum) / cnt) var std float64 if cnt < 2 { // no enough data for standard deviation, we assume it's half of the average rtt // if we don't do this, standard deviation of 1 round tested nodes is 0, will always // selected before 2 or more rounds tested nodes std = float64(stats.Average / 2) } else { variance := float64(0) for _, rtt := range validRTTs { variance += math.Pow(float64(rtt-stats.Average), 2) } std = math.Sqrt(variance / float64(cnt)) } stats.Deviation = time.Duration(std) return stats } func (h *HealthPingRTTS) findOutdated(now time.Time) int { for i := h.cap - 1; i < 2*h.cap; i++ { // from oldest to latest idx := h.calcIndex(i) validity := h.rtts[idx].time.Add(h.validity) if h.lastUpdateAt.After(validity) { return idx } if validity.Before(now) { return idx } } return -1 } ================================================ FILE: app/observatory/burst/healthping_result_test.go ================================================ package burst_test import ( "math" reflect "reflect" "testing" "time" "github.com/v2fly/v2ray-core/v5/app/observatory/burst" ) func TestHealthPingResults(t *testing.T) { rtts := []int64{60, 140, 60, 140, 60, 60, 140, 60, 140} hr := burst.NewHealthPingResult(4, time.Hour) for _, rtt := range rtts { hr.Put(time.Duration(rtt)) } rttFailed := time.Duration(math.MaxInt64) expected := &burst.HealthPingStats{ All: 4, Fail: 0, Deviation: 40, Average: 100, Max: 140, Min: 60, } actual := hr.Get() if !reflect.DeepEqual(expected, actual) { t.Errorf("expected: %v, actual: %v", expected, actual) } hr.Put(rttFailed) hr.Put(rttFailed) expected.Fail = 2 actual = hr.Get() if !reflect.DeepEqual(expected, actual) { t.Errorf("failed half-failures test, expected: %v, actual: %v", expected, actual) } hr.Put(rttFailed) hr.Put(rttFailed) expected = &burst.HealthPingStats{ All: 4, Fail: 4, Deviation: 0, Average: 0, Max: 0, Min: 0, } actual = hr.Get() if !reflect.DeepEqual(expected, actual) { t.Errorf("failed all-failures test, expected: %v, actual: %v", expected, actual) } } func TestHealthPingResultsIgnoreOutdated(t *testing.T) { rtts := []int64{60, 140, 60, 140} hr := burst.NewHealthPingResult(4, time.Duration(10)*time.Millisecond) for i, rtt := range rtts { if i == 2 { // wait for previous 2 outdated time.Sleep(time.Duration(10) * time.Millisecond) } hr.Put(time.Duration(rtt)) } hr.Get() expected := &burst.HealthPingStats{ All: 2, Fail: 0, Deviation: 40, Average: 100, Max: 140, Min: 60, } actual := hr.Get() if !reflect.DeepEqual(expected, actual) { t.Errorf("failed 'half-outdated' test, expected: %v, actual: %v", expected, actual) } // wait for all outdated time.Sleep(time.Duration(10) * time.Millisecond) expected = &burst.HealthPingStats{ All: 0, Fail: 0, Deviation: 0, Average: 0, Max: 0, Min: 0, } actual = hr.Get() if !reflect.DeepEqual(expected, actual) { t.Errorf("failed 'outdated / not-tested' test, expected: %v, actual: %v", expected, actual) } hr.Put(time.Duration(60)) expected = &burst.HealthPingStats{ All: 1, Fail: 0, // 1 sample, std=0.5rtt Deviation: 30, Average: 60, Max: 60, Min: 60, } actual = hr.Get() if !reflect.DeepEqual(expected, actual) { t.Errorf("expected: %v, actual: %v", expected, actual) } } ================================================ FILE: app/observatory/burst/ping.go ================================================ package burst import ( "context" "net/http" "time" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet/tagged" ) type pingClient struct { destination string httpClient *http.Client } func newPingClient(ctx context.Context, destination string, timeout time.Duration, handler string) *pingClient { return &pingClient{ destination: destination, httpClient: newHTTPClient(ctx, handler, timeout), } } func newDirectPingClient(destination string, timeout time.Duration) *pingClient { return &pingClient{ destination: destination, httpClient: &http.Client{Timeout: timeout}, } } func newHTTPClient(ctxv context.Context, handler string, timeout time.Duration) *http.Client { tr := &http.Transport{ DisableKeepAlives: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { dest, err := net.ParseDestination(network + ":" + addr) if err != nil { return nil, err } return tagged.Dialer(ctxv, dest, handler) }, } return &http.Client{ Transport: tr, Timeout: timeout, // don't follow redirect CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } } // MeasureDelay returns the delay time of the request to dest func (s *pingClient) MeasureDelay() (time.Duration, error) { if s.httpClient == nil { panic("pingClient no initialized") } req, err := http.NewRequest(http.MethodHead, s.destination, nil) if err != nil { return rttFailed, err } start := time.Now() resp, err := s.httpClient.Do(req) if err != nil { return rttFailed, err } // don't wait for body resp.Body.Close() return time.Since(start), nil } ================================================ FILE: app/observatory/command/command.go ================================================ //go:build !confonly // +build !confonly package command //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "github.com/golang/protobuf/proto" "google.golang.org/grpc" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/observatory" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/extension" ) type service struct { UnimplementedObservatoryServiceServer v *core.Instance observatory extension.Observatory } func (s *service) GetOutboundStatus(ctx context.Context, request *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) { var result proto.Message if request.Tag == "" { observeResult, err := s.observatory.GetObservation(ctx) if err != nil { return nil, newError("cannot get observation").Base(err) } result = observeResult } else { if _, ok := s.observatory.(features.TaggedFeatures); !ok { return nil, newError("observatory does not support tagged features") } fet, err := s.observatory.(features.TaggedFeatures).GetFeaturesByTag(request.Tag) if err != nil { return nil, newError("cannot get tagged observatory").Base(err) } observeResult, err := fet.(extension.Observatory).GetObservation(ctx) if err != nil { return nil, newError("cannot get observation").Base(err) } result = observeResult } retdata := result.(*observatory.ObservationResult) return &GetOutboundStatusResponse{ Status: retdata, }, nil } func (s *service) Register(server *grpc.Server) { RegisterObservatoryServiceServer(server, s) } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { s := core.MustFromContext(ctx) sv := &service{v: s} err := s.RequireFeatures(func(Observatory extension.Observatory) { sv.observatory = Observatory }) if err != nil { return nil, err } return sv, nil })) } ================================================ FILE: app/observatory/command/command.pb.go ================================================ package command import ( observatory "github.com/v2fly/v2ray-core/v5/app/observatory" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type GetOutboundStatusRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=Tag,proto3" json:"Tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetOutboundStatusRequest) Reset() { *x = GetOutboundStatusRequest{} mi := &file_app_observatory_command_command_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetOutboundStatusRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetOutboundStatusRequest) ProtoMessage() {} func (x *GetOutboundStatusRequest) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_command_command_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetOutboundStatusRequest.ProtoReflect.Descriptor instead. func (*GetOutboundStatusRequest) Descriptor() ([]byte, []int) { return file_app_observatory_command_command_proto_rawDescGZIP(), []int{0} } func (x *GetOutboundStatusRequest) GetTag() string { if x != nil { return x.Tag } return "" } type GetOutboundStatusResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Status *observatory.ObservationResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetOutboundStatusResponse) Reset() { *x = GetOutboundStatusResponse{} mi := &file_app_observatory_command_command_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetOutboundStatusResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetOutboundStatusResponse) ProtoMessage() {} func (x *GetOutboundStatusResponse) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_command_command_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetOutboundStatusResponse.ProtoReflect.Descriptor instead. func (*GetOutboundStatusResponse) Descriptor() ([]byte, []int) { return file_app_observatory_command_command_proto_rawDescGZIP(), []int{1} } func (x *GetOutboundStatusResponse) GetStatus() *observatory.ObservationResult { if x != nil { return x.Status } return nil } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_observatory_command_command_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_command_command_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_observatory_command_command_proto_rawDescGZIP(), []int{2} } var File_app_observatory_command_command_proto protoreflect.FileDescriptor const file_app_observatory_command_command_proto_rawDesc = "" + "\n" + "%app/observatory/command/command.proto\x12\"v2ray.core.app.observatory.command\x1a common/protoext/extensions.proto\x1a\x1capp/observatory/config.proto\",\n" + "\x18GetOutboundStatusRequest\x12\x10\n" + "\x03Tag\x18\x01 \x01(\tR\x03Tag\"b\n" + "\x19GetOutboundStatusResponse\x12E\n" + "\x06status\x18\x01 \x01(\v2-.v2ray.core.app.observatory.ObservationResultR\x06status\"(\n" + "\x06Config:\x1e\x82\xb5\x18\x1a\n" + "\vgrpcservice\x12\vobservatory2\xa9\x01\n" + "\x12ObservatoryService\x12\x92\x01\n" + "\x11GetOutboundStatus\x12<.v2ray.core.app.observatory.command.GetOutboundStatusRequest\x1a=.v2ray.core.app.observatory.command.GetOutboundStatusResponse\"\x00B\x87\x01\n" + "&com.v2ray.core.app.observatory.commandP\x01Z6github.com/v2fly/v2ray-core/v5/app/observatory/command\xaa\x02\"V2Ray.Core.App.Observatory.Commandb\x06proto3" var ( file_app_observatory_command_command_proto_rawDescOnce sync.Once file_app_observatory_command_command_proto_rawDescData []byte ) func file_app_observatory_command_command_proto_rawDescGZIP() []byte { file_app_observatory_command_command_proto_rawDescOnce.Do(func() { file_app_observatory_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_command_command_proto_rawDesc), len(file_app_observatory_command_command_proto_rawDesc))) }) return file_app_observatory_command_command_proto_rawDescData } var file_app_observatory_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_app_observatory_command_command_proto_goTypes = []any{ (*GetOutboundStatusRequest)(nil), // 0: v2ray.core.app.observatory.command.GetOutboundStatusRequest (*GetOutboundStatusResponse)(nil), // 1: v2ray.core.app.observatory.command.GetOutboundStatusResponse (*Config)(nil), // 2: v2ray.core.app.observatory.command.Config (*observatory.ObservationResult)(nil), // 3: v2ray.core.app.observatory.ObservationResult } var file_app_observatory_command_command_proto_depIdxs = []int32{ 3, // 0: v2ray.core.app.observatory.command.GetOutboundStatusResponse.status:type_name -> v2ray.core.app.observatory.ObservationResult 0, // 1: v2ray.core.app.observatory.command.ObservatoryService.GetOutboundStatus:input_type -> v2ray.core.app.observatory.command.GetOutboundStatusRequest 1, // 2: v2ray.core.app.observatory.command.ObservatoryService.GetOutboundStatus:output_type -> v2ray.core.app.observatory.command.GetOutboundStatusResponse 2, // [2:3] is the sub-list for method output_type 1, // [1:2] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_app_observatory_command_command_proto_init() } func file_app_observatory_command_command_proto_init() { if File_app_observatory_command_command_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_command_command_proto_rawDesc), len(file_app_observatory_command_command_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 1, }, GoTypes: file_app_observatory_command_command_proto_goTypes, DependencyIndexes: file_app_observatory_command_command_proto_depIdxs, MessageInfos: file_app_observatory_command_command_proto_msgTypes, }.Build() File_app_observatory_command_command_proto = out.File file_app_observatory_command_command_proto_goTypes = nil file_app_observatory_command_command_proto_depIdxs = nil } ================================================ FILE: app/observatory/command/command.proto ================================================ syntax = "proto3"; package v2ray.core.app.observatory.command; option csharp_namespace = "V2Ray.Core.App.Observatory.Command"; option go_package = "github.com/v2fly/v2ray-core/v5/app/observatory/command"; option java_package = "com.v2ray.core.app.observatory.command"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "app/observatory/config.proto"; message GetOutboundStatusRequest { string Tag = 1; } message GetOutboundStatusResponse { v2ray.core.app.observatory.ObservationResult status = 1; } service ObservatoryService { rpc GetOutboundStatus(GetOutboundStatusRequest) returns (GetOutboundStatusResponse) {} } message Config { option (v2ray.core.common.protoext.message_opt).type = "grpcservice"; option (v2ray.core.common.protoext.message_opt).short_name = "observatory"; } ================================================ FILE: app/observatory/command/command_grpc.pb.go ================================================ package command import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( ObservatoryService_GetOutboundStatus_FullMethodName = "/v2ray.core.app.observatory.command.ObservatoryService/GetOutboundStatus" ) // ObservatoryServiceClient is the client API for ObservatoryService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type ObservatoryServiceClient interface { GetOutboundStatus(ctx context.Context, in *GetOutboundStatusRequest, opts ...grpc.CallOption) (*GetOutboundStatusResponse, error) } type observatoryServiceClient struct { cc grpc.ClientConnInterface } func NewObservatoryServiceClient(cc grpc.ClientConnInterface) ObservatoryServiceClient { return &observatoryServiceClient{cc} } func (c *observatoryServiceClient) GetOutboundStatus(ctx context.Context, in *GetOutboundStatusRequest, opts ...grpc.CallOption) (*GetOutboundStatusResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetOutboundStatusResponse) err := c.cc.Invoke(ctx, ObservatoryService_GetOutboundStatus_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // ObservatoryServiceServer is the server API for ObservatoryService service. // All implementations must embed UnimplementedObservatoryServiceServer // for forward compatibility. type ObservatoryServiceServer interface { GetOutboundStatus(context.Context, *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) mustEmbedUnimplementedObservatoryServiceServer() } // UnimplementedObservatoryServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedObservatoryServiceServer struct{} func (UnimplementedObservatoryServiceServer) GetOutboundStatus(context.Context, *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOutboundStatus not implemented") } func (UnimplementedObservatoryServiceServer) mustEmbedUnimplementedObservatoryServiceServer() {} func (UnimplementedObservatoryServiceServer) testEmbeddedByValue() {} // UnsafeObservatoryServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ObservatoryServiceServer will // result in compilation errors. type UnsafeObservatoryServiceServer interface { mustEmbedUnimplementedObservatoryServiceServer() } func RegisterObservatoryServiceServer(s grpc.ServiceRegistrar, srv ObservatoryServiceServer) { // If the following call pancis, it indicates UnimplementedObservatoryServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&ObservatoryService_ServiceDesc, srv) } func _ObservatoryService_GetOutboundStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetOutboundStatusRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ObservatoryServiceServer).GetOutboundStatus(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ObservatoryService_GetOutboundStatus_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ObservatoryServiceServer).GetOutboundStatus(ctx, req.(*GetOutboundStatusRequest)) } return interceptor(ctx, in, info, handler) } // ObservatoryService_ServiceDesc is the grpc.ServiceDesc for ObservatoryService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var ObservatoryService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "v2ray.core.app.observatory.command.ObservatoryService", HandlerType: (*ObservatoryServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "GetOutboundStatus", Handler: _ObservatoryService_GetOutboundStatus_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "app/observatory/command/command.proto", } ================================================ FILE: app/observatory/command/errors.generated.go ================================================ package command import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/observatory/config.pb.go ================================================ package observatory import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ObservationResult struct { state protoimpl.MessageState `protogen:"open.v1"` Status []*OutboundStatus `protobuf:"bytes,1,rep,name=status,proto3" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ObservationResult) Reset() { *x = ObservationResult{} mi := &file_app_observatory_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ObservationResult) String() string { return protoimpl.X.MessageStringOf(x) } func (*ObservationResult) ProtoMessage() {} func (x *ObservationResult) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ObservationResult.ProtoReflect.Descriptor instead. func (*ObservationResult) Descriptor() ([]byte, []int) { return file_app_observatory_config_proto_rawDescGZIP(), []int{0} } func (x *ObservationResult) GetStatus() []*OutboundStatus { if x != nil { return x.Status } return nil } type HealthPingMeasurementResult struct { state protoimpl.MessageState `protogen:"open.v1"` All int64 `protobuf:"varint,1,opt,name=all,proto3" json:"all,omitempty"` Fail int64 `protobuf:"varint,2,opt,name=fail,proto3" json:"fail,omitempty"` Deviation int64 `protobuf:"varint,3,opt,name=deviation,proto3" json:"deviation,omitempty"` Average int64 `protobuf:"varint,4,opt,name=average,proto3" json:"average,omitempty"` Max int64 `protobuf:"varint,5,opt,name=max,proto3" json:"max,omitempty"` Min int64 `protobuf:"varint,6,opt,name=min,proto3" json:"min,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HealthPingMeasurementResult) Reset() { *x = HealthPingMeasurementResult{} mi := &file_app_observatory_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HealthPingMeasurementResult) String() string { return protoimpl.X.MessageStringOf(x) } func (*HealthPingMeasurementResult) ProtoMessage() {} func (x *HealthPingMeasurementResult) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HealthPingMeasurementResult.ProtoReflect.Descriptor instead. func (*HealthPingMeasurementResult) Descriptor() ([]byte, []int) { return file_app_observatory_config_proto_rawDescGZIP(), []int{1} } func (x *HealthPingMeasurementResult) GetAll() int64 { if x != nil { return x.All } return 0 } func (x *HealthPingMeasurementResult) GetFail() int64 { if x != nil { return x.Fail } return 0 } func (x *HealthPingMeasurementResult) GetDeviation() int64 { if x != nil { return x.Deviation } return 0 } func (x *HealthPingMeasurementResult) GetAverage() int64 { if x != nil { return x.Average } return 0 } func (x *HealthPingMeasurementResult) GetMax() int64 { if x != nil { return x.Max } return 0 } func (x *HealthPingMeasurementResult) GetMin() int64 { if x != nil { return x.Min } return 0 } type OutboundStatus struct { state protoimpl.MessageState `protogen:"open.v1"` // @Document Whether this outbound is usable // @Restriction ReadOnlyForUser Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"` // @Document The time for probe request to finish. // @Type time.ms // @Restriction ReadOnlyForUser Delay int64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"` // @Document The last error caused this outbound failed to relay probe request // @Restriction NotMachineReadable LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"` // @Document The outbound tag for this Server // @Type id.outboundTag OutboundTag string `protobuf:"bytes,4,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"` // @Document The time this outbound is known to be alive // @Type id.outboundTag LastSeenTime int64 `protobuf:"varint,5,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"` // @Document The time this outbound is tried // @Type id.outboundTag LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"` HealthPing *HealthPingMeasurementResult `protobuf:"bytes,7,opt,name=health_ping,json=healthPing,proto3" json:"health_ping,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OutboundStatus) Reset() { *x = OutboundStatus{} mi := &file_app_observatory_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OutboundStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutboundStatus) ProtoMessage() {} func (x *OutboundStatus) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutboundStatus.ProtoReflect.Descriptor instead. func (*OutboundStatus) Descriptor() ([]byte, []int) { return file_app_observatory_config_proto_rawDescGZIP(), []int{2} } func (x *OutboundStatus) GetAlive() bool { if x != nil { return x.Alive } return false } func (x *OutboundStatus) GetDelay() int64 { if x != nil { return x.Delay } return 0 } func (x *OutboundStatus) GetLastErrorReason() string { if x != nil { return x.LastErrorReason } return "" } func (x *OutboundStatus) GetOutboundTag() string { if x != nil { return x.OutboundTag } return "" } func (x *OutboundStatus) GetLastSeenTime() int64 { if x != nil { return x.LastSeenTime } return 0 } func (x *OutboundStatus) GetLastTryTime() int64 { if x != nil { return x.LastTryTime } return 0 } func (x *OutboundStatus) GetHealthPing() *HealthPingMeasurementResult { if x != nil { return x.HealthPing } return nil } type ProbeResult struct { state protoimpl.MessageState `protogen:"open.v1"` // @Document Whether this outbound is usable // @Restriction ReadOnlyForUser Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"` // @Document The time for probe request to finish. // @Type time.ms // @Restriction ReadOnlyForUser Delay int64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"` // @Document The error caused this outbound failed to relay probe request // @Restriction NotMachineReadable LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProbeResult) Reset() { *x = ProbeResult{} mi := &file_app_observatory_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ProbeResult) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProbeResult) ProtoMessage() {} func (x *ProbeResult) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProbeResult.ProtoReflect.Descriptor instead. func (*ProbeResult) Descriptor() ([]byte, []int) { return file_app_observatory_config_proto_rawDescGZIP(), []int{3} } func (x *ProbeResult) GetAlive() bool { if x != nil { return x.Alive } return false } func (x *ProbeResult) GetDelay() int64 { if x != nil { return x.Delay } return 0 } func (x *ProbeResult) GetLastErrorReason() string { if x != nil { return x.LastErrorReason } return "" } type Intensity struct { state protoimpl.MessageState `protogen:"open.v1"` // @Document The time interval for a probe request in ms. // @Type time.ms ProbeInterval uint32 `protobuf:"varint,1,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Intensity) Reset() { *x = Intensity{} mi := &file_app_observatory_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Intensity) String() string { return protoimpl.X.MessageStringOf(x) } func (*Intensity) ProtoMessage() {} func (x *Intensity) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Intensity.ProtoReflect.Descriptor instead. func (*Intensity) Descriptor() ([]byte, []int) { return file_app_observatory_config_proto_rawDescGZIP(), []int{4} } func (x *Intensity) GetProbeInterval() uint32 { if x != nil { return x.ProbeInterval } return 0 } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // @Document The selectors for outbound under observation SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"` ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"` ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"` PersistentProbeResult bool `protobuf:"varint,5,opt,name=persistent_probe_result,json=persistentProbeResult,proto3" json:"persistent_probe_result,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_observatory_config_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_config_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_observatory_config_proto_rawDescGZIP(), []int{5} } func (x *Config) GetSubjectSelector() []string { if x != nil { return x.SubjectSelector } return nil } func (x *Config) GetProbeUrl() string { if x != nil { return x.ProbeUrl } return "" } func (x *Config) GetProbeInterval() int64 { if x != nil { return x.ProbeInterval } return 0 } func (x *Config) GetPersistentProbeResult() bool { if x != nil { return x.PersistentProbeResult } return false } var File_app_observatory_config_proto protoreflect.FileDescriptor const file_app_observatory_config_proto_rawDesc = "" + "\n" + "\x1capp/observatory/config.proto\x12\x1av2ray.core.app.observatory\x1a common/protoext/extensions.proto\"W\n" + "\x11ObservationResult\x12B\n" + "\x06status\x18\x01 \x03(\v2*.v2ray.core.app.observatory.OutboundStatusR\x06status\"\x9f\x01\n" + "\x1bHealthPingMeasurementResult\x12\x10\n" + "\x03all\x18\x01 \x01(\x03R\x03all\x12\x12\n" + "\x04fail\x18\x02 \x01(\x03R\x04fail\x12\x1c\n" + "\tdeviation\x18\x03 \x01(\x03R\tdeviation\x12\x18\n" + "\aaverage\x18\x04 \x01(\x03R\aaverage\x12\x10\n" + "\x03max\x18\x05 \x01(\x03R\x03max\x12\x10\n" + "\x03min\x18\x06 \x01(\x03R\x03min\"\xaf\x02\n" + "\x0eOutboundStatus\x12\x14\n" + "\x05alive\x18\x01 \x01(\bR\x05alive\x12\x14\n" + "\x05delay\x18\x02 \x01(\x03R\x05delay\x12*\n" + "\x11last_error_reason\x18\x03 \x01(\tR\x0flastErrorReason\x12!\n" + "\foutbound_tag\x18\x04 \x01(\tR\voutboundTag\x12$\n" + "\x0elast_seen_time\x18\x05 \x01(\x03R\flastSeenTime\x12\"\n" + "\rlast_try_time\x18\x06 \x01(\x03R\vlastTryTime\x12X\n" + "\vhealth_ping\x18\a \x01(\v27.v2ray.core.app.observatory.HealthPingMeasurementResultR\n" + "healthPing\"e\n" + "\vProbeResult\x12\x14\n" + "\x05alive\x18\x01 \x01(\bR\x05alive\x12\x14\n" + "\x05delay\x18\x02 \x01(\x03R\x05delay\x12*\n" + "\x11last_error_reason\x18\x03 \x01(\tR\x0flastErrorReason\"2\n" + "\tIntensity\x12%\n" + "\x0eprobe_interval\x18\x01 \x01(\rR\rprobeInterval\"\xd5\x01\n" + "\x06Config\x12)\n" + "\x10subject_selector\x18\x02 \x03(\tR\x0fsubjectSelector\x12\x1b\n" + "\tprobe_url\x18\x03 \x01(\tR\bprobeUrl\x12%\n" + "\x0eprobe_interval\x18\x04 \x01(\x03R\rprobeInterval\x126\n" + "\x17persistent_probe_result\x18\x05 \x01(\bR\x15persistentProbeResult:$\x82\xb5\x18 \n" + "\aservice\x12\x15backgroundObservatoryBo\n" + "\x1ecom.v2ray.core.app.observatoryP\x01Z.github.com/v2fly/v2ray-core/v5/app/observatory\xaa\x02\x1aV2Ray.Core.App.Observatoryb\x06proto3" var ( file_app_observatory_config_proto_rawDescOnce sync.Once file_app_observatory_config_proto_rawDescData []byte ) func file_app_observatory_config_proto_rawDescGZIP() []byte { file_app_observatory_config_proto_rawDescOnce.Do(func() { file_app_observatory_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_config_proto_rawDesc), len(file_app_observatory_config_proto_rawDesc))) }) return file_app_observatory_config_proto_rawDescData } var file_app_observatory_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_app_observatory_config_proto_goTypes = []any{ (*ObservationResult)(nil), // 0: v2ray.core.app.observatory.ObservationResult (*HealthPingMeasurementResult)(nil), // 1: v2ray.core.app.observatory.HealthPingMeasurementResult (*OutboundStatus)(nil), // 2: v2ray.core.app.observatory.OutboundStatus (*ProbeResult)(nil), // 3: v2ray.core.app.observatory.ProbeResult (*Intensity)(nil), // 4: v2ray.core.app.observatory.Intensity (*Config)(nil), // 5: v2ray.core.app.observatory.Config } var file_app_observatory_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.app.observatory.ObservationResult.status:type_name -> v2ray.core.app.observatory.OutboundStatus 1, // 1: v2ray.core.app.observatory.OutboundStatus.health_ping:type_name -> v2ray.core.app.observatory.HealthPingMeasurementResult 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_app_observatory_config_proto_init() } func file_app_observatory_config_proto_init() { if File_app_observatory_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_config_proto_rawDesc), len(file_app_observatory_config_proto_rawDesc)), NumEnums: 0, NumMessages: 6, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_observatory_config_proto_goTypes, DependencyIndexes: file_app_observatory_config_proto_depIdxs, MessageInfos: file_app_observatory_config_proto_msgTypes, }.Build() File_app_observatory_config_proto = out.File file_app_observatory_config_proto_goTypes = nil file_app_observatory_config_proto_depIdxs = nil } ================================================ FILE: app/observatory/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.observatory; option csharp_namespace = "V2Ray.Core.App.Observatory"; option go_package = "github.com/v2fly/v2ray-core/v5/app/observatory"; option java_package = "com.v2ray.core.app.observatory"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message ObservationResult { repeated OutboundStatus status = 1; } message HealthPingMeasurementResult { int64 all = 1; int64 fail = 2; int64 deviation = 3; int64 average = 4; int64 max = 5; int64 min = 6; } message OutboundStatus{ /* @Document Whether this outbound is usable @Restriction ReadOnlyForUser */ bool alive = 1; /* @Document The time for probe request to finish. @Type time.ms @Restriction ReadOnlyForUser */ int64 delay = 2; /* @Document The last error caused this outbound failed to relay probe request @Restriction NotMachineReadable */ string last_error_reason = 3; /* @Document The outbound tag for this Server @Type id.outboundTag */ string outbound_tag = 4; /* @Document The time this outbound is known to be alive @Type id.outboundTag */ int64 last_seen_time = 5; /* @Document The time this outbound is tried @Type id.outboundTag */ int64 last_try_time = 6; HealthPingMeasurementResult health_ping = 7; } message ProbeResult{ /* @Document Whether this outbound is usable @Restriction ReadOnlyForUser */ bool alive = 1; /* @Document The time for probe request to finish. @Type time.ms @Restriction ReadOnlyForUser */ int64 delay = 2; /* @Document The error caused this outbound failed to relay probe request @Restriction NotMachineReadable */ string last_error_reason = 3; } message Intensity{ /* @Document The time interval for a probe request in ms. @Type time.ms */ uint32 probe_interval = 1; } message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "backgroundObservatory"; /* @Document The selectors for outbound under observation */ repeated string subject_selector = 2; string probe_url = 3; int64 probe_interval = 4; bool persistent_probe_result = 5; } ================================================ FILE: app/observatory/errors.generated.go ================================================ package observatory import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/observatory/explainErrors.go ================================================ package observatory import "github.com/v2fly/v2ray-core/v5/common/errors" type errorCollector struct { errors *errors.Error } func (e *errorCollector) SubmitError(err error) { if e.errors == nil { e.errors = newError("underlying connection error").Base(err) return } e.errors = e.errors.Base(newError("underlying connection error").Base(err)) } func newErrorCollector() *errorCollector { return &errorCollector{} } func (e *errorCollector) UnderlyingError() error { if e.errors == nil { return newError("failed to produce report") } return e.errors } ================================================ FILE: app/observatory/multiobservatory/config.pb.go ================================================ package multiobservatory import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" taggedfeatures "github.com/v2fly/v2ray-core/v5/common/taggedfeatures" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Holders *taggedfeatures.Config `protobuf:"bytes,1,opt,name=holders,proto3" json:"holders,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_observatory_multiobservatory_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_observatory_multiobservatory_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_observatory_multiobservatory_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetHolders() *taggedfeatures.Config { if x != nil { return x.Holders } return nil } var File_app_observatory_multiobservatory_config_proto protoreflect.FileDescriptor const file_app_observatory_multiobservatory_config_proto_rawDesc = "" + "\n" + "-app/observatory/multiobservatory/config.proto\x12+v2ray.core.app.observatory.multiobservatory\x1a$common/taggedfeatures/skeleton.proto\x1a common/protoext/extensions.proto\"m\n" + "\x06Config\x12B\n" + "\aholders\x18\x01 \x01(\v2(.v2ray.core.common.taggedfeatures.ConfigR\aholders:\x1f\x82\xb5\x18\x1b\n" + "\aservice\x12\x10multiobservatoryB\xa2\x01\n" + "/com.v2ray.core.app.observatory.multiObservatoryP\x01Z?github.com/v2fly/v2ray-core/v5/app/observatory/multiobservatory\xaa\x02+V2Ray.Core.App.Observatory.MultiObservatoryb\x06proto3" var ( file_app_observatory_multiobservatory_config_proto_rawDescOnce sync.Once file_app_observatory_multiobservatory_config_proto_rawDescData []byte ) func file_app_observatory_multiobservatory_config_proto_rawDescGZIP() []byte { file_app_observatory_multiobservatory_config_proto_rawDescOnce.Do(func() { file_app_observatory_multiobservatory_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_multiobservatory_config_proto_rawDesc), len(file_app_observatory_multiobservatory_config_proto_rawDesc))) }) return file_app_observatory_multiobservatory_config_proto_rawDescData } var file_app_observatory_multiobservatory_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_app_observatory_multiobservatory_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.observatory.multiobservatory.Config (*taggedfeatures.Config)(nil), // 1: v2ray.core.common.taggedfeatures.Config } var file_app_observatory_multiobservatory_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.app.observatory.multiobservatory.Config.holders:type_name -> v2ray.core.common.taggedfeatures.Config 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_app_observatory_multiobservatory_config_proto_init() } func file_app_observatory_multiobservatory_config_proto_init() { if File_app_observatory_multiobservatory_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_multiobservatory_config_proto_rawDesc), len(file_app_observatory_multiobservatory_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_observatory_multiobservatory_config_proto_goTypes, DependencyIndexes: file_app_observatory_multiobservatory_config_proto_depIdxs, MessageInfos: file_app_observatory_multiobservatory_config_proto_msgTypes, }.Build() File_app_observatory_multiobservatory_config_proto = out.File file_app_observatory_multiobservatory_config_proto_goTypes = nil file_app_observatory_multiobservatory_config_proto_depIdxs = nil } ================================================ FILE: app/observatory/multiobservatory/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.observatory.multiobservatory; option csharp_namespace = "V2Ray.Core.App.Observatory.MultiObservatory"; option go_package = "github.com/v2fly/v2ray-core/v5/app/observatory/multiobservatory"; option java_package = "com.v2ray.core.app.observatory.multiObservatory"; option java_multiple_files = true; import "common/taggedfeatures/skeleton.proto"; import "common/protoext/extensions.proto"; message Config{ option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "multiobservatory"; v2ray.core.common.taggedfeatures.Config holders = 1 ; } ================================================ FILE: app/observatory/multiobservatory/multi.go ================================================ package multiobservatory import ( "context" "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/taggedfeatures" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/extension" ) type Observer struct { features.TaggedFeatures config *Config ctx context.Context } func (o Observer) GetObservation(ctx context.Context) (proto.Message, error) { return common.Must2(o.GetFeaturesByTag("")).(extension.Observatory).GetObservation(ctx) } func (o Observer) Type() interface{} { return extension.ObservatoryType() } func New(ctx context.Context, config *Config) (*Observer, error) { holder, err := taggedfeatures.NewHolderFromConfig(ctx, config.Holders, extension.ObservatoryType()) if err != nil { return nil, err } return &Observer{config: config, ctx: ctx, TaggedFeatures: holder}, nil } func (x *Config) UnmarshalJSONPB(unmarshaler *jsonpb.Unmarshaler, bytes []byte) error { var err error x.Holders, err = taggedfeatures.LoadJSONConfig(context.TODO(), "service", "background", bytes) return err } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) } ================================================ FILE: app/observatory/observatory.go ================================================ package observatory //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/observatory/observer.go ================================================ //go:build !confonly // +build !confonly package observatory import ( "context" "net" "net/http" "net/url" "sort" "sync" "time" "github.com/v2fly/v2ray-core/v5/app/persistentstorage" "github.com/v2fly/v2ray-core/v5/app/persistentstorage/protostorage" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/golang/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" v2net "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/extension" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/transport/internet/tagged" ) type Observer struct { config *Config ctx context.Context statusLock sync.Mutex status []*OutboundStatus finished *done.Instance ohm outbound.Manager persistStorage persistentstorage.ScopedPersistentStorage persistOutboundStatusProtoStorage protostorage.ProtoPersistentStorage } func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) { return &ObservationResult{Status: o.status}, nil } func (o *Observer) Type() interface{} { return extension.ObservatoryType() } func (o *Observer) Start() error { if o.config != nil && len(o.config.SubjectSelector) != 0 { if o.config.PersistentProbeResult { appEnvironment := envctx.EnvironmentFromContext(o.ctx).(environment.AppEnvironment) o.persistStorage = appEnvironment.PersistentStorage() outboundStatusStorage, err := o.persistStorage.NarrowScope(o.ctx, []byte("outbound_status")) if err != nil { return newError("failed to get persistent storage for outbound_status").Base(err) } o.persistOutboundStatusProtoStorage = outboundStatusStorage.(protostorage.ProtoPersistentStorage) list, err := outboundStatusStorage.List(o.ctx, []byte("")) if err != nil { newError("failed to list persisted outbound status").Base(err).WriteToLog() } else { for _, v := range list { o.loadOutboundStatus(string(v)) } } } o.finished = done.New() go o.background() } return nil } func (o *Observer) Close() error { if o.finished != nil { return o.finished.Close() } return nil } func (o *Observer) background() { for !o.finished.Done() { hs, ok := o.ohm.(outbound.HandlerSelector) if !ok { newError("outbound.Manager is not a HandlerSelector").WriteToLog() return } outbounds := hs.Select(o.config.SubjectSelector) sort.Strings(outbounds) o.updateStatus(outbounds) slept := false for _, v := range outbounds { result := o.probe(v) o.updateStatusForResult(v, &result) if o.finished.Done() { return } sleepTime := time.Second * 10 if o.config.ProbeInterval != 0 { sleepTime = time.Duration(o.config.ProbeInterval) } time.Sleep(sleepTime) slept = true } if !slept { sleepTime := time.Second * 10 if o.config.ProbeInterval != 0 { sleepTime = time.Duration(o.config.ProbeInterval) } time.Sleep(sleepTime) } } } func (o *Observer) updateStatus(outbounds []string) { o.statusLock.Lock() defer o.statusLock.Unlock() // TODO should remove old inbound that is removed _ = outbounds } func (o *Observer) probe(outbound string) ProbeResult { errorCollectorForRequest := newErrorCollector() httpTransport := http.Transport{ Proxy: func(*http.Request) (*url.URL, error) { return nil, nil }, DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) { var connection net.Conn taskErr := task.Run(ctx, func() error { // MUST use V2Fly's built in context system dest, err := v2net.ParseDestination(network + ":" + addr) if err != nil { return newError("cannot understand address").Base(err) } trackedCtx := session.TrackedConnectionError(o.ctx, errorCollectorForRequest) conn, err := tagged.Dialer(trackedCtx, dest, outbound) if err != nil { return newError("cannot dial remote address ", dest).Base(err) } connection = conn return nil }) if taskErr != nil { return nil, newError("cannot finish connection").Base(taskErr) } return connection, nil }, TLSHandshakeTimeout: time.Second * 5, } httpClient := &http.Client{ Transport: &httpTransport, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, Jar: nil, Timeout: time.Second * 5, } var GETTime time.Duration err := task.Run(o.ctx, func() error { startTime := time.Now() probeURL := "https://api.v2fly.org/checkConnection.svgz" if o.config.ProbeUrl != "" { probeURL = o.config.ProbeUrl } response, err := httpClient.Get(probeURL) if err != nil { return newError("outbound failed to relay connection").Base(err) } if response.Body != nil { response.Body.Close() } endTime := time.Now() GETTime = endTime.Sub(startTime) return nil }) if err != nil { fullerr := newError("underlying connection failed").Base(errorCollectorForRequest.UnderlyingError()) fullerr = newError("with outbound handler report").Base(fullerr) fullerr = newError("GET request failed:", err).Base(fullerr) fullerr = newError("the outbound ", outbound, " is dead:").Base(fullerr) fullerr = fullerr.AtInfo() fullerr.WriteToLog() return ProbeResult{Alive: false, LastErrorReason: fullerr.Error()} } newError("the outbound ", outbound, " is alive:", GETTime.Seconds()).AtInfo().WriteToLog() return ProbeResult{Alive: true, Delay: GETTime.Milliseconds()} } func (o *Observer) updateStatusForResult(outbound string, result *ProbeResult) { o.statusLock.Lock() defer o.statusLock.Unlock() var status *OutboundStatus if location := o.findStatusLocationLockHolderOnly(outbound); location != -1 { status = o.status[location] } else { status = &OutboundStatus{} o.status = append(o.status, status) } status.LastTryTime = time.Now().Unix() status.OutboundTag = outbound status.Alive = result.Alive if result.Alive { status.Delay = result.Delay status.LastSeenTime = status.LastTryTime status.LastErrorReason = "" } else { status.LastErrorReason = result.LastErrorReason status.Delay = 99999999 } if o.config.PersistentProbeResult { err := o.persistOutboundStatusProtoStorage.PutProto(o.ctx, outbound, status) if err != nil { newError("failed to persist outbound status").Base(err).WriteToLog() } } } func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int { for i, v := range o.status { if v.OutboundTag == outbound { return i } } return -1 } func (o *Observer) loadOutboundStatus(name string) { if o.persistOutboundStatusProtoStorage == nil { return } status := &OutboundStatus{} err := o.persistOutboundStatusProtoStorage.GetProto(o.ctx, name, status) if err != nil { newError("failed to load outbound status").Base(err).WriteToLog() return } o.status = append(o.status, status) } func New(ctx context.Context, config *Config) (*Observer, error) { obs := &Observer{ config: config, ctx: ctx, } err := core.RequireFeatures(ctx, func(om outbound.Manager) { obs.ohm = om }) if err != nil { return nil, newError("Cannot get depended features").Base(err) } return obs, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) } ================================================ FILE: app/persistentstorage/filesystemstorage/config.pb.go ================================================ package filesystemstorage import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type StateStorageRoot int32 const ( StateStorageRoot_WorkDir StateStorageRoot = 0 ) // Enum value maps for StateStorageRoot. var ( StateStorageRoot_name = map[int32]string{ 0: "WorkDir", } StateStorageRoot_value = map[string]int32{ "WorkDir": 0, } ) func (x StateStorageRoot) Enum() *StateStorageRoot { p := new(StateStorageRoot) *p = x return p } func (x StateStorageRoot) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (StateStorageRoot) Descriptor() protoreflect.EnumDescriptor { return file_app_persistentstorage_filesystemstorage_config_proto_enumTypes[0].Descriptor() } func (StateStorageRoot) Type() protoreflect.EnumType { return &file_app_persistentstorage_filesystemstorage_config_proto_enumTypes[0] } func (x StateStorageRoot) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use StateStorageRoot.Descriptor instead. func (StateStorageRoot) EnumDescriptor() ([]byte, []int) { return file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP(), []int{0} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` StateStorageRoot StateStorageRoot `protobuf:"varint,1,opt,name=state_storage_root,json=stateStorageRoot,proto3,enum=v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot" json:"state_storage_root,omitempty"` InstanceName string `protobuf:"bytes,4,opt,name=instance_name,json=instanceName,proto3" json:"instance_name,omitempty"` Protojson bool `protobuf:"varint,5,opt,name=protojson,proto3" json:"protojson,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_persistentstorage_filesystemstorage_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_persistentstorage_filesystemstorage_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetStateStorageRoot() StateStorageRoot { if x != nil { return x.StateStorageRoot } return StateStorageRoot_WorkDir } func (x *Config) GetInstanceName() string { if x != nil { return x.InstanceName } return "" } func (x *Config) GetProtojson() bool { if x != nil { return x.Protojson } return false } var File_app_persistentstorage_filesystemstorage_config_proto protoreflect.FileDescriptor const file_app_persistentstorage_filesystemstorage_config_proto_rawDesc = "" + "\n" + "4app/persistentstorage/filesystemstorage/config.proto\x122v2ray.core.app.persistentstorage.filesystemstorage\x1a common/protoext/extensions.proto\"\xe1\x01\n" + "\x06Config\x12r\n" + "\x12state_storage_root\x18\x01 \x01(\x0e2D.v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRootR\x10stateStorageRoot\x12#\n" + "\rinstance_name\x18\x04 \x01(\tR\finstanceName\x12\x1c\n" + "\tprotojson\x18\x05 \x01(\bR\tprotojson: \x82\xb5\x18\x1c\n" + "\aservice\x12\x11filesystemstorage*\x1f\n" + "\x10StateStorageRoot\x12\v\n" + "\aWorkDir\x10\x00B\xb3\x01\n" + "2com.v2ray.core.persistentstorage.filesystemstorageP\x01ZFgithub.com/v2fly/v2ray-core/v5/app/persistentstorage/filesystemstorage\xaa\x022V2Ray.Core.App.Persistentstorage.Filesystemstorageb\x06proto3" var ( file_app_persistentstorage_filesystemstorage_config_proto_rawDescOnce sync.Once file_app_persistentstorage_filesystemstorage_config_proto_rawDescData []byte ) func file_app_persistentstorage_filesystemstorage_config_proto_rawDescGZIP() []byte { file_app_persistentstorage_filesystemstorage_config_proto_rawDescOnce.Do(func() { file_app_persistentstorage_filesystemstorage_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc), len(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc))) }) return file_app_persistentstorage_filesystemstorage_config_proto_rawDescData } var file_app_persistentstorage_filesystemstorage_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_app_persistentstorage_filesystemstorage_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_app_persistentstorage_filesystemstorage_config_proto_goTypes = []any{ (StateStorageRoot)(0), // 0: v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot (*Config)(nil), // 1: v2ray.core.app.persistentstorage.filesystemstorage.Config } var file_app_persistentstorage_filesystemstorage_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.app.persistentstorage.filesystemstorage.Config.state_storage_root:type_name -> v2ray.core.app.persistentstorage.filesystemstorage.StateStorageRoot 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_app_persistentstorage_filesystemstorage_config_proto_init() } func file_app_persistentstorage_filesystemstorage_config_proto_init() { if File_app_persistentstorage_filesystemstorage_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc), len(file_app_persistentstorage_filesystemstorage_config_proto_rawDesc)), NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_persistentstorage_filesystemstorage_config_proto_goTypes, DependencyIndexes: file_app_persistentstorage_filesystemstorage_config_proto_depIdxs, EnumInfos: file_app_persistentstorage_filesystemstorage_config_proto_enumTypes, MessageInfos: file_app_persistentstorage_filesystemstorage_config_proto_msgTypes, }.Build() File_app_persistentstorage_filesystemstorage_config_proto = out.File file_app_persistentstorage_filesystemstorage_config_proto_goTypes = nil file_app_persistentstorage_filesystemstorage_config_proto_depIdxs = nil } ================================================ FILE: app/persistentstorage/filesystemstorage/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.persistentstorage.filesystemstorage; option csharp_namespace = "V2Ray.Core.App.Persistentstorage.Filesystemstorage"; option go_package = "github.com/v2fly/v2ray-core/v5/app/persistentstorage/filesystemstorage"; option java_package = "com.v2ray.core.persistentstorage.filesystemstorage"; option java_multiple_files = true; import "common/protoext/extensions.proto"; enum StateStorageRoot { WorkDir = 0; } message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "filesystemstorage"; StateStorageRoot state_storage_root = 1; string instance_name = 4; bool protojson = 5; } ================================================ FILE: app/persistentstorage/filesystemstorage/fs.go ================================================ package filesystemstorage import ( "bytes" "context" "io" "path/filepath" "strings" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/persistentstorage/protostorage" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/features/extension/storage" ) func newFileSystemStorage(ctx context.Context, config *Config) storage.ScopedPersistentStorageService { appEnvironment := envctx.EnvironmentFromContext(ctx).(environment.AppEnvironment) fss := &fileSystemStorage{ fs: appEnvironment, pathRoot: config.InstanceName, currentLocation: nil, config: config, } protoStorageInst := protostorage.NewProtoStorage(fss, config.Protojson) fss.proto = protoStorageInst return fss } type fileSystemStorage struct { fs environment.FileSystemCapabilitySet proto protostorage.ProtoPersistentStorage pathRoot string currentLocation []string config *Config } func (f *fileSystemStorage) Type() interface{} { return storage.ScopedPersistentStorageServiceType } func (f *fileSystemStorage) Start() error { return nil } func (f *fileSystemStorage) Close() error { return nil } func (f *fileSystemStorage) PutProto(ctx context.Context, key string, pb proto.Message) error { return f.proto.PutProto(ctx, key, pb) } func (f *fileSystemStorage) GetProto(ctx context.Context, key string, pb proto.Message) error { return f.proto.GetProto(ctx, key, pb) } func (f *fileSystemStorage) ScopedPersistentStorageEngine() { } func (f *fileSystemStorage) Put(ctx context.Context, key []byte, value []byte) error { finalPath := filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...), string(key)) if value == nil { return f.fs.RemoveFile()(finalPath) } writer, err := f.fs.OpenFileForWrite()(finalPath) if err != nil { return err } defer writer.Close() _, err = io.Copy(writer, io.NopCloser(bytes.NewReader(value))) return err } func (f *fileSystemStorage) Get(ctx context.Context, key []byte) ([]byte, error) { finalPath := filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...), string(key)) reader, err := f.fs.OpenFileForRead()(finalPath) if err != nil { return nil, err } defer reader.Close() return io.ReadAll(reader) } func (f *fileSystemStorage) List(ctx context.Context, keyPrefix []byte) ([][]byte, error) { res, err := f.fs.ReadDir()(filepath.Join(f.pathRoot, filepath.Join(f.currentLocation...))) if err != nil { return nil, err } var result [][]byte for _, entry := range res { if !entry.IsDir() && bytes.HasPrefix([]byte(entry.Name()), keyPrefix) { result = append(result, []byte(entry.Name())) } } return result, nil } func (f *fileSystemStorage) Clear(ctx context.Context) { allFile, err := f.List(ctx, []byte{}) if err != nil { return } for _, file := range allFile { _ = f.Put(ctx, file, nil) } } func (f *fileSystemStorage) NarrowScope(ctx context.Context, key []byte) (storage.ScopedPersistentStorage, error) { escapedKey := strings.ReplaceAll(string(key), "/", "_") fss := &fileSystemStorage{ fs: f.fs, pathRoot: f.pathRoot, currentLocation: append(f.currentLocation, escapedKey), config: f.config, } fss.proto = protostorage.NewProtoStorage(fss, f.config.Protojson) return fss, nil } func (f *fileSystemStorage) DropScope(ctx context.Context, key []byte) error { panic("unimplemented") } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return newFileSystemStorage(ctx, config.(*Config)), nil })) } ================================================ FILE: app/persistentstorage/protostorage/protokv.go ================================================ package protostorage import ( "context" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/features/extension/storage" ) type ProtoPersistentStorage interface { PutProto(ctx context.Context, key string, pb proto.Message) error GetProto(ctx context.Context, key string, pb proto.Message) error } type protoStorage struct { storage storage.ScopedPersistentStorage textFormat bool } func (p *protoStorage) PutProto(ctx context.Context, key string, pb proto.Message) error { if !p.textFormat { data, err := proto.Marshal(pb) if err != nil { return err } return p.storage.Put(ctx, []byte(key), data) } else { protojsonStr := protojson.Format(pb) return p.storage.Put(ctx, []byte(key), []byte(protojsonStr)) } } func (p *protoStorage) GetProto(ctx context.Context, key string, pb proto.Message) error { data, err := p.storage.Get(ctx, []byte(key)) if err != nil { return err } if !p.textFormat { return proto.Unmarshal(data, pb) } return protojson.Unmarshal(data, pb) } func NewProtoStorage(storage storage.ScopedPersistentStorage, textFormat bool) ProtoPersistentStorage { return &protoStorage{ storage: storage, textFormat: textFormat, } } ================================================ FILE: app/persistentstorage/storage.go ================================================ package persistentstorage import "github.com/v2fly/v2ray-core/v5/features/extension/storage" type ScopedPersistentStorage = storage.ScopedPersistentStorage ================================================ FILE: app/policy/config.go ================================================ package policy import ( "time" "github.com/v2fly/v2ray-core/v5/features/policy" ) // Duration converts Second to time.Duration. func (s *Second) Duration() time.Duration { if s == nil { return 0 } return time.Second * time.Duration(s.Value) } func defaultPolicy() *Policy { p := policy.SessionDefault() return &Policy{ Timeout: &Policy_Timeout{ Handshake: &Second{Value: uint32(p.Timeouts.Handshake / time.Second)}, ConnectionIdle: &Second{Value: uint32(p.Timeouts.ConnectionIdle / time.Second)}, UplinkOnly: &Second{Value: uint32(p.Timeouts.UplinkOnly / time.Second)}, DownlinkOnly: &Second{Value: uint32(p.Timeouts.DownlinkOnly / time.Second)}, }, Buffer: &Policy_Buffer{ Connection: p.Buffer.PerConnection, }, } } func (p *Policy_Timeout) overrideWith(another *Policy_Timeout) { if another.Handshake != nil { p.Handshake = &Second{Value: another.Handshake.Value} } if another.ConnectionIdle != nil { p.ConnectionIdle = &Second{Value: another.ConnectionIdle.Value} } if another.UplinkOnly != nil { p.UplinkOnly = &Second{Value: another.UplinkOnly.Value} } if another.DownlinkOnly != nil { p.DownlinkOnly = &Second{Value: another.DownlinkOnly.Value} } } func (p *Policy) overrideWith(another *Policy) { if another.Timeout != nil { p.Timeout.overrideWith(another.Timeout) } if another.Stats != nil && p.Stats == nil { p.Stats = &Policy_Stats{} p.Stats = another.Stats } if another.Buffer != nil { p.Buffer = &Policy_Buffer{ Connection: another.Buffer.Connection, } } } // ToCorePolicy converts this Policy to policy.Session. func (p *Policy) ToCorePolicy() policy.Session { cp := policy.SessionDefault() if p.Timeout != nil { cp.Timeouts.ConnectionIdle = p.Timeout.ConnectionIdle.Duration() cp.Timeouts.Handshake = p.Timeout.Handshake.Duration() cp.Timeouts.DownlinkOnly = p.Timeout.DownlinkOnly.Duration() cp.Timeouts.UplinkOnly = p.Timeout.UplinkOnly.Duration() } if p.Stats != nil { cp.Stats.UserUplink = p.Stats.UserUplink cp.Stats.UserDownlink = p.Stats.UserDownlink } if p.Buffer != nil { cp.Buffer.PerConnection = p.Buffer.Connection } return cp } // ToCorePolicy converts this SystemPolicy to policy.System. func (p *SystemPolicy) ToCorePolicy() policy.System { return policy.System{ Stats: policy.SystemStats{ InboundUplink: p.Stats.InboundUplink, InboundDownlink: p.Stats.InboundDownlink, OutboundUplink: p.Stats.OutboundUplink, OutboundDownlink: p.Stats.OutboundDownlink, }, OverrideAccessLogDest: p.OverrideAccessLogDest, } } ================================================ FILE: app/policy/config.pb.go ================================================ package policy import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Second struct { state protoimpl.MessageState `protogen:"open.v1"` Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Second) Reset() { *x = Second{} mi := &file_app_policy_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Second) String() string { return protoimpl.X.MessageStringOf(x) } func (*Second) ProtoMessage() {} func (x *Second) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Second.ProtoReflect.Descriptor instead. func (*Second) Descriptor() ([]byte, []int) { return file_app_policy_config_proto_rawDescGZIP(), []int{0} } func (x *Second) GetValue() uint32 { if x != nil { return x.Value } return 0 } type Policy struct { state protoimpl.MessageState `protogen:"open.v1"` Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout,proto3" json:"timeout,omitempty"` Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats,proto3" json:"stats,omitempty"` Buffer *Policy_Buffer `protobuf:"bytes,3,opt,name=buffer,proto3" json:"buffer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Policy) Reset() { *x = Policy{} mi := &file_app_policy_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Policy) String() string { return protoimpl.X.MessageStringOf(x) } func (*Policy) ProtoMessage() {} func (x *Policy) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Policy.ProtoReflect.Descriptor instead. func (*Policy) Descriptor() ([]byte, []int) { return file_app_policy_config_proto_rawDescGZIP(), []int{1} } func (x *Policy) GetTimeout() *Policy_Timeout { if x != nil { return x.Timeout } return nil } func (x *Policy) GetStats() *Policy_Stats { if x != nil { return x.Stats } return nil } func (x *Policy) GetBuffer() *Policy_Buffer { if x != nil { return x.Buffer } return nil } type SystemPolicy struct { state protoimpl.MessageState `protogen:"open.v1"` Stats *SystemPolicy_Stats `protobuf:"bytes,1,opt,name=stats,proto3" json:"stats,omitempty"` OverrideAccessLogDest bool `protobuf:"varint,2,opt,name=override_access_log_dest,json=overrideAccessLogDest,proto3" json:"override_access_log_dest,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SystemPolicy) Reset() { *x = SystemPolicy{} mi := &file_app_policy_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SystemPolicy) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemPolicy) ProtoMessage() {} func (x *SystemPolicy) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemPolicy.ProtoReflect.Descriptor instead. func (*SystemPolicy) Descriptor() ([]byte, []int) { return file_app_policy_config_proto_rawDescGZIP(), []int{2} } func (x *SystemPolicy) GetStats() *SystemPolicy_Stats { if x != nil { return x.Stats } return nil } func (x *SystemPolicy) GetOverrideAccessLogDest() bool { if x != nil { return x.OverrideAccessLogDest } return false } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level,proto3" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` System *SystemPolicy `protobuf:"bytes,2,opt,name=system,proto3" json:"system,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_policy_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_policy_config_proto_rawDescGZIP(), []int{3} } func (x *Config) GetLevel() map[uint32]*Policy { if x != nil { return x.Level } return nil } func (x *Config) GetSystem() *SystemPolicy { if x != nil { return x.System } return nil } // Timeout is a message for timeout settings in various stages, in seconds. type Policy_Timeout struct { state protoimpl.MessageState `protogen:"open.v1"` Handshake *Second `protobuf:"bytes,1,opt,name=handshake,proto3" json:"handshake,omitempty"` ConnectionIdle *Second `protobuf:"bytes,2,opt,name=connection_idle,json=connectionIdle,proto3" json:"connection_idle,omitempty"` UplinkOnly *Second `protobuf:"bytes,3,opt,name=uplink_only,json=uplinkOnly,proto3" json:"uplink_only,omitempty"` DownlinkOnly *Second `protobuf:"bytes,4,opt,name=downlink_only,json=downlinkOnly,proto3" json:"downlink_only,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Policy_Timeout) Reset() { *x = Policy_Timeout{} mi := &file_app_policy_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Policy_Timeout) String() string { return protoimpl.X.MessageStringOf(x) } func (*Policy_Timeout) ProtoMessage() {} func (x *Policy_Timeout) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Policy_Timeout.ProtoReflect.Descriptor instead. func (*Policy_Timeout) Descriptor() ([]byte, []int) { return file_app_policy_config_proto_rawDescGZIP(), []int{1, 0} } func (x *Policy_Timeout) GetHandshake() *Second { if x != nil { return x.Handshake } return nil } func (x *Policy_Timeout) GetConnectionIdle() *Second { if x != nil { return x.ConnectionIdle } return nil } func (x *Policy_Timeout) GetUplinkOnly() *Second { if x != nil { return x.UplinkOnly } return nil } func (x *Policy_Timeout) GetDownlinkOnly() *Second { if x != nil { return x.DownlinkOnly } return nil } type Policy_Stats struct { state protoimpl.MessageState `protogen:"open.v1"` UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink,proto3" json:"user_uplink,omitempty"` UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink,proto3" json:"user_downlink,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Policy_Stats) Reset() { *x = Policy_Stats{} mi := &file_app_policy_config_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Policy_Stats) String() string { return protoimpl.X.MessageStringOf(x) } func (*Policy_Stats) ProtoMessage() {} func (x *Policy_Stats) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Policy_Stats.ProtoReflect.Descriptor instead. func (*Policy_Stats) Descriptor() ([]byte, []int) { return file_app_policy_config_proto_rawDescGZIP(), []int{1, 1} } func (x *Policy_Stats) GetUserUplink() bool { if x != nil { return x.UserUplink } return false } func (x *Policy_Stats) GetUserDownlink() bool { if x != nil { return x.UserDownlink } return false } type Policy_Buffer struct { state protoimpl.MessageState `protogen:"open.v1"` // Buffer size per connection, in bytes. -1 for unlimited buffer. Connection int32 `protobuf:"varint,1,opt,name=connection,proto3" json:"connection,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Policy_Buffer) Reset() { *x = Policy_Buffer{} mi := &file_app_policy_config_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Policy_Buffer) String() string { return protoimpl.X.MessageStringOf(x) } func (*Policy_Buffer) ProtoMessage() {} func (x *Policy_Buffer) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Policy_Buffer.ProtoReflect.Descriptor instead. func (*Policy_Buffer) Descriptor() ([]byte, []int) { return file_app_policy_config_proto_rawDescGZIP(), []int{1, 2} } func (x *Policy_Buffer) GetConnection() int32 { if x != nil { return x.Connection } return 0 } type SystemPolicy_Stats struct { state protoimpl.MessageState `protogen:"open.v1"` InboundUplink bool `protobuf:"varint,1,opt,name=inbound_uplink,json=inboundUplink,proto3" json:"inbound_uplink,omitempty"` InboundDownlink bool `protobuf:"varint,2,opt,name=inbound_downlink,json=inboundDownlink,proto3" json:"inbound_downlink,omitempty"` OutboundUplink bool `protobuf:"varint,3,opt,name=outbound_uplink,json=outboundUplink,proto3" json:"outbound_uplink,omitempty"` OutboundDownlink bool `protobuf:"varint,4,opt,name=outbound_downlink,json=outboundDownlink,proto3" json:"outbound_downlink,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SystemPolicy_Stats) Reset() { *x = SystemPolicy_Stats{} mi := &file_app_policy_config_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SystemPolicy_Stats) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemPolicy_Stats) ProtoMessage() {} func (x *SystemPolicy_Stats) ProtoReflect() protoreflect.Message { mi := &file_app_policy_config_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemPolicy_Stats.ProtoReflect.Descriptor instead. func (*SystemPolicy_Stats) Descriptor() ([]byte, []int) { return file_app_policy_config_proto_rawDescGZIP(), []int{2, 0} } func (x *SystemPolicy_Stats) GetInboundUplink() bool { if x != nil { return x.InboundUplink } return false } func (x *SystemPolicy_Stats) GetInboundDownlink() bool { if x != nil { return x.InboundDownlink } return false } func (x *SystemPolicy_Stats) GetOutboundUplink() bool { if x != nil { return x.OutboundUplink } return false } func (x *SystemPolicy_Stats) GetOutboundDownlink() bool { if x != nil { return x.OutboundDownlink } return false } var File_app_policy_config_proto protoreflect.FileDescriptor const file_app_policy_config_proto_rawDesc = "" + "\n" + "\x17app/policy/config.proto\x12\x15v2ray.core.app.policy\x1a common/protoext/extensions.proto\"\x1e\n" + "\x06Second\x12\x14\n" + "\x05value\x18\x01 \x01(\rR\x05value\"\xd0\x04\n" + "\x06Policy\x12?\n" + "\atimeout\x18\x01 \x01(\v2%.v2ray.core.app.policy.Policy.TimeoutR\atimeout\x129\n" + "\x05stats\x18\x02 \x01(\v2#.v2ray.core.app.policy.Policy.StatsR\x05stats\x12<\n" + "\x06buffer\x18\x03 \x01(\v2$.v2ray.core.app.policy.Policy.BufferR\x06buffer\x1a\x92\x02\n" + "\aTimeout\x12;\n" + "\thandshake\x18\x01 \x01(\v2\x1d.v2ray.core.app.policy.SecondR\thandshake\x12F\n" + "\x0fconnection_idle\x18\x02 \x01(\v2\x1d.v2ray.core.app.policy.SecondR\x0econnectionIdle\x12>\n" + "\vuplink_only\x18\x03 \x01(\v2\x1d.v2ray.core.app.policy.SecondR\n" + "uplinkOnly\x12B\n" + "\rdownlink_only\x18\x04 \x01(\v2\x1d.v2ray.core.app.policy.SecondR\fdownlinkOnly\x1aM\n" + "\x05Stats\x12\x1f\n" + "\vuser_uplink\x18\x01 \x01(\bR\n" + "userUplink\x12#\n" + "\ruser_downlink\x18\x02 \x01(\bR\fuserDownlink\x1a(\n" + "\x06Buffer\x12\x1e\n" + "\n" + "connection\x18\x01 \x01(\x05R\n" + "connection\"\xba\x02\n" + "\fSystemPolicy\x12?\n" + "\x05stats\x18\x01 \x01(\v2).v2ray.core.app.policy.SystemPolicy.StatsR\x05stats\x127\n" + "\x18override_access_log_dest\x18\x02 \x01(\bR\x15overrideAccessLogDest\x1a\xaf\x01\n" + "\x05Stats\x12%\n" + "\x0einbound_uplink\x18\x01 \x01(\bR\rinboundUplink\x12)\n" + "\x10inbound_downlink\x18\x02 \x01(\bR\x0finboundDownlink\x12'\n" + "\x0foutbound_uplink\x18\x03 \x01(\bR\x0eoutboundUplink\x12+\n" + "\x11outbound_downlink\x18\x04 \x01(\bR\x10outboundDownlink\"\xf5\x01\n" + "\x06Config\x12>\n" + "\x05level\x18\x01 \x03(\v2(.v2ray.core.app.policy.Config.LevelEntryR\x05level\x12;\n" + "\x06system\x18\x02 \x01(\v2#.v2ray.core.app.policy.SystemPolicyR\x06system\x1aW\n" + "\n" + "LevelEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\rR\x03key\x123\n" + "\x05value\x18\x02 \x01(\v2\x1d.v2ray.core.app.policy.PolicyR\x05value:\x028\x01:\x15\x82\xb5\x18\x11\n" + "\aservice\x12\x06policyB`\n" + "\x19com.v2ray.core.app.policyP\x01Z)github.com/v2fly/v2ray-core/v5/app/policy\xaa\x02\x15V2Ray.Core.App.Policyb\x06proto3" var ( file_app_policy_config_proto_rawDescOnce sync.Once file_app_policy_config_proto_rawDescData []byte ) func file_app_policy_config_proto_rawDescGZIP() []byte { file_app_policy_config_proto_rawDescOnce.Do(func() { file_app_policy_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_policy_config_proto_rawDesc), len(file_app_policy_config_proto_rawDesc))) }) return file_app_policy_config_proto_rawDescData } var file_app_policy_config_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_app_policy_config_proto_goTypes = []any{ (*Second)(nil), // 0: v2ray.core.app.policy.Second (*Policy)(nil), // 1: v2ray.core.app.policy.Policy (*SystemPolicy)(nil), // 2: v2ray.core.app.policy.SystemPolicy (*Config)(nil), // 3: v2ray.core.app.policy.Config (*Policy_Timeout)(nil), // 4: v2ray.core.app.policy.Policy.Timeout (*Policy_Stats)(nil), // 5: v2ray.core.app.policy.Policy.Stats (*Policy_Buffer)(nil), // 6: v2ray.core.app.policy.Policy.Buffer (*SystemPolicy_Stats)(nil), // 7: v2ray.core.app.policy.SystemPolicy.Stats nil, // 8: v2ray.core.app.policy.Config.LevelEntry } var file_app_policy_config_proto_depIdxs = []int32{ 4, // 0: v2ray.core.app.policy.Policy.timeout:type_name -> v2ray.core.app.policy.Policy.Timeout 5, // 1: v2ray.core.app.policy.Policy.stats:type_name -> v2ray.core.app.policy.Policy.Stats 6, // 2: v2ray.core.app.policy.Policy.buffer:type_name -> v2ray.core.app.policy.Policy.Buffer 7, // 3: v2ray.core.app.policy.SystemPolicy.stats:type_name -> v2ray.core.app.policy.SystemPolicy.Stats 8, // 4: v2ray.core.app.policy.Config.level:type_name -> v2ray.core.app.policy.Config.LevelEntry 2, // 5: v2ray.core.app.policy.Config.system:type_name -> v2ray.core.app.policy.SystemPolicy 0, // 6: v2ray.core.app.policy.Policy.Timeout.handshake:type_name -> v2ray.core.app.policy.Second 0, // 7: v2ray.core.app.policy.Policy.Timeout.connection_idle:type_name -> v2ray.core.app.policy.Second 0, // 8: v2ray.core.app.policy.Policy.Timeout.uplink_only:type_name -> v2ray.core.app.policy.Second 0, // 9: v2ray.core.app.policy.Policy.Timeout.downlink_only:type_name -> v2ray.core.app.policy.Second 1, // 10: v2ray.core.app.policy.Config.LevelEntry.value:type_name -> v2ray.core.app.policy.Policy 11, // [11:11] is the sub-list for method output_type 11, // [11:11] is the sub-list for method input_type 11, // [11:11] is the sub-list for extension type_name 11, // [11:11] is the sub-list for extension extendee 0, // [0:11] is the sub-list for field type_name } func init() { file_app_policy_config_proto_init() } func file_app_policy_config_proto_init() { if File_app_policy_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_policy_config_proto_rawDesc), len(file_app_policy_config_proto_rawDesc)), NumEnums: 0, NumMessages: 9, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_policy_config_proto_goTypes, DependencyIndexes: file_app_policy_config_proto_depIdxs, MessageInfos: file_app_policy_config_proto_msgTypes, }.Build() File_app_policy_config_proto = out.File file_app_policy_config_proto_goTypes = nil file_app_policy_config_proto_depIdxs = nil } ================================================ FILE: app/policy/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.policy; option csharp_namespace = "V2Ray.Core.App.Policy"; option go_package = "github.com/v2fly/v2ray-core/v5/app/policy"; option java_package = "com.v2ray.core.app.policy"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Second { uint32 value = 1; } message Policy { // Timeout is a message for timeout settings in various stages, in seconds. message Timeout { Second handshake = 1; Second connection_idle = 2; Second uplink_only = 3; Second downlink_only = 4; } message Stats { bool user_uplink = 1; bool user_downlink = 2; } message Buffer { // Buffer size per connection, in bytes. -1 for unlimited buffer. int32 connection = 1; } Timeout timeout = 1; Stats stats = 2; Buffer buffer = 3; } message SystemPolicy { message Stats { bool inbound_uplink = 1; bool inbound_downlink = 2; bool outbound_uplink = 3; bool outbound_downlink = 4; } Stats stats = 1; bool override_access_log_dest = 2; } message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "policy"; map level = 1; SystemPolicy system = 2; } ================================================ FILE: app/policy/errors.generated.go ================================================ package policy import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/policy/manager.go ================================================ package policy import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/policy" ) // Instance is an instance of Policy manager. type Instance struct { levels map[uint32]*Policy system *SystemPolicy } // New creates new Policy manager instance. func New(ctx context.Context, config *Config) (*Instance, error) { m := &Instance{ levels: make(map[uint32]*Policy), system: config.System, } if len(config.Level) > 0 { for lv, p := range config.Level { pp := defaultPolicy() pp.overrideWith(p) m.levels[lv] = pp } } return m, nil } // Type implements common.HasType. func (*Instance) Type() interface{} { return policy.ManagerType() } // ForLevel implements policy.Manager. func (m *Instance) ForLevel(level uint32) policy.Session { if p, ok := m.levels[level]; ok { return p.ToCorePolicy() } return policy.SessionDefault() } // ForSystem implements policy.Manager. func (m *Instance) ForSystem() policy.System { if m.system == nil { return policy.System{} } return m.system.ToCorePolicy() } // Start implements common.Runnable.Start(). func (m *Instance) Start() error { return nil } // Close implements common.Closable.Close(). func (m *Instance) Close() error { return nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) } ================================================ FILE: app/policy/manager_test.go ================================================ package policy_test import ( "context" "testing" "time" . "github.com/v2fly/v2ray-core/v5/app/policy" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/policy" ) func TestPolicy(t *testing.T) { manager, err := New(context.Background(), &Config{ Level: map[uint32]*Policy{ 0: { Timeout: &Policy_Timeout{ Handshake: &Second{ Value: 2, }, }, }, }, }) common.Must(err) pDefault := policy.SessionDefault() { p := manager.ForLevel(0) if p.Timeouts.Handshake != 2*time.Second { t.Error("expect 2 sec timeout, but got ", p.Timeouts.Handshake) } if p.Timeouts.ConnectionIdle != pDefault.Timeouts.ConnectionIdle { t.Error("expect ", pDefault.Timeouts.ConnectionIdle, " sec timeout, but got ", p.Timeouts.ConnectionIdle) } } { p := manager.ForLevel(1) if p.Timeouts.Handshake != pDefault.Timeouts.Handshake { t.Error("expect ", pDefault.Timeouts.Handshake, " sec timeout, but got ", p.Timeouts.Handshake) } } } ================================================ FILE: app/policy/policy.go ================================================ // Package policy is an implementation of policy.Manager feature. package policy //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/proxyman/command/command.go ================================================ package command import ( "context" grpc "google.golang.org/grpc" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features/inbound" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/proxy" ) // InboundOperation is the interface for operations that applies to inbound handlers. type InboundOperation interface { // ApplyInbound applies this operation to the given inbound handler. ApplyInbound(context.Context, inbound.Handler) error } // OutboundOperation is the interface for operations that applies to outbound handlers. type OutboundOperation interface { // ApplyOutbound applies this operation to the given outbound handler. ApplyOutbound(context.Context, outbound.Handler) error } func getInbound(handler inbound.Handler) (proxy.Inbound, error) { gi, ok := handler.(proxy.GetInbound) if !ok { return nil, newError("can't get inbound proxy from handler.") } return gi.GetInbound(), nil } // ApplyInbound implements InboundOperation. func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler inbound.Handler) error { p, err := getInbound(handler) if err != nil { return err } um, ok := p.(proxy.UserManager) if !ok { return newError("proxy is not a UserManager") } mUser, err := op.User.ToMemoryUser() if err != nil { return newError("failed to parse user").Base(err) } return um.AddUser(ctx, mUser) } // ApplyInbound implements InboundOperation. func (op *RemoveUserOperation) ApplyInbound(ctx context.Context, handler inbound.Handler) error { p, err := getInbound(handler) if err != nil { return err } um, ok := p.(proxy.UserManager) if !ok { return newError("proxy is not a UserManager") } return um.RemoveUser(ctx, op.Email) } type handlerServer struct { s *core.Instance ihm inbound.Manager ohm outbound.Manager } func (s *handlerServer) AddInbound(ctx context.Context, request *AddInboundRequest) (*AddInboundResponse, error) { if err := core.AddInboundHandler(s.s, request.Inbound); err != nil { return nil, err } return &AddInboundResponse{}, nil } func (s *handlerServer) RemoveInbound(ctx context.Context, request *RemoveInboundRequest) (*RemoveInboundResponse, error) { return &RemoveInboundResponse{}, s.ihm.RemoveHandler(ctx, request.Tag) } func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundRequest) (*AlterInboundResponse, error) { rawOperation, err := serial.GetInstanceOf(request.Operation) if err != nil { return nil, newError("unknown operation").Base(err) } operation, ok := rawOperation.(InboundOperation) if !ok { return nil, newError("not an inbound operation") } handler, err := s.ihm.GetHandler(ctx, request.Tag) if err != nil { return nil, newError("failed to get handler: ", request.Tag).Base(err) } return &AlterInboundResponse{}, operation.ApplyInbound(ctx, handler) } func (s *handlerServer) AddOutbound(ctx context.Context, request *AddOutboundRequest) (*AddOutboundResponse, error) { if err := core.AddOutboundHandler(s.s, request.Outbound); err != nil { return nil, err } return &AddOutboundResponse{}, nil } func (s *handlerServer) RemoveOutbound(ctx context.Context, request *RemoveOutboundRequest) (*RemoveOutboundResponse, error) { return &RemoveOutboundResponse{}, core.RemoveOutboundHandler(s.s, request.Tag) } func (s *handlerServer) AlterOutbound(ctx context.Context, request *AlterOutboundRequest) (*AlterOutboundResponse, error) { rawOperation, err := serial.GetInstanceOf(request.Operation) if err != nil { return nil, newError("unknown operation").Base(err) } operation, ok := rawOperation.(OutboundOperation) if !ok { return nil, newError("not an outbound operation") } handler := s.ohm.GetHandler(request.Tag) return &AlterOutboundResponse{}, operation.ApplyOutbound(ctx, handler) } func (s *handlerServer) mustEmbedUnimplementedHandlerServiceServer() {} type service struct { v *core.Instance } func (s *service) Register(server *grpc.Server) { hs := &handlerServer{ s: s.v, } common.Must(s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) { hs.ihm = im hs.ohm = om })) RegisterHandlerServiceServer(server, hs) } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { s := core.MustFromContext(ctx) return &service{v: s}, nil })) } ================================================ FILE: app/proxyman/command/command.pb.go ================================================ package command import ( v5 "github.com/v2fly/v2ray-core/v5" protocol "github.com/v2fly/v2ray-core/v5/common/protocol" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type AddUserOperation struct { state protoimpl.MessageState `protogen:"open.v1"` User *protocol.User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddUserOperation) Reset() { *x = AddUserOperation{} mi := &file_app_proxyman_command_command_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddUserOperation) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddUserOperation) ProtoMessage() {} func (x *AddUserOperation) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddUserOperation.ProtoReflect.Descriptor instead. func (*AddUserOperation) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{0} } func (x *AddUserOperation) GetUser() *protocol.User { if x != nil { return x.User } return nil } type RemoveUserOperation struct { state protoimpl.MessageState `protogen:"open.v1"` Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RemoveUserOperation) Reset() { *x = RemoveUserOperation{} mi := &file_app_proxyman_command_command_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RemoveUserOperation) String() string { return protoimpl.X.MessageStringOf(x) } func (*RemoveUserOperation) ProtoMessage() {} func (x *RemoveUserOperation) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RemoveUserOperation.ProtoReflect.Descriptor instead. func (*RemoveUserOperation) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{1} } func (x *RemoveUserOperation) GetEmail() string { if x != nil { return x.Email } return "" } type AddInboundRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Inbound *v5.InboundHandlerConfig `protobuf:"bytes,1,opt,name=inbound,proto3" json:"inbound,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddInboundRequest) Reset() { *x = AddInboundRequest{} mi := &file_app_proxyman_command_command_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddInboundRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddInboundRequest) ProtoMessage() {} func (x *AddInboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddInboundRequest.ProtoReflect.Descriptor instead. func (*AddInboundRequest) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{2} } func (x *AddInboundRequest) GetInbound() *v5.InboundHandlerConfig { if x != nil { return x.Inbound } return nil } type AddInboundResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddInboundResponse) Reset() { *x = AddInboundResponse{} mi := &file_app_proxyman_command_command_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddInboundResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddInboundResponse) ProtoMessage() {} func (x *AddInboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddInboundResponse.ProtoReflect.Descriptor instead. func (*AddInboundResponse) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{3} } type RemoveInboundRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RemoveInboundRequest) Reset() { *x = RemoveInboundRequest{} mi := &file_app_proxyman_command_command_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RemoveInboundRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RemoveInboundRequest) ProtoMessage() {} func (x *RemoveInboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RemoveInboundRequest.ProtoReflect.Descriptor instead. func (*RemoveInboundRequest) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{4} } func (x *RemoveInboundRequest) GetTag() string { if x != nil { return x.Tag } return "" } type RemoveInboundResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RemoveInboundResponse) Reset() { *x = RemoveInboundResponse{} mi := &file_app_proxyman_command_command_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RemoveInboundResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*RemoveInboundResponse) ProtoMessage() {} func (x *RemoveInboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RemoveInboundResponse.ProtoReflect.Descriptor instead. func (*RemoveInboundResponse) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{5} } type AlterInboundRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` Operation *anypb.Any `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AlterInboundRequest) Reset() { *x = AlterInboundRequest{} mi := &file_app_proxyman_command_command_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AlterInboundRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*AlterInboundRequest) ProtoMessage() {} func (x *AlterInboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AlterInboundRequest.ProtoReflect.Descriptor instead. func (*AlterInboundRequest) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{6} } func (x *AlterInboundRequest) GetTag() string { if x != nil { return x.Tag } return "" } func (x *AlterInboundRequest) GetOperation() *anypb.Any { if x != nil { return x.Operation } return nil } type AlterInboundResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AlterInboundResponse) Reset() { *x = AlterInboundResponse{} mi := &file_app_proxyman_command_command_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AlterInboundResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*AlterInboundResponse) ProtoMessage() {} func (x *AlterInboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AlterInboundResponse.ProtoReflect.Descriptor instead. func (*AlterInboundResponse) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{7} } type AddOutboundRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Outbound *v5.OutboundHandlerConfig `protobuf:"bytes,1,opt,name=outbound,proto3" json:"outbound,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddOutboundRequest) Reset() { *x = AddOutboundRequest{} mi := &file_app_proxyman_command_command_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddOutboundRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddOutboundRequest) ProtoMessage() {} func (x *AddOutboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddOutboundRequest.ProtoReflect.Descriptor instead. func (*AddOutboundRequest) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{8} } func (x *AddOutboundRequest) GetOutbound() *v5.OutboundHandlerConfig { if x != nil { return x.Outbound } return nil } type AddOutboundResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddOutboundResponse) Reset() { *x = AddOutboundResponse{} mi := &file_app_proxyman_command_command_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddOutboundResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddOutboundResponse) ProtoMessage() {} func (x *AddOutboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddOutboundResponse.ProtoReflect.Descriptor instead. func (*AddOutboundResponse) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{9} } type RemoveOutboundRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RemoveOutboundRequest) Reset() { *x = RemoveOutboundRequest{} mi := &file_app_proxyman_command_command_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RemoveOutboundRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RemoveOutboundRequest) ProtoMessage() {} func (x *RemoveOutboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RemoveOutboundRequest.ProtoReflect.Descriptor instead. func (*RemoveOutboundRequest) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{10} } func (x *RemoveOutboundRequest) GetTag() string { if x != nil { return x.Tag } return "" } type RemoveOutboundResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RemoveOutboundResponse) Reset() { *x = RemoveOutboundResponse{} mi := &file_app_proxyman_command_command_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RemoveOutboundResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*RemoveOutboundResponse) ProtoMessage() {} func (x *RemoveOutboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RemoveOutboundResponse.ProtoReflect.Descriptor instead. func (*RemoveOutboundResponse) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{11} } type AlterOutboundRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` Operation *anypb.Any `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AlterOutboundRequest) Reset() { *x = AlterOutboundRequest{} mi := &file_app_proxyman_command_command_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AlterOutboundRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*AlterOutboundRequest) ProtoMessage() {} func (x *AlterOutboundRequest) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AlterOutboundRequest.ProtoReflect.Descriptor instead. func (*AlterOutboundRequest) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{12} } func (x *AlterOutboundRequest) GetTag() string { if x != nil { return x.Tag } return "" } func (x *AlterOutboundRequest) GetOperation() *anypb.Any { if x != nil { return x.Operation } return nil } type AlterOutboundResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AlterOutboundResponse) Reset() { *x = AlterOutboundResponse{} mi := &file_app_proxyman_command_command_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AlterOutboundResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*AlterOutboundResponse) ProtoMessage() {} func (x *AlterOutboundResponse) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AlterOutboundResponse.ProtoReflect.Descriptor instead. func (*AlterOutboundResponse) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{13} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_proxyman_command_command_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_command_command_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{14} } var File_app_proxyman_command_command_proto protoreflect.FileDescriptor const file_app_proxyman_command_command_proto_rawDesc = "" + "\n" + "\"app/proxyman/command/command.proto\x12\x1fv2ray.core.app.proxyman.command\x1a\x1acommon/protocol/user.proto\x1a\x19google/protobuf/any.proto\x1a common/protoext/extensions.proto\x1a\fconfig.proto\"H\n" + "\x10AddUserOperation\x124\n" + "\x04user\x18\x01 \x01(\v2 .v2ray.core.common.protocol.UserR\x04user\"+\n" + "\x13RemoveUserOperation\x12\x14\n" + "\x05email\x18\x01 \x01(\tR\x05email\"O\n" + "\x11AddInboundRequest\x12:\n" + "\ainbound\x18\x01 \x01(\v2 .v2ray.core.InboundHandlerConfigR\ainbound\"\x14\n" + "\x12AddInboundResponse\"(\n" + "\x14RemoveInboundRequest\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\"\x17\n" + "\x15RemoveInboundResponse\"[\n" + "\x13AlterInboundRequest\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x122\n" + "\toperation\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\toperation\"\x16\n" + "\x14AlterInboundResponse\"S\n" + "\x12AddOutboundRequest\x12=\n" + "\boutbound\x18\x01 \x01(\v2!.v2ray.core.OutboundHandlerConfigR\boutbound\"\x15\n" + "\x13AddOutboundResponse\")\n" + "\x15RemoveOutboundRequest\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\"\x18\n" + "\x16RemoveOutboundResponse\"\\\n" + "\x14AlterOutboundRequest\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x122\n" + "\toperation\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\toperation\"\x17\n" + "\x15AlterOutboundResponse\"%\n" + "\x06Config:\x1b\x82\xb5\x18\x17\n" + "\vgrpcservice\x12\bproxyman2\x90\x06\n" + "\x0eHandlerService\x12w\n" + "\n" + "AddInbound\x122.v2ray.core.app.proxyman.command.AddInboundRequest\x1a3.v2ray.core.app.proxyman.command.AddInboundResponse\"\x00\x12\x80\x01\n" + "\rRemoveInbound\x125.v2ray.core.app.proxyman.command.RemoveInboundRequest\x1a6.v2ray.core.app.proxyman.command.RemoveInboundResponse\"\x00\x12}\n" + "\fAlterInbound\x124.v2ray.core.app.proxyman.command.AlterInboundRequest\x1a5.v2ray.core.app.proxyman.command.AlterInboundResponse\"\x00\x12z\n" + "\vAddOutbound\x123.v2ray.core.app.proxyman.command.AddOutboundRequest\x1a4.v2ray.core.app.proxyman.command.AddOutboundResponse\"\x00\x12\x83\x01\n" + "\x0eRemoveOutbound\x126.v2ray.core.app.proxyman.command.RemoveOutboundRequest\x1a7.v2ray.core.app.proxyman.command.RemoveOutboundResponse\"\x00\x12\x80\x01\n" + "\rAlterOutbound\x125.v2ray.core.app.proxyman.command.AlterOutboundRequest\x1a6.v2ray.core.app.proxyman.command.AlterOutboundResponse\"\x00B~\n" + "#com.v2ray.core.app.proxyman.commandP\x01Z3github.com/v2fly/v2ray-core/v5/app/proxyman/command\xaa\x02\x1fV2Ray.Core.App.Proxyman.Commandb\x06proto3" var ( file_app_proxyman_command_command_proto_rawDescOnce sync.Once file_app_proxyman_command_command_proto_rawDescData []byte ) func file_app_proxyman_command_command_proto_rawDescGZIP() []byte { file_app_proxyman_command_command_proto_rawDescOnce.Do(func() { file_app_proxyman_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_proxyman_command_command_proto_rawDesc), len(file_app_proxyman_command_command_proto_rawDesc))) }) return file_app_proxyman_command_command_proto_rawDescData } var file_app_proxyman_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_app_proxyman_command_command_proto_goTypes = []any{ (*AddUserOperation)(nil), // 0: v2ray.core.app.proxyman.command.AddUserOperation (*RemoveUserOperation)(nil), // 1: v2ray.core.app.proxyman.command.RemoveUserOperation (*AddInboundRequest)(nil), // 2: v2ray.core.app.proxyman.command.AddInboundRequest (*AddInboundResponse)(nil), // 3: v2ray.core.app.proxyman.command.AddInboundResponse (*RemoveInboundRequest)(nil), // 4: v2ray.core.app.proxyman.command.RemoveInboundRequest (*RemoveInboundResponse)(nil), // 5: v2ray.core.app.proxyman.command.RemoveInboundResponse (*AlterInboundRequest)(nil), // 6: v2ray.core.app.proxyman.command.AlterInboundRequest (*AlterInboundResponse)(nil), // 7: v2ray.core.app.proxyman.command.AlterInboundResponse (*AddOutboundRequest)(nil), // 8: v2ray.core.app.proxyman.command.AddOutboundRequest (*AddOutboundResponse)(nil), // 9: v2ray.core.app.proxyman.command.AddOutboundResponse (*RemoveOutboundRequest)(nil), // 10: v2ray.core.app.proxyman.command.RemoveOutboundRequest (*RemoveOutboundResponse)(nil), // 11: v2ray.core.app.proxyman.command.RemoveOutboundResponse (*AlterOutboundRequest)(nil), // 12: v2ray.core.app.proxyman.command.AlterOutboundRequest (*AlterOutboundResponse)(nil), // 13: v2ray.core.app.proxyman.command.AlterOutboundResponse (*Config)(nil), // 14: v2ray.core.app.proxyman.command.Config (*protocol.User)(nil), // 15: v2ray.core.common.protocol.User (*v5.InboundHandlerConfig)(nil), // 16: v2ray.core.InboundHandlerConfig (*anypb.Any)(nil), // 17: google.protobuf.Any (*v5.OutboundHandlerConfig)(nil), // 18: v2ray.core.OutboundHandlerConfig } var file_app_proxyman_command_command_proto_depIdxs = []int32{ 15, // 0: v2ray.core.app.proxyman.command.AddUserOperation.user:type_name -> v2ray.core.common.protocol.User 16, // 1: v2ray.core.app.proxyman.command.AddInboundRequest.inbound:type_name -> v2ray.core.InboundHandlerConfig 17, // 2: v2ray.core.app.proxyman.command.AlterInboundRequest.operation:type_name -> google.protobuf.Any 18, // 3: v2ray.core.app.proxyman.command.AddOutboundRequest.outbound:type_name -> v2ray.core.OutboundHandlerConfig 17, // 4: v2ray.core.app.proxyman.command.AlterOutboundRequest.operation:type_name -> google.protobuf.Any 2, // 5: v2ray.core.app.proxyman.command.HandlerService.AddInbound:input_type -> v2ray.core.app.proxyman.command.AddInboundRequest 4, // 6: v2ray.core.app.proxyman.command.HandlerService.RemoveInbound:input_type -> v2ray.core.app.proxyman.command.RemoveInboundRequest 6, // 7: v2ray.core.app.proxyman.command.HandlerService.AlterInbound:input_type -> v2ray.core.app.proxyman.command.AlterInboundRequest 8, // 8: v2ray.core.app.proxyman.command.HandlerService.AddOutbound:input_type -> v2ray.core.app.proxyman.command.AddOutboundRequest 10, // 9: v2ray.core.app.proxyman.command.HandlerService.RemoveOutbound:input_type -> v2ray.core.app.proxyman.command.RemoveOutboundRequest 12, // 10: v2ray.core.app.proxyman.command.HandlerService.AlterOutbound:input_type -> v2ray.core.app.proxyman.command.AlterOutboundRequest 3, // 11: v2ray.core.app.proxyman.command.HandlerService.AddInbound:output_type -> v2ray.core.app.proxyman.command.AddInboundResponse 5, // 12: v2ray.core.app.proxyman.command.HandlerService.RemoveInbound:output_type -> v2ray.core.app.proxyman.command.RemoveInboundResponse 7, // 13: v2ray.core.app.proxyman.command.HandlerService.AlterInbound:output_type -> v2ray.core.app.proxyman.command.AlterInboundResponse 9, // 14: v2ray.core.app.proxyman.command.HandlerService.AddOutbound:output_type -> v2ray.core.app.proxyman.command.AddOutboundResponse 11, // 15: v2ray.core.app.proxyman.command.HandlerService.RemoveOutbound:output_type -> v2ray.core.app.proxyman.command.RemoveOutboundResponse 13, // 16: v2ray.core.app.proxyman.command.HandlerService.AlterOutbound:output_type -> v2ray.core.app.proxyman.command.AlterOutboundResponse 11, // [11:17] is the sub-list for method output_type 5, // [5:11] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_app_proxyman_command_command_proto_init() } func file_app_proxyman_command_command_proto_init() { if File_app_proxyman_command_command_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proxyman_command_command_proto_rawDesc), len(file_app_proxyman_command_command_proto_rawDesc)), NumEnums: 0, NumMessages: 15, NumExtensions: 0, NumServices: 1, }, GoTypes: file_app_proxyman_command_command_proto_goTypes, DependencyIndexes: file_app_proxyman_command_command_proto_depIdxs, MessageInfos: file_app_proxyman_command_command_proto_msgTypes, }.Build() File_app_proxyman_command_command_proto = out.File file_app_proxyman_command_command_proto_goTypes = nil file_app_proxyman_command_command_proto_depIdxs = nil } ================================================ FILE: app/proxyman/command/command.proto ================================================ syntax = "proto3"; package v2ray.core.app.proxyman.command; option csharp_namespace = "V2Ray.Core.App.Proxyman.Command"; option go_package = "github.com/v2fly/v2ray-core/v5/app/proxyman/command"; option java_package = "com.v2ray.core.app.proxyman.command"; option java_multiple_files = true; import "common/protocol/user.proto"; import "google/protobuf/any.proto"; import "common/protoext/extensions.proto"; import "config.proto"; message AddUserOperation { v2ray.core.common.protocol.User user = 1; } message RemoveUserOperation { string email = 1; } message AddInboundRequest { core.InboundHandlerConfig inbound = 1; } message AddInboundResponse {} message RemoveInboundRequest { string tag = 1; } message RemoveInboundResponse {} message AlterInboundRequest { string tag = 1; google.protobuf.Any operation = 2; } message AlterInboundResponse {} message AddOutboundRequest { core.OutboundHandlerConfig outbound = 1; } message AddOutboundResponse {} message RemoveOutboundRequest { string tag = 1; } message RemoveOutboundResponse {} message AlterOutboundRequest { string tag = 1; google.protobuf.Any operation = 2; } message AlterOutboundResponse {} service HandlerService { rpc AddInbound(AddInboundRequest) returns (AddInboundResponse) {} rpc RemoveInbound(RemoveInboundRequest) returns (RemoveInboundResponse) {} rpc AlterInbound(AlterInboundRequest) returns (AlterInboundResponse) {} rpc AddOutbound(AddOutboundRequest) returns (AddOutboundResponse) {} rpc RemoveOutbound(RemoveOutboundRequest) returns (RemoveOutboundResponse) {} rpc AlterOutbound(AlterOutboundRequest) returns (AlterOutboundResponse) {} } message Config { option (v2ray.core.common.protoext.message_opt).type = "grpcservice"; option (v2ray.core.common.protoext.message_opt).short_name = "proxyman"; } ================================================ FILE: app/proxyman/command/command_grpc.pb.go ================================================ package command import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( HandlerService_AddInbound_FullMethodName = "/v2ray.core.app.proxyman.command.HandlerService/AddInbound" HandlerService_RemoveInbound_FullMethodName = "/v2ray.core.app.proxyman.command.HandlerService/RemoveInbound" HandlerService_AlterInbound_FullMethodName = "/v2ray.core.app.proxyman.command.HandlerService/AlterInbound" HandlerService_AddOutbound_FullMethodName = "/v2ray.core.app.proxyman.command.HandlerService/AddOutbound" HandlerService_RemoveOutbound_FullMethodName = "/v2ray.core.app.proxyman.command.HandlerService/RemoveOutbound" HandlerService_AlterOutbound_FullMethodName = "/v2ray.core.app.proxyman.command.HandlerService/AlterOutbound" ) // HandlerServiceClient is the client API for HandlerService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type HandlerServiceClient interface { AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) } type handlerServiceClient struct { cc grpc.ClientConnInterface } func NewHandlerServiceClient(cc grpc.ClientConnInterface) HandlerServiceClient { return &handlerServiceClient{cc} } func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AddInboundResponse) err := c.cc.Invoke(ctx, HandlerService_AddInbound_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RemoveInboundResponse) err := c.cc.Invoke(ctx, HandlerService_RemoveInbound_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AlterInboundResponse) err := c.cc.Invoke(ctx, HandlerService_AlterInbound_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AddOutboundResponse) err := c.cc.Invoke(ctx, HandlerService_AddOutbound_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RemoveOutboundResponse) err := c.cc.Invoke(ctx, HandlerService_RemoveOutbound_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AlterOutboundResponse) err := c.cc.Invoke(ctx, HandlerService_AlterOutbound_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // HandlerServiceServer is the server API for HandlerService service. // All implementations must embed UnimplementedHandlerServiceServer // for forward compatibility. type HandlerServiceServer interface { AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) mustEmbedUnimplementedHandlerServiceServer() } // UnimplementedHandlerServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedHandlerServiceServer struct{} func (UnimplementedHandlerServiceServer) AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddInbound not implemented") } func (UnimplementedHandlerServiceServer) RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveInbound not implemented") } func (UnimplementedHandlerServiceServer) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AlterInbound not implemented") } func (UnimplementedHandlerServiceServer) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddOutbound not implemented") } func (UnimplementedHandlerServiceServer) RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveOutbound not implemented") } func (UnimplementedHandlerServiceServer) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AlterOutbound not implemented") } func (UnimplementedHandlerServiceServer) mustEmbedUnimplementedHandlerServiceServer() {} func (UnimplementedHandlerServiceServer) testEmbeddedByValue() {} // UnsafeHandlerServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to HandlerServiceServer will // result in compilation errors. type UnsafeHandlerServiceServer interface { mustEmbedUnimplementedHandlerServiceServer() } func RegisterHandlerServiceServer(s grpc.ServiceRegistrar, srv HandlerServiceServer) { // If the following call pancis, it indicates UnimplementedHandlerServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&HandlerService_ServiceDesc, srv) } func _HandlerService_AddInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AddInboundRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(HandlerServiceServer).AddInbound(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: HandlerService_AddInbound_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).AddInbound(ctx, req.(*AddInboundRequest)) } return interceptor(ctx, in, info, handler) } func _HandlerService_RemoveInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RemoveInboundRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(HandlerServiceServer).RemoveInbound(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: HandlerService_RemoveInbound_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).RemoveInbound(ctx, req.(*RemoveInboundRequest)) } return interceptor(ctx, in, info, handler) } func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AlterInboundRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(HandlerServiceServer).AlterInbound(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: HandlerService_AlterInbound_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).AlterInbound(ctx, req.(*AlterInboundRequest)) } return interceptor(ctx, in, info, handler) } func _HandlerService_AddOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AddOutboundRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(HandlerServiceServer).AddOutbound(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: HandlerService_AddOutbound_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).AddOutbound(ctx, req.(*AddOutboundRequest)) } return interceptor(ctx, in, info, handler) } func _HandlerService_RemoveOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RemoveOutboundRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(HandlerServiceServer).RemoveOutbound(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: HandlerService_RemoveOutbound_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).RemoveOutbound(ctx, req.(*RemoveOutboundRequest)) } return interceptor(ctx, in, info, handler) } func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AlterOutboundRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(HandlerServiceServer).AlterOutbound(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: HandlerService_AlterOutbound_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HandlerServiceServer).AlterOutbound(ctx, req.(*AlterOutboundRequest)) } return interceptor(ctx, in, info, handler) } // HandlerService_ServiceDesc is the grpc.ServiceDesc for HandlerService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var HandlerService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "v2ray.core.app.proxyman.command.HandlerService", HandlerType: (*HandlerServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "AddInbound", Handler: _HandlerService_AddInbound_Handler, }, { MethodName: "RemoveInbound", Handler: _HandlerService_RemoveInbound_Handler, }, { MethodName: "AlterInbound", Handler: _HandlerService_AlterInbound_Handler, }, { MethodName: "AddOutbound", Handler: _HandlerService_AddOutbound_Handler, }, { MethodName: "RemoveOutbound", Handler: _HandlerService_RemoveOutbound_Handler, }, { MethodName: "AlterOutbound", Handler: _HandlerService_AlterOutbound_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "app/proxyman/command/command.proto", } ================================================ FILE: app/proxyman/command/doc.go ================================================ package command //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/proxyman/command/errors.generated.go ================================================ package command import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/proxyman/config.go ================================================ package proxyman func (s *AllocationStrategy) GetConcurrencyValue() uint32 { if s == nil || s.Concurrency == nil { return 3 } return s.Concurrency.Value } func (s *AllocationStrategy) GetRefreshValue() uint32 { if s == nil || s.Refresh == nil { return 5 } return s.Refresh.Value } func (c *ReceiverConfig) GetEffectiveSniffingSettings() *SniffingConfig { if c.SniffingSettings != nil { return c.SniffingSettings } if len(c.DomainOverride) > 0 { var p []string for _, kd := range c.DomainOverride { switch kd { case KnownProtocols_HTTP: p = append(p, "http") case KnownProtocols_TLS: p = append(p, "tls") } } return &SniffingConfig{ Enabled: true, DestinationOverride: p, } } return nil } ================================================ FILE: app/proxyman/config.pb.go ================================================ package proxyman import ( net "github.com/v2fly/v2ray-core/v5/common/net" internet "github.com/v2fly/v2ray-core/v5/transport/internet" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type KnownProtocols int32 const ( KnownProtocols_HTTP KnownProtocols = 0 KnownProtocols_TLS KnownProtocols = 1 ) // Enum value maps for KnownProtocols. var ( KnownProtocols_name = map[int32]string{ 0: "HTTP", 1: "TLS", } KnownProtocols_value = map[string]int32{ "HTTP": 0, "TLS": 1, } ) func (x KnownProtocols) Enum() *KnownProtocols { p := new(KnownProtocols) *p = x return p } func (x KnownProtocols) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (KnownProtocols) Descriptor() protoreflect.EnumDescriptor { return file_app_proxyman_config_proto_enumTypes[0].Descriptor() } func (KnownProtocols) Type() protoreflect.EnumType { return &file_app_proxyman_config_proto_enumTypes[0] } func (x KnownProtocols) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use KnownProtocols.Descriptor instead. func (KnownProtocols) EnumDescriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{0} } type AllocationStrategy_Type int32 const ( // Always allocate all connection handlers. AllocationStrategy_Always AllocationStrategy_Type = 0 // Randomly allocate specific range of handlers. AllocationStrategy_Random AllocationStrategy_Type = 1 // External. Not supported yet. AllocationStrategy_External AllocationStrategy_Type = 2 ) // Enum value maps for AllocationStrategy_Type. var ( AllocationStrategy_Type_name = map[int32]string{ 0: "Always", 1: "Random", 2: "External", } AllocationStrategy_Type_value = map[string]int32{ "Always": 0, "Random": 1, "External": 2, } ) func (x AllocationStrategy_Type) Enum() *AllocationStrategy_Type { p := new(AllocationStrategy_Type) *p = x return p } func (x AllocationStrategy_Type) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (AllocationStrategy_Type) Descriptor() protoreflect.EnumDescriptor { return file_app_proxyman_config_proto_enumTypes[1].Descriptor() } func (AllocationStrategy_Type) Type() protoreflect.EnumType { return &file_app_proxyman_config_proto_enumTypes[1] } func (x AllocationStrategy_Type) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use AllocationStrategy_Type.Descriptor instead. func (AllocationStrategy_Type) EnumDescriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 0} } type SenderConfig_DomainStrategy int32 const ( SenderConfig_AS_IS SenderConfig_DomainStrategy = 0 SenderConfig_USE_IP SenderConfig_DomainStrategy = 1 SenderConfig_USE_IP4 SenderConfig_DomainStrategy = 2 SenderConfig_USE_IP6 SenderConfig_DomainStrategy = 3 ) // Enum value maps for SenderConfig_DomainStrategy. var ( SenderConfig_DomainStrategy_name = map[int32]string{ 0: "AS_IS", 1: "USE_IP", 2: "USE_IP4", 3: "USE_IP6", } SenderConfig_DomainStrategy_value = map[string]int32{ "AS_IS": 0, "USE_IP": 1, "USE_IP4": 2, "USE_IP6": 3, } ) func (x SenderConfig_DomainStrategy) Enum() *SenderConfig_DomainStrategy { p := new(SenderConfig_DomainStrategy) *p = x return p } func (x SenderConfig_DomainStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (SenderConfig_DomainStrategy) Descriptor() protoreflect.EnumDescriptor { return file_app_proxyman_config_proto_enumTypes[2].Descriptor() } func (SenderConfig_DomainStrategy) Type() protoreflect.EnumType { return &file_app_proxyman_config_proto_enumTypes[2] } func (x SenderConfig_DomainStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use SenderConfig_DomainStrategy.Descriptor instead. func (SenderConfig_DomainStrategy) EnumDescriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{6, 0} } type InboundConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *InboundConfig) Reset() { *x = InboundConfig{} mi := &file_app_proxyman_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *InboundConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*InboundConfig) ProtoMessage() {} func (x *InboundConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InboundConfig.ProtoReflect.Descriptor instead. func (*InboundConfig) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{0} } type AllocationStrategy struct { state protoimpl.MessageState `protogen:"open.v1"` Type AllocationStrategy_Type `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.proxyman.AllocationStrategy_Type" json:"type,omitempty"` // Number of handlers (ports) running in parallel. // Default value is 3 if unset. Concurrency *AllocationStrategy_AllocationStrategyConcurrency `protobuf:"bytes,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"` // Number of minutes before a handler is regenerated. // Default value is 5 if unset. Refresh *AllocationStrategy_AllocationStrategyRefresh `protobuf:"bytes,3,opt,name=refresh,proto3" json:"refresh,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AllocationStrategy) Reset() { *x = AllocationStrategy{} mi := &file_app_proxyman_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AllocationStrategy) String() string { return protoimpl.X.MessageStringOf(x) } func (*AllocationStrategy) ProtoMessage() {} func (x *AllocationStrategy) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AllocationStrategy.ProtoReflect.Descriptor instead. func (*AllocationStrategy) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{1} } func (x *AllocationStrategy) GetType() AllocationStrategy_Type { if x != nil { return x.Type } return AllocationStrategy_Always } func (x *AllocationStrategy) GetConcurrency() *AllocationStrategy_AllocationStrategyConcurrency { if x != nil { return x.Concurrency } return nil } func (x *AllocationStrategy) GetRefresh() *AllocationStrategy_AllocationStrategyRefresh { if x != nil { return x.Refresh } return nil } type SniffingConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Whether or not to enable content sniffing on an inbound connection. Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` // Override target destination if sniff'ed protocol is in the given list. // Supported values are "http", "tls", "fakedns". DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` // Whether should only try to sniff metadata without waiting for client input. // Can be used to support SMTP like protocol where server send the first message. MetadataOnly bool `protobuf:"varint,3,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SniffingConfig) Reset() { *x = SniffingConfig{} mi := &file_app_proxyman_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SniffingConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SniffingConfig) ProtoMessage() {} func (x *SniffingConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SniffingConfig.ProtoReflect.Descriptor instead. func (*SniffingConfig) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{2} } func (x *SniffingConfig) GetEnabled() bool { if x != nil { return x.Enabled } return false } func (x *SniffingConfig) GetDestinationOverride() []string { if x != nil { return x.DestinationOverride } return nil } func (x *SniffingConfig) GetMetadataOnly() bool { if x != nil { return x.MetadataOnly } return false } type ReceiverConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // PortRange specifies the ports which the Receiver should listen on. PortRange *net.PortRange `protobuf:"bytes,1,opt,name=port_range,json=portRange,proto3" json:"port_range,omitempty"` // Listen specifies the IP address that the Receiver should listen on. Listen *net.IPOrDomain `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"` AllocationStrategy *AllocationStrategy `protobuf:"bytes,3,opt,name=allocation_strategy,json=allocationStrategy,proto3" json:"allocation_strategy,omitempty"` StreamSettings *internet.StreamConfig `protobuf:"bytes,4,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` ReceiveOriginalDestination bool `protobuf:"varint,5,opt,name=receive_original_destination,json=receiveOriginalDestination,proto3" json:"receive_original_destination,omitempty"` // Override domains for the given protocol. // Deprecated. Use sniffing_settings. // // Deprecated: Marked as deprecated in app/proxyman/config.proto. DomainOverride []KnownProtocols `protobuf:"varint,7,rep,packed,name=domain_override,json=domainOverride,proto3,enum=v2ray.core.app.proxyman.KnownProtocols" json:"domain_override,omitempty"` SniffingSettings *SniffingConfig `protobuf:"bytes,8,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ReceiverConfig) Reset() { *x = ReceiverConfig{} mi := &file_app_proxyman_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ReceiverConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReceiverConfig) ProtoMessage() {} func (x *ReceiverConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReceiverConfig.ProtoReflect.Descriptor instead. func (*ReceiverConfig) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{3} } func (x *ReceiverConfig) GetPortRange() *net.PortRange { if x != nil { return x.PortRange } return nil } func (x *ReceiverConfig) GetListen() *net.IPOrDomain { if x != nil { return x.Listen } return nil } func (x *ReceiverConfig) GetAllocationStrategy() *AllocationStrategy { if x != nil { return x.AllocationStrategy } return nil } func (x *ReceiverConfig) GetStreamSettings() *internet.StreamConfig { if x != nil { return x.StreamSettings } return nil } func (x *ReceiverConfig) GetReceiveOriginalDestination() bool { if x != nil { return x.ReceiveOriginalDestination } return false } // Deprecated: Marked as deprecated in app/proxyman/config.proto. func (x *ReceiverConfig) GetDomainOverride() []KnownProtocols { if x != nil { return x.DomainOverride } return nil } func (x *ReceiverConfig) GetSniffingSettings() *SniffingConfig { if x != nil { return x.SniffingSettings } return nil } type InboundHandlerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` ReceiverSettings *anypb.Any `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings,proto3" json:"receiver_settings,omitempty"` ProxySettings *anypb.Any `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *InboundHandlerConfig) Reset() { *x = InboundHandlerConfig{} mi := &file_app_proxyman_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *InboundHandlerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*InboundHandlerConfig) ProtoMessage() {} func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InboundHandlerConfig.ProtoReflect.Descriptor instead. func (*InboundHandlerConfig) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{4} } func (x *InboundHandlerConfig) GetTag() string { if x != nil { return x.Tag } return "" } func (x *InboundHandlerConfig) GetReceiverSettings() *anypb.Any { if x != nil { return x.ReceiverSettings } return nil } func (x *InboundHandlerConfig) GetProxySettings() *anypb.Any { if x != nil { return x.ProxySettings } return nil } type OutboundConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OutboundConfig) Reset() { *x = OutboundConfig{} mi := &file_app_proxyman_config_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OutboundConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutboundConfig) ProtoMessage() {} func (x *OutboundConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutboundConfig.ProtoReflect.Descriptor instead. func (*OutboundConfig) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{5} } type SenderConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Send traffic through the given IP. Only IP is allowed. Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"` StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"` MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"` DomainStrategy SenderConfig_DomainStrategy `protobuf:"varint,5,opt,name=domain_strategy,json=domainStrategy,proto3,enum=v2ray.core.app.proxyman.SenderConfig_DomainStrategy" json:"domain_strategy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SenderConfig) Reset() { *x = SenderConfig{} mi := &file_app_proxyman_config_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SenderConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SenderConfig) ProtoMessage() {} func (x *SenderConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SenderConfig.ProtoReflect.Descriptor instead. func (*SenderConfig) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{6} } func (x *SenderConfig) GetVia() *net.IPOrDomain { if x != nil { return x.Via } return nil } func (x *SenderConfig) GetStreamSettings() *internet.StreamConfig { if x != nil { return x.StreamSettings } return nil } func (x *SenderConfig) GetProxySettings() *internet.ProxyConfig { if x != nil { return x.ProxySettings } return nil } func (x *SenderConfig) GetMultiplexSettings() *MultiplexingConfig { if x != nil { return x.MultiplexSettings } return nil } func (x *SenderConfig) GetDomainStrategy() SenderConfig_DomainStrategy { if x != nil { return x.DomainStrategy } return SenderConfig_AS_IS } type MultiplexingConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Whether or not Mux is enabled. Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` // Max number of concurrent connections that one Mux connection can handle. Concurrency uint32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MultiplexingConfig) Reset() { *x = MultiplexingConfig{} mi := &file_app_proxyman_config_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MultiplexingConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*MultiplexingConfig) ProtoMessage() {} func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MultiplexingConfig.ProtoReflect.Descriptor instead. func (*MultiplexingConfig) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{7} } func (x *MultiplexingConfig) GetEnabled() bool { if x != nil { return x.Enabled } return false } func (x *MultiplexingConfig) GetConcurrency() uint32 { if x != nil { return x.Concurrency } return 0 } type AllocationStrategy_AllocationStrategyConcurrency struct { state protoimpl.MessageState `protogen:"open.v1"` Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AllocationStrategy_AllocationStrategyConcurrency) Reset() { *x = AllocationStrategy_AllocationStrategyConcurrency{} mi := &file_app_proxyman_config_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AllocationStrategy_AllocationStrategyConcurrency) String() string { return protoimpl.X.MessageStringOf(x) } func (*AllocationStrategy_AllocationStrategyConcurrency) ProtoMessage() {} func (x *AllocationStrategy_AllocationStrategyConcurrency) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AllocationStrategy_AllocationStrategyConcurrency.ProtoReflect.Descriptor instead. func (*AllocationStrategy_AllocationStrategyConcurrency) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 0} } func (x *AllocationStrategy_AllocationStrategyConcurrency) GetValue() uint32 { if x != nil { return x.Value } return 0 } type AllocationStrategy_AllocationStrategyRefresh struct { state protoimpl.MessageState `protogen:"open.v1"` Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AllocationStrategy_AllocationStrategyRefresh) Reset() { *x = AllocationStrategy_AllocationStrategyRefresh{} mi := &file_app_proxyman_config_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AllocationStrategy_AllocationStrategyRefresh) String() string { return protoimpl.X.MessageStringOf(x) } func (*AllocationStrategy_AllocationStrategyRefresh) ProtoMessage() {} func (x *AllocationStrategy_AllocationStrategyRefresh) ProtoReflect() protoreflect.Message { mi := &file_app_proxyman_config_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AllocationStrategy_AllocationStrategyRefresh.ProtoReflect.Descriptor instead. func (*AllocationStrategy_AllocationStrategyRefresh) Descriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 1} } func (x *AllocationStrategy_AllocationStrategyRefresh) GetValue() uint32 { if x != nil { return x.Value } return 0 } var File_app_proxyman_config_proto protoreflect.FileDescriptor const file_app_proxyman_config_proto_rawDesc = "" + "\n" + "\x19app/proxyman/config.proto\x12\x17v2ray.core.app.proxyman\x1a\x18common/net/address.proto\x1a\x15common/net/port.proto\x1a\x1ftransport/internet/config.proto\x1a\x19google/protobuf/any.proto\"\x0f\n" + "\rInboundConfig\"\xc0\x03\n" + "\x12AllocationStrategy\x12D\n" + "\x04type\x18\x01 \x01(\x0e20.v2ray.core.app.proxyman.AllocationStrategy.TypeR\x04type\x12k\n" + "\vconcurrency\x18\x02 \x01(\v2I.v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrencyR\vconcurrency\x12_\n" + "\arefresh\x18\x03 \x01(\v2E.v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefreshR\arefresh\x1a5\n" + "\x1dAllocationStrategyConcurrency\x12\x14\n" + "\x05value\x18\x01 \x01(\rR\x05value\x1a1\n" + "\x19AllocationStrategyRefresh\x12\x14\n" + "\x05value\x18\x01 \x01(\rR\x05value\",\n" + "\x04Type\x12\n" + "\n" + "\x06Always\x10\x00\x12\n" + "\n" + "\x06Random\x10\x01\x12\f\n" + "\bExternal\x10\x02\"\x82\x01\n" + "\x0eSniffingConfig\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\x121\n" + "\x14destination_override\x18\x02 \x03(\tR\x13destinationOverride\x12#\n" + "\rmetadata_only\x18\x03 \x01(\bR\fmetadataOnly\"\xb4\x04\n" + "\x0eReceiverConfig\x12?\n" + "\n" + "port_range\x18\x01 \x01(\v2 .v2ray.core.common.net.PortRangeR\tportRange\x129\n" + "\x06listen\x18\x02 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\x06listen\x12\\\n" + "\x13allocation_strategy\x18\x03 \x01(\v2+.v2ray.core.app.proxyman.AllocationStrategyR\x12allocationStrategy\x12T\n" + "\x0fstream_settings\x18\x04 \x01(\v2+.v2ray.core.transport.internet.StreamConfigR\x0estreamSettings\x12@\n" + "\x1creceive_original_destination\x18\x05 \x01(\bR\x1areceiveOriginalDestination\x12T\n" + "\x0fdomain_override\x18\a \x03(\x0e2'.v2ray.core.app.proxyman.KnownProtocolsB\x02\x18\x01R\x0edomainOverride\x12T\n" + "\x11sniffing_settings\x18\b \x01(\v2'.v2ray.core.app.proxyman.SniffingConfigR\x10sniffingSettingsJ\x04\b\x06\x10\a\"\xa8\x01\n" + "\x14InboundHandlerConfig\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12A\n" + "\x11receiver_settings\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x10receiverSettings\x12;\n" + "\x0eproxy_settings\x18\x03 \x01(\v2\x14.google.protobuf.AnyR\rproxySettings\"\x10\n" + "\x0eOutboundConfig\"\xea\x03\n" + "\fSenderConfig\x123\n" + "\x03via\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\x03via\x12T\n" + "\x0fstream_settings\x18\x02 \x01(\v2+.v2ray.core.transport.internet.StreamConfigR\x0estreamSettings\x12Q\n" + "\x0eproxy_settings\x18\x03 \x01(\v2*.v2ray.core.transport.internet.ProxyConfigR\rproxySettings\x12Z\n" + "\x12multiplex_settings\x18\x04 \x01(\v2+.v2ray.core.app.proxyman.MultiplexingConfigR\x11multiplexSettings\x12]\n" + "\x0fdomain_strategy\x18\x05 \x01(\x0e24.v2ray.core.app.proxyman.SenderConfig.DomainStrategyR\x0edomainStrategy\"A\n" + "\x0eDomainStrategy\x12\t\n" + "\x05AS_IS\x10\x00\x12\n" + "\n" + "\x06USE_IP\x10\x01\x12\v\n" + "\aUSE_IP4\x10\x02\x12\v\n" + "\aUSE_IP6\x10\x03\"P\n" + "\x12MultiplexingConfig\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\x12 \n" + "\vconcurrency\x18\x02 \x01(\rR\vconcurrency*#\n" + "\x0eKnownProtocols\x12\b\n" + "\x04HTTP\x10\x00\x12\a\n" + "\x03TLS\x10\x01Bf\n" + "\x1bcom.v2ray.core.app.proxymanP\x01Z+github.com/v2fly/v2ray-core/v5/app/proxyman\xaa\x02\x17V2Ray.Core.App.Proxymanb\x06proto3" var ( file_app_proxyman_config_proto_rawDescOnce sync.Once file_app_proxyman_config_proto_rawDescData []byte ) func file_app_proxyman_config_proto_rawDescGZIP() []byte { file_app_proxyman_config_proto_rawDescOnce.Do(func() { file_app_proxyman_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_proxyman_config_proto_rawDesc), len(file_app_proxyman_config_proto_rawDesc))) }) return file_app_proxyman_config_proto_rawDescData } var file_app_proxyman_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_app_proxyman_config_proto_goTypes = []any{ (KnownProtocols)(0), // 0: v2ray.core.app.proxyman.KnownProtocols (AllocationStrategy_Type)(0), // 1: v2ray.core.app.proxyman.AllocationStrategy.Type (SenderConfig_DomainStrategy)(0), // 2: v2ray.core.app.proxyman.SenderConfig.DomainStrategy (*InboundConfig)(nil), // 3: v2ray.core.app.proxyman.InboundConfig (*AllocationStrategy)(nil), // 4: v2ray.core.app.proxyman.AllocationStrategy (*SniffingConfig)(nil), // 5: v2ray.core.app.proxyman.SniffingConfig (*ReceiverConfig)(nil), // 6: v2ray.core.app.proxyman.ReceiverConfig (*InboundHandlerConfig)(nil), // 7: v2ray.core.app.proxyman.InboundHandlerConfig (*OutboundConfig)(nil), // 8: v2ray.core.app.proxyman.OutboundConfig (*SenderConfig)(nil), // 9: v2ray.core.app.proxyman.SenderConfig (*MultiplexingConfig)(nil), // 10: v2ray.core.app.proxyman.MultiplexingConfig (*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 11: v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency (*AllocationStrategy_AllocationStrategyRefresh)(nil), // 12: v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh (*net.PortRange)(nil), // 13: v2ray.core.common.net.PortRange (*net.IPOrDomain)(nil), // 14: v2ray.core.common.net.IPOrDomain (*internet.StreamConfig)(nil), // 15: v2ray.core.transport.internet.StreamConfig (*anypb.Any)(nil), // 16: google.protobuf.Any (*internet.ProxyConfig)(nil), // 17: v2ray.core.transport.internet.ProxyConfig } var file_app_proxyman_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.app.proxyman.AllocationStrategy.type:type_name -> v2ray.core.app.proxyman.AllocationStrategy.Type 11, // 1: v2ray.core.app.proxyman.AllocationStrategy.concurrency:type_name -> v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency 12, // 2: v2ray.core.app.proxyman.AllocationStrategy.refresh:type_name -> v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh 13, // 3: v2ray.core.app.proxyman.ReceiverConfig.port_range:type_name -> v2ray.core.common.net.PortRange 14, // 4: v2ray.core.app.proxyman.ReceiverConfig.listen:type_name -> v2ray.core.common.net.IPOrDomain 4, // 5: v2ray.core.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> v2ray.core.app.proxyman.AllocationStrategy 15, // 6: v2ray.core.app.proxyman.ReceiverConfig.stream_settings:type_name -> v2ray.core.transport.internet.StreamConfig 0, // 7: v2ray.core.app.proxyman.ReceiverConfig.domain_override:type_name -> v2ray.core.app.proxyman.KnownProtocols 5, // 8: v2ray.core.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> v2ray.core.app.proxyman.SniffingConfig 16, // 9: v2ray.core.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> google.protobuf.Any 16, // 10: v2ray.core.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> google.protobuf.Any 14, // 11: v2ray.core.app.proxyman.SenderConfig.via:type_name -> v2ray.core.common.net.IPOrDomain 15, // 12: v2ray.core.app.proxyman.SenderConfig.stream_settings:type_name -> v2ray.core.transport.internet.StreamConfig 17, // 13: v2ray.core.app.proxyman.SenderConfig.proxy_settings:type_name -> v2ray.core.transport.internet.ProxyConfig 10, // 14: v2ray.core.app.proxyman.SenderConfig.multiplex_settings:type_name -> v2ray.core.app.proxyman.MultiplexingConfig 2, // 15: v2ray.core.app.proxyman.SenderConfig.domain_strategy:type_name -> v2ray.core.app.proxyman.SenderConfig.DomainStrategy 16, // [16:16] is the sub-list for method output_type 16, // [16:16] is the sub-list for method input_type 16, // [16:16] is the sub-list for extension type_name 16, // [16:16] is the sub-list for extension extendee 0, // [0:16] is the sub-list for field type_name } func init() { file_app_proxyman_config_proto_init() } func file_app_proxyman_config_proto_init() { if File_app_proxyman_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proxyman_config_proto_rawDesc), len(file_app_proxyman_config_proto_rawDesc)), NumEnums: 3, NumMessages: 10, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_proxyman_config_proto_goTypes, DependencyIndexes: file_app_proxyman_config_proto_depIdxs, EnumInfos: file_app_proxyman_config_proto_enumTypes, MessageInfos: file_app_proxyman_config_proto_msgTypes, }.Build() File_app_proxyman_config_proto = out.File file_app_proxyman_config_proto_goTypes = nil file_app_proxyman_config_proto_depIdxs = nil } ================================================ FILE: app/proxyman/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.proxyman; option csharp_namespace = "V2Ray.Core.App.Proxyman"; option go_package = "github.com/v2fly/v2ray-core/v5/app/proxyman"; option java_package = "com.v2ray.core.app.proxyman"; option java_multiple_files = true; import "common/net/address.proto"; import "common/net/port.proto"; import "transport/internet/config.proto"; import "google/protobuf/any.proto"; message InboundConfig {} message AllocationStrategy { enum Type { // Always allocate all connection handlers. Always = 0; // Randomly allocate specific range of handlers. Random = 1; // External. Not supported yet. External = 2; } Type type = 1; message AllocationStrategyConcurrency { uint32 value = 1; } // Number of handlers (ports) running in parallel. // Default value is 3 if unset. AllocationStrategyConcurrency concurrency = 2; message AllocationStrategyRefresh { uint32 value = 1; } // Number of minutes before a handler is regenerated. // Default value is 5 if unset. AllocationStrategyRefresh refresh = 3; } enum KnownProtocols { HTTP = 0; TLS = 1; } message SniffingConfig { // Whether or not to enable content sniffing on an inbound connection. bool enabled = 1; // Override target destination if sniff'ed protocol is in the given list. // Supported values are "http", "tls", "fakedns". repeated string destination_override = 2; // Whether should only try to sniff metadata without waiting for client input. // Can be used to support SMTP like protocol where server send the first message. bool metadata_only = 3; } message ReceiverConfig { // PortRange specifies the ports which the Receiver should listen on. v2ray.core.common.net.PortRange port_range = 1; // Listen specifies the IP address that the Receiver should listen on. v2ray.core.common.net.IPOrDomain listen = 2; AllocationStrategy allocation_strategy = 3; v2ray.core.transport.internet.StreamConfig stream_settings = 4; bool receive_original_destination = 5; reserved 6; // Override domains for the given protocol. // Deprecated. Use sniffing_settings. repeated KnownProtocols domain_override = 7 [deprecated = true]; SniffingConfig sniffing_settings = 8; } message InboundHandlerConfig { string tag = 1; google.protobuf.Any receiver_settings = 2; google.protobuf.Any proxy_settings = 3; } message OutboundConfig {} message SenderConfig { enum DomainStrategy { AS_IS = 0; USE_IP = 1; USE_IP4 = 2; USE_IP6 = 3; } // Send traffic through the given IP. Only IP is allowed. v2ray.core.common.net.IPOrDomain via = 1; v2ray.core.transport.internet.StreamConfig stream_settings = 2; v2ray.core.transport.internet.ProxyConfig proxy_settings = 3; MultiplexingConfig multiplex_settings = 4; DomainStrategy domain_strategy = 5; } message MultiplexingConfig { // Whether or not Mux is enabled. bool enabled = 1; // Max number of concurrent connections that one Mux connection can handle. uint32 concurrency = 2; } ================================================ FILE: app/proxyman/inbound/always.go ================================================ package inbound import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/stats" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) { var uplinkCounter stats.Counter var downlinkCounter stats.Counter policy := v.GetFeature(policy.ManagerType()).(policy.Manager) if len(tag) > 0 && policy.ForSystem().Stats.InboundUplink { statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) name := "inbound>>>" + tag + ">>>traffic>>>uplink" c, _ := stats.GetOrRegisterCounter(statsManager, name) if c != nil { uplinkCounter = c } } if len(tag) > 0 && policy.ForSystem().Stats.InboundDownlink { statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) name := "inbound>>>" + tag + ">>>traffic>>>downlink" c, _ := stats.GetOrRegisterCounter(statsManager, name) if c != nil { downlinkCounter = c } } return uplinkCounter, downlinkCounter } type AlwaysOnInboundHandler struct { proxy proxy.Inbound workers []worker mux *mux.Server tag string } func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) { rawProxy, err := common.CreateObject(ctx, proxyConfig) if err != nil { return nil, err } p, ok := rawProxy.(proxy.Inbound) if !ok { return nil, newError("not an inbound proxy.") } h := &AlwaysOnInboundHandler{ proxy: p, mux: mux.NewServer(ctx), tag: tag, } uplinkCounter, downlinkCounter := getStatCounter(core.MustFromContext(ctx), tag) nl := p.Network() pr := receiverConfig.PortRange address := receiverConfig.Listen.AsAddress() if address == nil { address = net.AnyIP } mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings) if err != nil { return nil, newError("failed to parse stream config").Base(err).AtWarning() } if receiverConfig.ReceiveOriginalDestination { if mss.SocketSettings == nil { mss.SocketSettings = &internet.SocketConfig{} } if mss.SocketSettings.Tproxy == internet.SocketConfig_Off { mss.SocketSettings.Tproxy = internet.SocketConfig_Redirect } mss.SocketSettings.ReceiveOriginalDestAddress = true } if pr == nil { if net.HasNetwork(nl, net.Network_UNIX) { newError("creating unix domain socket worker on ", address).AtDebug().WriteToLog() worker := &dsWorker{ address: address, proxy: p, stream: mss, tag: tag, dispatcher: h.mux, sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(), uplinkCounter: uplinkCounter, downlinkCounter: downlinkCounter, ctx: ctx, } h.workers = append(h.workers, worker) } } if pr != nil { for port := pr.From; port <= pr.To; port++ { if net.HasNetwork(nl, net.Network_TCP) { newError("creating stream worker on ", address, ":", port).AtDebug().WriteToLog() worker := &tcpWorker{ address: address, port: net.Port(port), proxy: p, stream: mss, recvOrigDest: receiverConfig.ReceiveOriginalDestination, tag: tag, dispatcher: h.mux, sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(), uplinkCounter: uplinkCounter, downlinkCounter: downlinkCounter, ctx: ctx, } h.workers = append(h.workers, worker) } if net.HasNetwork(nl, net.Network_UDP) { worker := &udpWorker{ ctx: ctx, tag: tag, proxy: p, address: address, port: net.Port(port), dispatcher: h.mux, sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(), uplinkCounter: uplinkCounter, downlinkCounter: downlinkCounter, stream: mss, } h.workers = append(h.workers, worker) } } } return h, nil } // Start implements common.Runnable. func (h *AlwaysOnInboundHandler) Start() error { for _, worker := range h.workers { if err := worker.Start(); err != nil { return err } } return nil } // Close implements common.Closable. func (h *AlwaysOnInboundHandler) Close() error { var errs []error for _, worker := range h.workers { errs = append(errs, worker.Close()) } errs = append(errs, h.mux.Close()) if err := errors.Combine(errs...); err != nil { return newError("failed to close all resources").Base(err) } return nil } func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) { if len(h.workers) == 0 { return nil, 0, 0 } w := h.workers[dice.Roll(len(h.workers))] return w.Proxy(), w.Port(), 9999 } func (h *AlwaysOnInboundHandler) Tag() string { return h.tag } func (h *AlwaysOnInboundHandler) GetInbound() proxy.Inbound { return h.proxy } ================================================ FILE: app/proxyman/inbound/dynamic.go ================================================ package inbound import ( "context" "sync" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type DynamicInboundHandler struct { tag string v *core.Instance proxyConfig interface{} receiverConfig *proxyman.ReceiverConfig streamSettings *internet.MemoryStreamConfig portMutex sync.Mutex portsInUse map[net.Port]bool workerMutex sync.RWMutex worker []worker lastRefresh time.Time mux *mux.Server task *task.Periodic ctx context.Context } func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) { v := core.MustFromContext(ctx) h := &DynamicInboundHandler{ tag: tag, proxyConfig: proxyConfig, receiverConfig: receiverConfig, portsInUse: make(map[net.Port]bool), mux: mux.NewServer(ctx), v: v, ctx: ctx, } mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings) if err != nil { return nil, newError("failed to parse stream settings").Base(err).AtWarning() } if receiverConfig.ReceiveOriginalDestination { if mss.SocketSettings == nil { mss.SocketSettings = &internet.SocketConfig{} } if mss.SocketSettings.Tproxy == internet.SocketConfig_Off { mss.SocketSettings.Tproxy = internet.SocketConfig_Redirect } mss.SocketSettings.ReceiveOriginalDestAddress = true } h.streamSettings = mss h.task = &task.Periodic{ Interval: time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()), Execute: h.refresh, } return h, nil } func (h *DynamicInboundHandler) allocatePort() net.Port { from := int(h.receiverConfig.PortRange.From) delta := int(h.receiverConfig.PortRange.To) - from + 1 h.portMutex.Lock() defer h.portMutex.Unlock() for { r := dice.Roll(delta) port := net.Port(from + r) _, used := h.portsInUse[port] if !used { h.portsInUse[port] = true return port } } } func (h *DynamicInboundHandler) closeWorkers(workers []worker) { ports2Del := make([]net.Port, len(workers)) for idx, worker := range workers { ports2Del[idx] = worker.Port() if err := worker.Close(); err != nil { newError("failed to close worker").Base(err).WriteToLog() } } h.portMutex.Lock() for _, port := range ports2Del { delete(h.portsInUse, port) } h.portMutex.Unlock() } func (h *DynamicInboundHandler) refresh() error { h.lastRefresh = time.Now() timeout := time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()) * 2 concurrency := h.receiverConfig.AllocationStrategy.GetConcurrencyValue() workers := make([]worker, 0, concurrency) address := h.receiverConfig.Listen.AsAddress() if address == nil { address = net.AnyIP } uplinkCounter, downlinkCounter := getStatCounter(h.v, h.tag) for i := uint32(0); i < concurrency; i++ { port := h.allocatePort() rawProxy, err := core.CreateObject(h.v, h.proxyConfig) if err != nil { newError("failed to create proxy instance").Base(err).AtWarning().WriteToLog() continue } p := rawProxy.(proxy.Inbound) nl := p.Network() if net.HasNetwork(nl, net.Network_TCP) { worker := &tcpWorker{ tag: h.tag, address: address, port: port, proxy: p, stream: h.streamSettings, recvOrigDest: h.receiverConfig.ReceiveOriginalDestination, dispatcher: h.mux, sniffingConfig: h.receiverConfig.GetEffectiveSniffingSettings(), uplinkCounter: uplinkCounter, downlinkCounter: downlinkCounter, ctx: h.ctx, } if err := worker.Start(); err != nil { newError("failed to create TCP worker").Base(err).AtWarning().WriteToLog() continue } workers = append(workers, worker) } if net.HasNetwork(nl, net.Network_UDP) { worker := &udpWorker{ ctx: h.ctx, tag: h.tag, proxy: p, address: address, port: port, dispatcher: h.mux, sniffingConfig: h.receiverConfig.GetEffectiveSniffingSettings(), uplinkCounter: uplinkCounter, downlinkCounter: downlinkCounter, stream: h.streamSettings, } if err := worker.Start(); err != nil { newError("failed to create UDP worker").Base(err).AtWarning().WriteToLog() continue } workers = append(workers, worker) } } h.workerMutex.Lock() h.worker = workers h.workerMutex.Unlock() time.AfterFunc(timeout, func() { h.closeWorkers(workers) }) return nil } func (h *DynamicInboundHandler) Start() error { return h.task.Start() } func (h *DynamicInboundHandler) Close() error { return h.task.Close() } func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) { h.workerMutex.RLock() defer h.workerMutex.RUnlock() if len(h.worker) == 0 { return nil, 0, 0 } w := h.worker[dice.Roll(len(h.worker))] expire := h.receiverConfig.AllocationStrategy.GetRefreshValue() - uint32(time.Since(h.lastRefresh)/time.Minute) return w.Proxy(), w.Port(), int(expire) } func (h *DynamicInboundHandler) Tag() string { return h.tag } ================================================ FILE: app/proxyman/inbound/errors.generated.go ================================================ package inbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/proxyman/inbound/inbound.go ================================================ package inbound //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "sync" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/inbound" ) // Manager is to manage all inbound handlers. type Manager struct { ctx context.Context access sync.RWMutex untaggedHandler []inbound.Handler taggedHandlers map[string]inbound.Handler running bool } // New returns a new Manager for inbound handlers. func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) { m := &Manager{ ctx: ctx, taggedHandlers: make(map[string]inbound.Handler), } return m, nil } // Type implements common.HasType. func (*Manager) Type() interface{} { return inbound.ManagerType() } // AddHandler implements inbound.Manager. func (m *Manager) AddHandler(ctx context.Context, handler inbound.Handler) error { m.access.Lock() defer m.access.Unlock() tag := handler.Tag() if len(tag) > 0 { m.taggedHandlers[tag] = handler } else { m.untaggedHandler = append(m.untaggedHandler, handler) } if m.running { return handler.Start() } return nil } // GetHandler implements inbound.Manager. func (m *Manager) GetHandler(ctx context.Context, tag string) (inbound.Handler, error) { m.access.RLock() defer m.access.RUnlock() handler, found := m.taggedHandlers[tag] if !found { return nil, newError("handler not found: ", tag) } return handler, nil } // RemoveHandler implements inbound.Manager. func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { if tag == "" { return common.ErrNoClue } m.access.Lock() defer m.access.Unlock() if handler, found := m.taggedHandlers[tag]; found { if err := handler.Close(); err != nil { newError("failed to close handler ", tag).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } delete(m.taggedHandlers, tag) return nil } return common.ErrNoClue } // Start implements common.Runnable. func (m *Manager) Start() error { m.access.Lock() defer m.access.Unlock() m.running = true for _, handler := range m.taggedHandlers { if err := handler.Start(); err != nil { return err } } for _, handler := range m.untaggedHandler { if err := handler.Start(); err != nil { return err } } return nil } // Close implements common.Closable. func (m *Manager) Close() error { m.access.Lock() defer m.access.Unlock() m.running = false var errors []interface{} for _, handler := range m.taggedHandlers { if err := handler.Close(); err != nil { errors = append(errors, err) } } for _, handler := range m.untaggedHandler { if err := handler.Close(); err != nil { errors = append(errors, err) } } if len(errors) > 0 { return newError("failed to close all handlers").Base(newError(serial.Concat(errors...))) } return nil } // NewHandler creates a new inbound.Handler based on the given config. func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (inbound.Handler, error) { rawReceiverSettings, err := serial.GetInstanceOf(config.ReceiverSettings) if err != nil { return nil, err } proxySettings, err := serial.GetInstanceOf(config.ProxySettings) if err != nil { return nil, err } tag := config.Tag receiverSettings, ok := rawReceiverSettings.(*proxyman.ReceiverConfig) if !ok { return nil, newError("not a ReceiverConfig").AtError() } streamSettings := receiverSettings.StreamSettings if streamSettings != nil && streamSettings.SocketSettings != nil { ctx = session.ContextWithSockopt(ctx, &session.Sockopt{ Mark: streamSettings.SocketSettings.Mark, }) } allocStrategy := receiverSettings.AllocationStrategy if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always { return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings) } if allocStrategy.Type == proxyman.AllocationStrategy_Random { return NewDynamicInboundHandler(ctx, tag, receiverSettings, proxySettings) } return nil, newError("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError() } func init() { common.Must(common.RegisterConfig((*proxyman.InboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*proxyman.InboundConfig)) })) common.Must(common.RegisterConfig((*core.InboundHandlerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewHandler(ctx, config.(*core.InboundHandlerConfig)) })) } ================================================ FILE: app/proxyman/inbound/worker.go ================================================ package inbound import ( "context" "sync" "sync/atomic" "time" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/features/stats" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tcp" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) type worker interface { Start() error Close() error Port() net.Port Proxy() proxy.Inbound } type tcpWorker struct { address net.Address port net.Port proxy proxy.Inbound stream *internet.MemoryStreamConfig recvOrigDest bool tag string dispatcher routing.Dispatcher sniffingConfig *proxyman.SniffingConfig uplinkCounter stats.Counter downlinkCounter stats.Counter hub internet.Listener ctx context.Context } func getTProxyType(s *internet.MemoryStreamConfig) internet.SocketConfig_TProxyMode { if s == nil || s.SocketSettings == nil { return internet.SocketConfig_Off } return s.SocketSettings.Tproxy } func (w *tcpWorker) callback(conn internet.Connection) { ctx, cancel := context.WithCancel(w.ctx) sid := session.NewID() ctx = session.ContextWithID(ctx, sid) if w.recvOrigDest { var dest net.Destination switch getTProxyType(w.stream) { case internet.SocketConfig_Redirect: d, err := tcp.GetOriginalDestination(conn) if err != nil { newError("failed to get original destination").Base(err).WriteToLog(session.ExportIDToError(ctx)) } else { dest = d } case internet.SocketConfig_TProxy: dest = net.DestinationFromAddr(conn.LocalAddr()) } if dest.IsValid() { ctx = session.ContextWithOutbound(ctx, &session.Outbound{ Target: dest, }) } } ctx = session.ContextWithInbound(ctx, &session.Inbound{ Source: net.DestinationFromAddr(conn.RemoteAddr()), Gateway: net.TCPDestination(w.address, w.port), Tag: w.tag, }) content := new(session.Content) if w.sniffingConfig != nil { content.SniffingRequest.Enabled = w.sniffingConfig.Enabled content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly } ctx = session.ContextWithContent(ctx, content) if w.uplinkCounter != nil || w.downlinkCounter != nil { conn = &internet.StatCouterConnection{ Connection: conn, ReadCounter: w.uplinkCounter, WriteCounter: w.downlinkCounter, } } if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil { newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } cancel() if err := conn.Close(); err != nil { newError("failed to close connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } func (w *tcpWorker) Proxy() proxy.Inbound { return w.proxy } func (w *tcpWorker) Start() error { ctx := w.ctx proxyEnvironment := envctx.EnvironmentFromContext(w.ctx).(environment.ProxyEnvironment) transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("transport") if err != nil { return newError("unable to narrow environment to transport").Base(err) } ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment) hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn internet.Connection) { go w.callback(conn) }) if err != nil { return newError("failed to listen TCP on ", w.port).AtWarning().Base(err) } w.hub = hub return nil } func (w *tcpWorker) Close() error { var errors []interface{} if w.hub != nil { if err := common.Close(w.hub); err != nil { errors = append(errors, err) } if err := common.Close(w.proxy); err != nil { errors = append(errors, err) } } if len(errors) > 0 { return newError("failed to close all resources").Base(newError(serial.Concat(errors...))) } return nil } func (w *tcpWorker) Port() net.Port { return w.port } type udpConn struct { lastActivityTime int64 // in seconds reader buf.Reader writer buf.Writer output func([]byte) (int, error) remote net.Addr local net.Addr done *done.Instance uplink stats.Counter downlink stats.Counter inactive bool } func (c *udpConn) setInactive() { c.inactive = true } func (c *udpConn) updateActivity() { atomic.StoreInt64(&c.lastActivityTime, time.Now().Unix()) } // ReadMultiBuffer implements buf.Reader func (c *udpConn) ReadMultiBuffer() (buf.MultiBuffer, error) { mb, err := c.reader.ReadMultiBuffer() if err != nil { return nil, err } c.updateActivity() if c.uplink != nil { c.uplink.Add(int64(mb.Len())) } return mb, nil } func (c *udpConn) Read(buf []byte) (int, error) { panic("not implemented") } // Write implements io.Writer. func (c *udpConn) Write(buf []byte) (int, error) { n, err := c.output(buf) if c.downlink != nil { c.downlink.Add(int64(n)) } if err == nil { c.updateActivity() } return n, err } func (c *udpConn) Close() error { common.Must(c.done.Close()) common.Must(common.Close(c.writer)) return nil } func (c *udpConn) RemoteAddr() net.Addr { return c.remote } func (c *udpConn) LocalAddr() net.Addr { return c.local } func (*udpConn) SetDeadline(time.Time) error { return nil } func (*udpConn) SetReadDeadline(time.Time) error { return nil } func (*udpConn) SetWriteDeadline(time.Time) error { return nil } type connID struct { src net.Destination dest net.Destination } type udpWorker struct { sync.RWMutex proxy proxy.Inbound hub *udp.Hub address net.Address port net.Port tag string stream *internet.MemoryStreamConfig dispatcher routing.Dispatcher sniffingConfig *proxyman.SniffingConfig uplinkCounter stats.Counter downlinkCounter stats.Counter checker *task.Periodic activeConn map[connID]*udpConn ctx context.Context } func (w *udpWorker) getConnection(id connID) (*udpConn, bool) { w.Lock() defer w.Unlock() if conn, found := w.activeConn[id]; found && !conn.done.Done() { return conn, true } pReader, pWriter := pipe.New(pipe.DiscardOverflow(), pipe.WithSizeLimit(16*1024)) conn := &udpConn{ reader: pReader, writer: pWriter, output: func(b []byte) (int, error) { return w.hub.WriteTo(b, id.src) }, remote: &net.UDPAddr{ IP: id.src.Address.IP(), Port: int(id.src.Port), }, local: &net.UDPAddr{ IP: w.address.IP(), Port: int(w.port), }, done: done.New(), uplink: w.uplinkCounter, downlink: w.downlinkCounter, } w.activeConn[id] = conn conn.updateActivity() return conn, false } func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) { id := connID{ src: source, } if originalDest.IsValid() { id.dest = originalDest } conn, existing := w.getConnection(id) // payload will be discarded in pipe is full. conn.writer.WriteMultiBuffer(buf.MultiBuffer{b}) if !existing { common.Must(w.checker.Start()) go func() { ctx := w.ctx sid := session.NewID() ctx = session.ContextWithID(ctx, sid) if originalDest.IsValid() { ctx = session.ContextWithOutbound(ctx, &session.Outbound{ Target: originalDest, }) } ctx = session.ContextWithInbound(ctx, &session.Inbound{ Source: source, Gateway: net.UDPDestination(w.address, w.port), Tag: w.tag, }) content := new(session.Content) if w.sniffingConfig != nil { content.SniffingRequest.Enabled = w.sniffingConfig.Enabled content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly } ctx = session.ContextWithContent(ctx, content) if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil { newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } conn.Close() // conn not removed by checker TODO may be lock worker here is better if !conn.inactive { conn.setInactive() w.removeConn(id) } }() } } func (w *udpWorker) removeConn(id connID) { w.Lock() delete(w.activeConn, id) w.Unlock() } func (w *udpWorker) handlePackets() { receive := w.hub.Receive() for payload := range receive { w.callback(payload.Payload, payload.Source, payload.Target) } } func (w *udpWorker) clean() error { nowSec := time.Now().Unix() w.Lock() defer w.Unlock() if len(w.activeConn) == 0 { return newError("no more connections. stopping...") } for addr, conn := range w.activeConn { if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 8 { // TODO Timeout too small if !conn.inactive { conn.setInactive() delete(w.activeConn, addr) } conn.Close() } } if len(w.activeConn) == 0 { w.activeConn = make(map[connID]*udpConn, 16) } return nil } func (w *udpWorker) Start() error { w.activeConn = make(map[connID]*udpConn, 16) ctx := context.Background() proxyEnvironment := envctx.EnvironmentFromContext(w.ctx).(environment.ProxyEnvironment) transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("transport") if err != nil { return newError("unable to narrow environment to transport").Base(err) } ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment) h, err := udp.ListenUDP(ctx, w.address, w.port, w.stream, udp.HubCapacity(256)) if err != nil { return err } w.checker = &task.Periodic{ Interval: time.Second * 16, Execute: w.clean, } w.hub = h go w.handlePackets() return nil } func (w *udpWorker) Close() error { w.Lock() defer w.Unlock() var errors []interface{} if w.hub != nil { if err := w.hub.Close(); err != nil { errors = append(errors, err) } } if w.checker != nil { if err := w.checker.Close(); err != nil { errors = append(errors, err) } } if err := common.Close(w.proxy); err != nil { errors = append(errors, err) } if len(errors) > 0 { return newError("failed to close all resources").Base(newError(serial.Concat(errors...))) } return nil } func (w *udpWorker) Port() net.Port { return w.port } func (w *udpWorker) Proxy() proxy.Inbound { return w.proxy } type dsWorker struct { address net.Address proxy proxy.Inbound stream *internet.MemoryStreamConfig tag string dispatcher routing.Dispatcher sniffingConfig *proxyman.SniffingConfig uplinkCounter stats.Counter downlinkCounter stats.Counter hub internet.Listener ctx context.Context } func (w *dsWorker) callback(conn internet.Connection) { ctx, cancel := context.WithCancel(w.ctx) sid := session.NewID() ctx = session.ContextWithID(ctx, sid) ctx = session.ContextWithInbound(ctx, &session.Inbound{ Source: net.DestinationFromAddr(conn.RemoteAddr()), Gateway: net.UnixDestination(w.address), Tag: w.tag, }) content := new(session.Content) if w.sniffingConfig != nil { content.SniffingRequest.Enabled = w.sniffingConfig.Enabled content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly } ctx = session.ContextWithContent(ctx, content) if w.uplinkCounter != nil || w.downlinkCounter != nil { conn = &internet.StatCouterConnection{ Connection: conn, ReadCounter: w.uplinkCounter, WriteCounter: w.downlinkCounter, } } if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher); err != nil { newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } cancel() if err := conn.Close(); err != nil { newError("failed to close connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } func (w *dsWorker) Proxy() proxy.Inbound { return w.proxy } func (w *dsWorker) Port() net.Port { return net.Port(0) } func (w *dsWorker) Start() error { ctx := context.Background() proxyEnvironment := envctx.EnvironmentFromContext(w.ctx).(environment.ProxyEnvironment) transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("transport") if err != nil { return newError("unable to narrow environment to transport").Base(err) } ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment) hub, err := internet.ListenUnix(ctx, w.address, w.stream, func(conn internet.Connection) { go w.callback(conn) }) if err != nil { return newError("failed to listen Unix Domain Socket on ", w.address).AtWarning().Base(err) } w.hub = hub return nil } func (w *dsWorker) Close() error { var errors []interface{} if w.hub != nil { if err := common.Close(w.hub); err != nil { errors = append(errors, err) } if err := common.Close(w.proxy); err != nil { errors = append(errors, err) } } if len(errors) > 0 { return newError("failed to close all resources").Base(newError(serial.Concat(errors...))) } return nil } ================================================ FILE: app/proxyman/outbound/errors.generated.go ================================================ package outbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/proxyman/outbound/handler.go ================================================ package outbound import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/stats" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/security" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) { var uplinkCounter stats.Counter var downlinkCounter stats.Counter policy := v.GetFeature(policy.ManagerType()).(policy.Manager) if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink { statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) name := "outbound>>>" + tag + ">>>traffic>>>uplink" c, _ := stats.GetOrRegisterCounter(statsManager, name) if c != nil { uplinkCounter = c } } if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink { statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) name := "outbound>>>" + tag + ">>>traffic>>>downlink" c, _ := stats.GetOrRegisterCounter(statsManager, name) if c != nil { downlinkCounter = c } } return uplinkCounter, downlinkCounter } // Handler is an implements of outbound.Handler. type Handler struct { ctx context.Context tag string senderSettings *proxyman.SenderConfig streamSettings *internet.MemoryStreamConfig proxy proxy.Outbound outboundManager outbound.Manager mux *mux.ClientManager uplinkCounter stats.Counter downlinkCounter stats.Counter dns dns.Client } // NewHandler create a new Handler based on the given configuration. func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbound.Handler, error) { v := core.MustFromContext(ctx) uplinkCounter, downlinkCounter := getStatCounter(v, config.Tag) h := &Handler{ ctx: ctx, tag: config.Tag, outboundManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager), uplinkCounter: uplinkCounter, downlinkCounter: downlinkCounter, } if config.SenderSettings != nil { senderSettings, err := serial.GetInstanceOf(config.SenderSettings) if err != nil { return nil, err } switch s := senderSettings.(type) { case *proxyman.SenderConfig: h.senderSettings = s mss, err := internet.ToMemoryStreamConfig(s.StreamSettings) if err != nil { return nil, newError("failed to parse stream settings").Base(err).AtWarning() } h.streamSettings = mss default: return nil, newError("settings is not SenderConfig") } } proxyConfig, err := serial.GetInstanceOf(config.ProxySettings) if err != nil { return nil, err } rawProxyHandler, err := common.CreateObject(ctx, proxyConfig) if err != nil { return nil, err } proxyHandler, ok := rawProxyHandler.(proxy.Outbound) if !ok { return nil, newError("not an outbound handler") } if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil { config := h.senderSettings.MultiplexSettings if config.Concurrency < 1 || config.Concurrency > 1024 { return nil, newError("invalid mux concurrency: ", config.Concurrency).AtWarning() } h.mux = &mux.ClientManager{ Enabled: h.senderSettings.MultiplexSettings.Enabled, Picker: &mux.IncrementalWorkerPicker{ Factory: mux.NewDialingWorkerFactory( ctx, proxyHandler, h, mux.ClientStrategy{ MaxConcurrency: config.Concurrency, MaxConnection: 128, }, ), }, } } if h.senderSettings != nil && h.senderSettings.DomainStrategy != proxyman.SenderConfig_AS_IS { err := core.RequireFeatures(ctx, func(d dns.Client) error { h.dns = d return nil }) if err != nil { return nil, err } } h.proxy = proxyHandler return h, nil } // Tag implements outbound.Handler. func (h *Handler) Tag() string { return h.tag } // Dispatch implements proxy.Outbound.Dispatch. func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { if h.senderSettings != nil && h.senderSettings.DomainStrategy != proxyman.SenderConfig_AS_IS { outbound := session.OutboundFromContext(ctx) if outbound == nil { outbound = new(session.Outbound) ctx = session.ContextWithOutbound(ctx, outbound) } if outbound.Target.Address != nil && outbound.Target.Address.Family().IsDomain() { if addr := h.resolveIP(ctx, outbound.Target.Address.Domain(), h.Address()); addr != nil { outbound.Target.Address = addr } } } if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) { if err := h.mux.Dispatch(ctx, link); err != nil { err := newError("failed to process mux outbound traffic").Base(err) session.SubmitOutboundErrorToOriginator(ctx, err) err.WriteToLog(session.ExportIDToError(ctx)) common.Interrupt(link.Writer) } } else { if err := h.proxy.Process(ctx, link, h); err != nil { // Ensure outbound ray is properly closed. err := newError("failed to process outbound traffic").Base(err) session.SubmitOutboundErrorToOriginator(ctx, err) err.WriteToLog(session.ExportIDToError(ctx)) common.Interrupt(link.Writer) } else { common.Must(common.Close(link.Writer)) } common.Interrupt(link.Reader) } } // Address implements internet.Dialer. func (h *Handler) Address() net.Address { if h.senderSettings == nil || h.senderSettings.Via == nil { return nil } return h.senderSettings.Via.AsAddress() } // Dial implements internet.Dialer. func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { if h.senderSettings != nil { if h.senderSettings.ProxySettings.HasTag() && !h.senderSettings.ProxySettings.TransportLayerProxy { tag := h.senderSettings.ProxySettings.Tag handler := h.outboundManager.GetHandler(tag) if handler != nil { newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx)) ctx = session.ContextWithOutbound(ctx, &session.Outbound{ Target: dest, }) opts := pipe.OptionsFromContext(ctx) uplinkReader, uplinkWriter := pipe.New(opts...) downlinkReader, downlinkWriter := pipe.New(opts...) go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}) conn := net.NewConnection(net.ConnectionInputMulti(uplinkWriter), net.ConnectionOutputMulti(downlinkReader)) securityEngine, err := security.CreateSecurityEngineFromSettings(ctx, h.streamSettings) if err != nil { return nil, newError("unable to create security engine").Base(err) } if securityEngine != nil { conn, err = securityEngine.Client(conn, security.OptionWithDestination{Dest: dest}) if err != nil { return nil, newError("unable to create security protocol client from security engine").Base(err) } } return h.getStatCouterConnection(conn), nil } newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } if h.senderSettings.Via != nil { outbound := session.OutboundFromContext(ctx) if outbound == nil { outbound = new(session.Outbound) ctx = session.ContextWithOutbound(ctx, outbound) } outbound.Gateway = h.senderSettings.Via.AsAddress() } if h.senderSettings.DomainStrategy != proxyman.SenderConfig_AS_IS { outbound := session.OutboundFromContext(ctx) if outbound == nil { outbound = new(session.Outbound) ctx = session.ContextWithOutbound(ctx, outbound) } outbound.Resolver = func(ctx context.Context, domain string) net.Address { return h.resolveIP(ctx, domain, h.Address()) } } } enablePacketAddrCapture := true if h.senderSettings != nil && h.senderSettings.ProxySettings != nil && h.senderSettings.ProxySettings.HasTag() && h.senderSettings.ProxySettings.TransportLayerProxy { tag := h.senderSettings.ProxySettings.Tag newError("transport layer proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx)) ctx = session.SetTransportLayerProxyTagToContext(ctx, tag) enablePacketAddrCapture = false } if isStream, err := packetaddr.GetDestinationSubsetOf(dest); err == nil && enablePacketAddrCapture { packetConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{IP: net.AnyIP.IP(), Port: 0}, h.streamSettings.SocketSettings) if err != nil { return nil, newError("unable to listen socket").Base(err) } conn := packetaddr.ToPacketAddrConnWrapper(packetConn, isStream) return h.getStatCouterConnection(conn), nil } proxyEnvironment := envctx.EnvironmentFromContext(h.ctx).(environment.ProxyEnvironment) transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("transport") if err != nil { return nil, newError("unable to narrow environment to transport").Base(err) } ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment) conn, err := internet.Dial(ctx, dest, h.streamSettings) return h.getStatCouterConnection(conn), err } func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { strategy := h.senderSettings.DomainStrategy ips, err := dns.LookupIPWithOption(h.dns, domain, dns.IPOption{ IPv4Enable: strategy == proxyman.SenderConfig_USE_IP || strategy == proxyman.SenderConfig_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()), IPv6Enable: strategy == proxyman.SenderConfig_USE_IP || strategy == proxyman.SenderConfig_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()), FakeEnable: false, }) if err != nil { newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) } if len(ips) == 0 { return nil } return net.IPAddress(ips[dice.Roll(len(ips))]) } func (h *Handler) getStatCouterConnection(conn internet.Connection) internet.Connection { if h.uplinkCounter != nil || h.downlinkCounter != nil { return &internet.StatCouterConnection{ Connection: conn, ReadCounter: h.downlinkCounter, WriteCounter: h.uplinkCounter, } } return conn } // GetOutbound implements proxy.GetOutbound. func (h *Handler) GetOutbound() proxy.Outbound { return h.proxy } // Start implements common.Runnable. func (h *Handler) Start() error { return nil } // Close implements common.Closable. func (h *Handler) Close() error { common.Close(h.mux) if closableProxy, ok := h.proxy.(common.Closable); ok { if err := closableProxy.Close(); err != nil { return newError("unable to close proxy").Base(err) } } return nil } ================================================ FILE: app/proxyman/outbound/handler_test.go ================================================ package outbound_test import ( "context" "testing" _ "unsafe" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/policy" . "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound" "github.com/v2fly/v2ray-core/v5/app/stats" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl" "github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl" "github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func TestInterfaces(t *testing.T) { _ = (outbound.Handler)(new(Handler)) _ = (outbound.Manager)(new(Manager)) } //go:linkname toContext github.com/v2fly/v2ray-core/v5.toContext func toContext(ctx context.Context, v *core.Instance) context.Context func TestOutboundWithoutStatCounter(t *testing.T) { config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&stats.Config{}), serial.ToTypedMessage(&policy.Config{ System: &policy.SystemPolicy{ Stats: &policy.SystemPolicy_Stats{ InboundUplink: true, }, }, }), }, } v, _ := core.New(config) v.AddFeature((outbound.Manager)(new(Manager))) ctx := toContext(context.Background(), v) defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl() deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx) rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(), defaultFilesystemImpl, deferredPersistentStorageImpl) proxyEnvironment := rootEnv.ProxyEnvironment("o") ctx = envctx.ContextWithEnvironment(ctx, proxyEnvironment) h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{ Tag: "tag", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }) conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146)) _, ok := conn.(*internet.StatCouterConnection) if ok { t.Errorf("Expected conn to not be StatCouterConnection") } } func TestOutboundWithStatCounter(t *testing.T) { config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&stats.Config{}), serial.ToTypedMessage(&policy.Config{ System: &policy.SystemPolicy{ Stats: &policy.SystemPolicy_Stats{ OutboundUplink: true, OutboundDownlink: true, }, }, }), }, } v, _ := core.New(config) v.AddFeature((outbound.Manager)(new(Manager))) ctx := toContext(context.Background(), v) defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl() deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx) rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(), defaultFilesystemImpl, deferredPersistentStorageImpl) proxyEnvironment := rootEnv.ProxyEnvironment("o") ctx = envctx.ContextWithEnvironment(ctx, proxyEnvironment) h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{ Tag: "tag", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }) conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146)) _, ok := conn.(*internet.StatCouterConnection) if !ok { t.Errorf("Expected conn to be StatCouterConnection") } } ================================================ FILE: app/proxyman/outbound/outbound.go ================================================ package outbound //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "strings" "sync" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/outbound" ) // Manager is to manage all outbound handlers. type Manager struct { access sync.RWMutex defaultHandler outbound.Handler taggedHandler map[string]outbound.Handler untaggedHandlers []outbound.Handler running bool } // New creates a new Manager. func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) { m := &Manager{ taggedHandler: make(map[string]outbound.Handler), } return m, nil } // Type implements common.HasType. func (m *Manager) Type() interface{} { return outbound.ManagerType() } // Start implements core.Feature func (m *Manager) Start() error { m.access.Lock() defer m.access.Unlock() m.running = true for _, h := range m.taggedHandler { if err := h.Start(); err != nil { return err } } for _, h := range m.untaggedHandlers { if err := h.Start(); err != nil { return err } } return nil } // Close implements core.Feature func (m *Manager) Close() error { m.access.Lock() defer m.access.Unlock() m.running = false var errs []error for _, h := range m.taggedHandler { errs = append(errs, h.Close()) } for _, h := range m.untaggedHandlers { errs = append(errs, h.Close()) } return errors.Combine(errs...) } // GetDefaultHandler implements outbound.Manager. func (m *Manager) GetDefaultHandler() outbound.Handler { m.access.RLock() defer m.access.RUnlock() return m.defaultHandler } // GetHandler implements outbound.Manager. func (m *Manager) GetHandler(tag string) outbound.Handler { m.access.RLock() defer m.access.RUnlock() if handler, found := m.taggedHandler[tag]; found { return handler } return nil } // AddHandler implements outbound.Manager. func (m *Manager) AddHandler(ctx context.Context, handler outbound.Handler) error { m.access.Lock() defer m.access.Unlock() tag := handler.Tag() if m.defaultHandler == nil || (len(tag) > 0 && tag == m.defaultHandler.Tag()) { m.defaultHandler = handler } if len(tag) > 0 { if oldHandler, found := m.taggedHandler[tag]; found { errors.New("will replace the existed outbound with the tag: " + tag).AtWarning().WriteToLog() _ = oldHandler.Close() } m.taggedHandler[tag] = handler } else { m.untaggedHandlers = append(m.untaggedHandlers, handler) } if m.running { return handler.Start() } return nil } // RemoveHandler implements outbound.Manager. func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { if tag == "" { return common.ErrNoClue } m.access.Lock() defer m.access.Unlock() if handler, found := m.taggedHandler[tag]; found { if err := handler.Close(); err != nil { newError("failed to close handler ", tag).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } delete(m.taggedHandler, tag) if m.defaultHandler != nil && m.defaultHandler.Tag() == tag { m.defaultHandler = nil } return nil } return common.ErrNoClue } // Select implements outbound.HandlerSelector. func (m *Manager) Select(selectors []string) []string { m.access.RLock() defer m.access.RUnlock() tags := make([]string, 0, len(selectors)) for tag := range m.taggedHandler { match := false for _, selector := range selectors { if strings.HasPrefix(tag, selector) { match = true break } } if match { tags = append(tags, tag) } } return tags } func init() { common.Must(common.RegisterConfig((*proxyman.OutboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*proxyman.OutboundConfig)) })) common.Must(common.RegisterConfig((*core.OutboundHandlerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewHandler(ctx, config.(*core.OutboundHandlerConfig)) })) } ================================================ FILE: app/restfulapi/config.go ================================================ package restfulapi import ( "context" "github.com/v2fly/v2ray-core/v5/common" ) func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return newRestfulService(ctx, config.(*Config)) })) } ================================================ FILE: app/restfulapi/config.pb.go ================================================ package restfulapi import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` ListenAddr string `protobuf:"bytes,1,opt,name=listen_addr,json=listenAddr,proto3" json:"listen_addr,omitempty"` ListenPort int32 `protobuf:"varint,2,opt,name=listen_port,json=listenPort,proto3" json:"listen_port,omitempty"` AuthToken string `protobuf:"bytes,3,opt,name=auth_token,json=authToken,proto3" json:"auth_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_restfulapi_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_restfulapi_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_restfulapi_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetListenAddr() string { if x != nil { return x.ListenAddr } return "" } func (x *Config) GetListenPort() int32 { if x != nil { return x.ListenPort } return 0 } func (x *Config) GetAuthToken() string { if x != nil { return x.AuthToken } return "" } var File_app_restfulapi_config_proto protoreflect.FileDescriptor const file_app_restfulapi_config_proto_rawDesc = "" + "\n" + "\x1bapp/restfulapi/config.proto\x12\x14v2ray.app.restfulapi\x1a common/protoext/extensions.proto\"\x84\x01\n" + "\x06Config\x12\x1f\n" + "\vlisten_addr\x18\x01 \x01(\tR\n" + "listenAddr\x12\x1f\n" + "\vlisten_port\x18\x02 \x01(\x05R\n" + "listenPort\x12\x1d\n" + "\n" + "auth_token\x18\x03 \x01(\tR\tauthToken:\x19\x82\xb5\x18\x15\n" + "\aservice\x12\n" + "restfulapiBa\n" + "\x1acom.v2ray.core.app.restapiP\x01Z-github.com/v2fly/v2ray-core/v5/app/restfulapi\xaa\x02\x11V2Ray.App.Restapib\x06proto3" var ( file_app_restfulapi_config_proto_rawDescOnce sync.Once file_app_restfulapi_config_proto_rawDescData []byte ) func file_app_restfulapi_config_proto_rawDescGZIP() []byte { file_app_restfulapi_config_proto_rawDescOnce.Do(func() { file_app_restfulapi_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_restfulapi_config_proto_rawDesc), len(file_app_restfulapi_config_proto_rawDesc))) }) return file_app_restfulapi_config_proto_rawDescData } var file_app_restfulapi_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_app_restfulapi_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.app.restfulapi.Config } var file_app_restfulapi_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_app_restfulapi_config_proto_init() } func file_app_restfulapi_config_proto_init() { if File_app_restfulapi_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_restfulapi_config_proto_rawDesc), len(file_app_restfulapi_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_restfulapi_config_proto_goTypes, DependencyIndexes: file_app_restfulapi_config_proto_depIdxs, MessageInfos: file_app_restfulapi_config_proto_msgTypes, }.Build() File_app_restfulapi_config_proto = out.File file_app_restfulapi_config_proto_goTypes = nil file_app_restfulapi_config_proto_depIdxs = nil } ================================================ FILE: app/restfulapi/config.proto ================================================ syntax = "proto3"; package v2ray.app.restfulapi; option csharp_namespace = "V2Ray.App.Restapi"; option go_package = "github.com/v2fly/v2ray-core/v5/app/restfulapi"; option java_package = "com.v2ray.core.app.restapi"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Config{ option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "restfulapi"; string listen_addr = 1; int32 listen_port = 2; string auth_token = 3; } ================================================ FILE: app/restfulapi/errors.generated.go ================================================ package restfulapi import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/restfulapi/restful_api.go ================================================ package restfulapi import ( "net/http" "strings" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/render" "github.com/go-playground/validator/v10" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) var validate *validator.Validate type StatsBound struct { // Better name? Uplink int64 `json:"uplink"` Downlink int64 `json:"downlink"` } func (rs *restfulService) tagStats(w http.ResponseWriter, r *http.Request) { boundType := chi.URLParam(r, "bound_type") tag := chi.URLParam(r, "tag") if validate.Var(boundType, "required,oneof=inbounds outbounds") != nil || validate.Var(tag, "required,min=1,max=255") != nil { render.Status(r, http.StatusUnprocessableEntity) render.JSON(w, r, render.M{}) return } bound := boundType[:len(boundType)-1] upCounter := rs.stats.GetCounter(bound + ">>>" + tag + ">>>traffic>>>uplink") downCounter := rs.stats.GetCounter(bound + ">>>" + tag + ">>>traffic>>>downlink") if upCounter == nil || downCounter == nil { render.Status(r, http.StatusNotFound) render.JSON(w, r, render.M{}) return } render.JSON(w, r, &StatsBound{ Uplink: upCounter.Value(), Downlink: downCounter.Value(), }) } func (rs *restfulService) version(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, render.M{"version": core.Version()}) } func (rs *restfulService) TokenAuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { header := r.Header.Get("Authorization") text := strings.SplitN(header, " ", 2) hasInvalidHeader := text[0] != "Bearer" hasInvalidSecret := len(text) != 2 || text[1] != rs.config.AuthToken if hasInvalidHeader || hasInvalidSecret { render.Status(r, http.StatusUnauthorized) render.JSON(w, r, render.M{}) return } next.ServeHTTP(w, r) }) } func (rs *restfulService) start() error { r := chi.NewRouter() r.Use(middleware.Heartbeat("/ping")) validate = validator.New() r.Route("/v1", func(r chi.Router) { r.Get("/{bound_type}/{tag}/stats", rs.tagStats) }) r.Get("/version", rs.version) var listener net.Listener var err error address := net.ParseAddress(rs.config.ListenAddr) switch { case address.Family().IsIP(): listener, err = internet.ListenSystem(rs.ctx, &net.TCPAddr{IP: address.IP(), Port: int(rs.config.ListenPort)}, nil) case strings.EqualFold(address.Domain(), "localhost"): listener, err = internet.ListenSystem(rs.ctx, &net.TCPAddr{IP: net.IP{127, 0, 0, 1}, Port: int(rs.config.ListenPort)}, nil) default: return newError("restful api cannot listen on the address: ", address) } if err != nil { return newError("restful api cannot listen on the port ", rs.config.ListenPort).Base(err) } go func() { err := http.Serve(listener, r) if err != nil { newError("unable to serve restful api").WriteToLog() } }() return nil } ================================================ FILE: app/restfulapi/service.go ================================================ package restfulapi import ( "context" "net" "sync" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/features" feature_stats "github.com/v2fly/v2ray-core/v5/features/stats" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type restfulService struct { listener net.Listener config *Config access sync.Mutex stats feature_stats.Manager ctx context.Context } func (rs *restfulService) Type() interface{} { return (*struct{})(nil) } func (rs *restfulService) Start() error { defer rs.access.Unlock() rs.access.Lock() return rs.start() } func (rs *restfulService) Close() error { defer rs.access.Unlock() rs.access.Lock() if rs.listener != nil { return rs.listener.Close() } return nil } func (rs *restfulService) init(config *Config, stats feature_stats.Manager) { rs.stats = stats rs.config = config } func newRestfulService(ctx context.Context, config *Config) (features.Feature, error) { r := new(restfulService) r.ctx = ctx if err := core.RequireFeatures(ctx, func(stats feature_stats.Manager) { r.init(config, stats) }); err != nil { return nil, err } return r, nil } ================================================ FILE: app/restfulapi/service_test.go ================================================ package restfulapi import ( "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestTypeReturnAnonymousType(t *testing.T) { service := restfulService{} serviceType := service.Type() assert.Empty(t, reflect.TypeOf(serviceType).Name(), "must return anonymous type") } ================================================ FILE: app/reverse/bridge.go ================================================ package reverse import ( "context" "time" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) // Bridge is a component in reverse proxy, that relays connections from Portal to local address. type Bridge struct { ctx context.Context dispatcher routing.Dispatcher tag string domain string workers []*BridgeWorker monitorTask *task.Periodic } // NewBridge creates a new Bridge instance. func NewBridge(ctx context.Context, config *BridgeConfig, dispatcher routing.Dispatcher) (*Bridge, error) { if config.Tag == "" { return nil, newError("bridge tag is empty") } if config.Domain == "" { return nil, newError("bridge domain is empty") } b := &Bridge{ ctx: ctx, dispatcher: dispatcher, tag: config.Tag, domain: config.Domain, } b.monitorTask = &task.Periodic{ Execute: b.monitor, Interval: time.Second * 2, } return b, nil } func (b *Bridge) cleanup() { var activeWorkers []*BridgeWorker for _, w := range b.workers { if w.IsActive() { activeWorkers = append(activeWorkers, w) } } if len(activeWorkers) != len(b.workers) { b.workers = activeWorkers } } func (b *Bridge) monitor() error { b.cleanup() var numConnections uint32 var numWorker uint32 for _, w := range b.workers { if w.IsActive() { numConnections += w.Connections() numWorker++ } } if numWorker == 0 || numConnections/numWorker > 16 { worker, err := NewBridgeWorker(b.ctx, b.domain, b.tag, b.dispatcher) if err != nil { newError("failed to create bridge worker").Base(err).AtWarning().WriteToLog() return nil } b.workers = append(b.workers, worker) } return nil } func (b *Bridge) Start() error { return b.monitorTask.Start() } func (b *Bridge) Close() error { return b.monitorTask.Close() } type BridgeWorker struct { tag string worker *mux.ServerWorker dispatcher routing.Dispatcher state Control_State } func NewBridgeWorker(ctx context.Context, domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) { bridgeCtx := session.ContextWithInbound(ctx, &session.Inbound{ Tag: tag, }) link, err := d.Dispatch(bridgeCtx, net.Destination{ Network: net.Network_TCP, Address: net.DomainAddress(domain), Port: 0, }) if err != nil { return nil, err } w := &BridgeWorker{ dispatcher: d, tag: tag, } worker, err := mux.NewServerWorker(ctx, w, link) if err != nil { return nil, err } w.worker = worker return w, nil } func (w *BridgeWorker) Type() interface{} { return routing.DispatcherType() } func (w *BridgeWorker) Start() error { return nil } func (w *BridgeWorker) Close() error { return nil } func (w *BridgeWorker) IsActive() bool { return w.state == Control_ACTIVE && !w.worker.Closed() } func (w *BridgeWorker) Connections() uint32 { return w.worker.ActiveConnections() } func (w *BridgeWorker) handleInternalConn(link transport.Link) { go func() { reader := link.Reader for { mb, err := reader.ReadMultiBuffer() if err != nil { break } for _, b := range mb { var ctl Control if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil { newError("failed to parse proto message").Base(err).WriteToLog() break } if ctl.State != w.state { w.state = ctl.State } } } }() } func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { if !isInternalDomain(dest) { ctx = session.ContextWithInbound(ctx, &session.Inbound{ Tag: w.tag, }) return w.dispatcher.Dispatch(ctx, dest) } opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)} uplinkReader, uplinkWriter := pipe.New(opt...) downlinkReader, downlinkWriter := pipe.New(opt...) w.handleInternalConn(transport.Link{ Reader: downlinkReader, Writer: uplinkWriter, }) return &transport.Link{ Reader: uplinkReader, Writer: downlinkWriter, }, nil } ================================================ FILE: app/reverse/config.go ================================================ package reverse import ( "crypto/rand" "io" "github.com/v2fly/v2ray-core/v5/common/dice" ) func (c *Control) FillInRandom() { randomLength := dice.Roll(64) c.Random = make([]byte, randomLength) io.ReadFull(rand.Reader, c.Random) } ================================================ FILE: app/reverse/config.pb.go ================================================ package reverse import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Control_State int32 const ( Control_ACTIVE Control_State = 0 Control_DRAIN Control_State = 1 ) // Enum value maps for Control_State. var ( Control_State_name = map[int32]string{ 0: "ACTIVE", 1: "DRAIN", } Control_State_value = map[string]int32{ "ACTIVE": 0, "DRAIN": 1, } ) func (x Control_State) Enum() *Control_State { p := new(Control_State) *p = x return p } func (x Control_State) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Control_State) Descriptor() protoreflect.EnumDescriptor { return file_app_reverse_config_proto_enumTypes[0].Descriptor() } func (Control_State) Type() protoreflect.EnumType { return &file_app_reverse_config_proto_enumTypes[0] } func (x Control_State) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Control_State.Descriptor instead. func (Control_State) EnumDescriptor() ([]byte, []int) { return file_app_reverse_config_proto_rawDescGZIP(), []int{0, 0} } type Control struct { state protoimpl.MessageState `protogen:"open.v1"` State Control_State `protobuf:"varint,1,opt,name=state,proto3,enum=v2ray.core.app.reverse.Control_State" json:"state,omitempty"` Random []byte `protobuf:"bytes,99,opt,name=random,proto3" json:"random,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Control) Reset() { *x = Control{} mi := &file_app_reverse_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Control) String() string { return protoimpl.X.MessageStringOf(x) } func (*Control) ProtoMessage() {} func (x *Control) ProtoReflect() protoreflect.Message { mi := &file_app_reverse_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Control.ProtoReflect.Descriptor instead. func (*Control) Descriptor() ([]byte, []int) { return file_app_reverse_config_proto_rawDescGZIP(), []int{0} } func (x *Control) GetState() Control_State { if x != nil { return x.State } return Control_ACTIVE } func (x *Control) GetRandom() []byte { if x != nil { return x.Random } return nil } type BridgeConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BridgeConfig) Reset() { *x = BridgeConfig{} mi := &file_app_reverse_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BridgeConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*BridgeConfig) ProtoMessage() {} func (x *BridgeConfig) ProtoReflect() protoreflect.Message { mi := &file_app_reverse_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BridgeConfig.ProtoReflect.Descriptor instead. func (*BridgeConfig) Descriptor() ([]byte, []int) { return file_app_reverse_config_proto_rawDescGZIP(), []int{1} } func (x *BridgeConfig) GetTag() string { if x != nil { return x.Tag } return "" } func (x *BridgeConfig) GetDomain() string { if x != nil { return x.Domain } return "" } type PortalConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PortalConfig) Reset() { *x = PortalConfig{} mi := &file_app_reverse_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PortalConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*PortalConfig) ProtoMessage() {} func (x *PortalConfig) ProtoReflect() protoreflect.Message { mi := &file_app_reverse_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PortalConfig.ProtoReflect.Descriptor instead. func (*PortalConfig) Descriptor() ([]byte, []int) { return file_app_reverse_config_proto_rawDescGZIP(), []int{2} } func (x *PortalConfig) GetTag() string { if x != nil { return x.Tag } return "" } func (x *PortalConfig) GetDomain() string { if x != nil { return x.Domain } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` BridgeConfig []*BridgeConfig `protobuf:"bytes,1,rep,name=bridge_config,json=bridgeConfig,proto3" json:"bridge_config,omitempty"` PortalConfig []*PortalConfig `protobuf:"bytes,2,rep,name=portal_config,json=portalConfig,proto3" json:"portal_config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_reverse_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_reverse_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_reverse_config_proto_rawDescGZIP(), []int{3} } func (x *Config) GetBridgeConfig() []*BridgeConfig { if x != nil { return x.BridgeConfig } return nil } func (x *Config) GetPortalConfig() []*PortalConfig { if x != nil { return x.PortalConfig } return nil } var File_app_reverse_config_proto protoreflect.FileDescriptor const file_app_reverse_config_proto_rawDesc = "" + "\n" + "\x18app/reverse/config.proto\x12\x16v2ray.core.app.reverse\x1a common/protoext/extensions.proto\"~\n" + "\aControl\x12;\n" + "\x05state\x18\x01 \x01(\x0e2%.v2ray.core.app.reverse.Control.StateR\x05state\x12\x16\n" + "\x06random\x18c \x01(\fR\x06random\"\x1e\n" + "\x05State\x12\n" + "\n" + "\x06ACTIVE\x10\x00\x12\t\n" + "\x05DRAIN\x10\x01\"8\n" + "\fBridgeConfig\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" + "\x06domain\x18\x02 \x01(\tR\x06domain\"8\n" + "\fPortalConfig\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" + "\x06domain\x18\x02 \x01(\tR\x06domain\"\xb6\x01\n" + "\x06Config\x12I\n" + "\rbridge_config\x18\x01 \x03(\v2$.v2ray.core.app.reverse.BridgeConfigR\fbridgeConfig\x12I\n" + "\rportal_config\x18\x02 \x03(\v2$.v2ray.core.app.reverse.PortalConfigR\fportalConfig:\x16\x82\xb5\x18\x12\n" + "\aservice\x12\areverseBg\n" + "\x1ccom.v2ray.core.proxy.reverseP\x01Z*github.com/v2fly/v2ray-core/v5/app/reverse\xaa\x02\x18V2Ray.Core.Proxy.Reverseb\x06proto3" var ( file_app_reverse_config_proto_rawDescOnce sync.Once file_app_reverse_config_proto_rawDescData []byte ) func file_app_reverse_config_proto_rawDescGZIP() []byte { file_app_reverse_config_proto_rawDescOnce.Do(func() { file_app_reverse_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_reverse_config_proto_rawDesc), len(file_app_reverse_config_proto_rawDesc))) }) return file_app_reverse_config_proto_rawDescData } var file_app_reverse_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_app_reverse_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_app_reverse_config_proto_goTypes = []any{ (Control_State)(0), // 0: v2ray.core.app.reverse.Control.State (*Control)(nil), // 1: v2ray.core.app.reverse.Control (*BridgeConfig)(nil), // 2: v2ray.core.app.reverse.BridgeConfig (*PortalConfig)(nil), // 3: v2ray.core.app.reverse.PortalConfig (*Config)(nil), // 4: v2ray.core.app.reverse.Config } var file_app_reverse_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.app.reverse.Control.state:type_name -> v2ray.core.app.reverse.Control.State 2, // 1: v2ray.core.app.reverse.Config.bridge_config:type_name -> v2ray.core.app.reverse.BridgeConfig 3, // 2: v2ray.core.app.reverse.Config.portal_config:type_name -> v2ray.core.app.reverse.PortalConfig 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_app_reverse_config_proto_init() } func file_app_reverse_config_proto_init() { if File_app_reverse_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_reverse_config_proto_rawDesc), len(file_app_reverse_config_proto_rawDesc)), NumEnums: 1, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_reverse_config_proto_goTypes, DependencyIndexes: file_app_reverse_config_proto_depIdxs, EnumInfos: file_app_reverse_config_proto_enumTypes, MessageInfos: file_app_reverse_config_proto_msgTypes, }.Build() File_app_reverse_config_proto = out.File file_app_reverse_config_proto_goTypes = nil file_app_reverse_config_proto_depIdxs = nil } ================================================ FILE: app/reverse/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.reverse; option csharp_namespace = "V2Ray.Core.Proxy.Reverse"; option go_package = "github.com/v2fly/v2ray-core/v5/app/reverse"; option java_package = "com.v2ray.core.proxy.reverse"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Control { enum State { ACTIVE = 0; DRAIN = 1; } State state = 1; bytes random = 99; } message BridgeConfig { string tag = 1; string domain = 2; } message PortalConfig { string tag = 1; string domain = 2; } message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "reverse"; repeated BridgeConfig bridge_config = 1; repeated PortalConfig portal_config = 2; } ================================================ FILE: app/reverse/errors.generated.go ================================================ package reverse import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/reverse/portal.go ================================================ package reverse import ( "context" "sync" "time" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) type Portal struct { ctx context.Context ohm outbound.Manager tag string domain string picker *StaticMuxPicker client *mux.ClientManager } func NewPortal(ctx context.Context, config *PortalConfig, ohm outbound.Manager) (*Portal, error) { if config.Tag == "" { return nil, newError("portal tag is empty") } if config.Domain == "" { return nil, newError("portal domain is empty") } picker, err := NewStaticMuxPicker() if err != nil { return nil, err } return &Portal{ ctx: ctx, ohm: ohm, tag: config.Tag, domain: config.Domain, picker: picker, client: &mux.ClientManager{ Picker: picker, }, }, nil } func (p *Portal) Start() error { return p.ohm.AddHandler(p.ctx, &Outbound{ portal: p, tag: p.tag, }) } func (p *Portal) Close() error { return p.ohm.RemoveHandler(p.ctx, p.tag) } func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) error { outboundMeta := session.OutboundFromContext(ctx) if outboundMeta == nil { return newError("outbound metadata not found").AtError() } if isDomain(outboundMeta.Target, p.domain) { muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{}) if err != nil { return newError("failed to create mux client worker").Base(err).AtWarning() } worker, err := NewPortalWorker(ctx, muxClient) if err != nil { return newError("failed to create portal worker").Base(err) } p.picker.AddWorker(worker) return nil } return p.client.Dispatch(ctx, link) } type Outbound struct { portal *Portal tag string } func (o *Outbound) Tag() string { return o.tag } func (o *Outbound) Dispatch(ctx context.Context, link *transport.Link) { if err := o.portal.HandleConnection(ctx, link); err != nil { newError("failed to process reverse connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) common.Interrupt(link.Writer) } } func (o *Outbound) Start() error { return nil } func (o *Outbound) Close() error { return nil } type StaticMuxPicker struct { access sync.Mutex workers []*PortalWorker cTask *task.Periodic } func NewStaticMuxPicker() (*StaticMuxPicker, error) { p := &StaticMuxPicker{} p.cTask = &task.Periodic{ Execute: p.cleanup, Interval: time.Second * 30, } p.cTask.Start() return p, nil } func (p *StaticMuxPicker) cleanup() error { p.access.Lock() defer p.access.Unlock() var activeWorkers []*PortalWorker for _, w := range p.workers { if !w.Closed() { activeWorkers = append(activeWorkers, w) } } if len(activeWorkers) != len(p.workers) { p.workers = activeWorkers } return nil } func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) { p.access.Lock() defer p.access.Unlock() if len(p.workers) == 0 { return nil, newError("empty worker list") } minIdx := -1 var minConn uint32 = 9999 for i, w := range p.workers { if w.draining { continue } if w.client.Closed() { continue } if w.client.ActiveConnections() < minConn { minConn = w.client.ActiveConnections() minIdx = i } } if minIdx == -1 { for i, w := range p.workers { if w.IsFull() { continue } if w.client.ActiveConnections() < minConn { minConn = w.client.ActiveConnections() minIdx = i } } } if minIdx != -1 { return p.workers[minIdx].client, nil } return nil, newError("no mux client worker available") } func (p *StaticMuxPicker) AddWorker(worker *PortalWorker) { p.access.Lock() defer p.access.Unlock() p.workers = append(p.workers, worker) } type PortalWorker struct { client *mux.ClientWorker control *task.Periodic writer buf.Writer reader buf.Reader draining bool } func NewPortalWorker(ctx context.Context, client *mux.ClientWorker) (*PortalWorker, error) { opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)} uplinkReader, uplinkWriter := pipe.New(opt...) downlinkReader, downlinkWriter := pipe.New(opt...) ctx = session.ContextWithOutbound(ctx, &session.Outbound{ Target: net.UDPDestination(net.DomainAddress(internalDomain), 0), }) f := client.Dispatch(ctx, &transport.Link{ Reader: uplinkReader, Writer: downlinkWriter, }) if !f { return nil, newError("unable to dispatch control connection") } w := &PortalWorker{ client: client, reader: downlinkReader, writer: uplinkWriter, } w.control = &task.Periodic{ Execute: w.heartbeat, Interval: time.Second * 2, } w.control.Start() return w, nil } func (w *PortalWorker) heartbeat() error { if w.client.Closed() { return newError("client worker stopped") } if w.draining || w.writer == nil { return newError("already disposed") } msg := &Control{} msg.FillInRandom() if w.client.TotalConnections() > 256 { w.draining = true msg.State = Control_DRAIN defer func() { common.Close(w.writer) common.Interrupt(w.reader) w.writer = nil }() } b, err := proto.Marshal(msg) common.Must(err) mb := buf.MergeBytes(nil, b) return w.writer.WriteMultiBuffer(mb) } func (w *PortalWorker) IsFull() bool { return w.client.IsFull() } func (w *PortalWorker) Closed() bool { return w.client.Closed() } ================================================ FILE: app/reverse/portal_test.go ================================================ package reverse_test import ( "testing" "github.com/v2fly/v2ray-core/v5/app/reverse" "github.com/v2fly/v2ray-core/v5/common" ) func TestStaticPickerEmpty(t *testing.T) { picker, err := reverse.NewStaticMuxPicker() common.Must(err) worker, err := picker.PickAvailable() if err == nil { t.Error("expected error, but nil") } if worker != nil { t.Error("expected nil worker, but not nil") } } ================================================ FILE: app/reverse/reverse.go ================================================ package reverse //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/features/routing" ) const ( internalDomain = "reverse.internal.v2fly.org" ) func isDomain(dest net.Destination, domain string) bool { return dest.Address.Family().IsDomain() && dest.Address.Domain() == domain } func isInternalDomain(dest net.Destination) bool { return isDomain(dest, internalDomain) } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { r := new(Reverse) if err := core.RequireFeatures(ctx, func(d routing.Dispatcher, om outbound.Manager) error { return r.Init(ctx, config.(*Config), d, om) }); err != nil { return nil, err } return r, nil })) } type Reverse struct { bridges []*Bridge portals []*Portal } func (r *Reverse) Init(ctx context.Context, config *Config, d routing.Dispatcher, ohm outbound.Manager) error { for _, bConfig := range config.BridgeConfig { b, err := NewBridge(ctx, bConfig, d) if err != nil { return err } r.bridges = append(r.bridges, b) } for _, pConfig := range config.PortalConfig { p, err := NewPortal(ctx, pConfig, ohm) if err != nil { return err } r.portals = append(r.portals, p) } return nil } func (r *Reverse) Type() interface{} { return (*Reverse)(nil) } func (r *Reverse) Start() error { for _, b := range r.bridges { if err := b.Start(); err != nil { return err } } for _, p := range r.portals { if err := p.Start(); err != nil { return err } } return nil } func (r *Reverse) Close() error { var errs []error for _, b := range r.bridges { errs = append(errs, b.Close()) } for _, p := range r.portals { errs = append(errs, p.Close()) } return errors.Combine(errs...) } ================================================ FILE: app/router/balancing.go ================================================ //go:build !confonly // +build !confonly package router import ( "context" "github.com/v2fly/v2ray-core/v5/features/extension" "github.com/v2fly/v2ray-core/v5/features/outbound" ) type BalancingStrategy interface { PickOutbound([]string) string } type BalancingPrincipleTarget interface { GetPrincipleTarget([]string) []string } type Balancer struct { selectors []string strategy BalancingStrategy ohm outbound.Manager fallbackTag string override override } // PickOutbound picks the tag of an outbound func (b *Balancer) PickOutbound() (string, error) { candidates, err := b.SelectOutbounds() if err != nil { if b.fallbackTag != "" { newError("fallback to [", b.fallbackTag, "], due to error: ", err).AtInfo().WriteToLog() return b.fallbackTag, nil } return "", err } var tag string if o := b.override.Get(); o != "" { tag = o } else { tag = b.strategy.PickOutbound(candidates) } if tag == "" { if b.fallbackTag != "" { newError("fallback to [", b.fallbackTag, "], due to empty tag returned").AtInfo().WriteToLog() return b.fallbackTag, nil } // will use default handler return "", newError("balancing strategy returns empty tag") } return tag, nil } func (b *Balancer) InjectContext(ctx context.Context) { if contextReceiver, ok := b.strategy.(extension.ContextReceiver); ok { contextReceiver.InjectContext(ctx) } } // SelectOutbounds select outbounds with selectors of the Balancer func (b *Balancer) SelectOutbounds() ([]string, error) { hs, ok := b.ohm.(outbound.HandlerSelector) if !ok { return nil, newError("outbound.Manager is not a HandlerSelector") } tags := hs.Select(b.selectors) return tags, nil } // GetPrincipleTarget implements routing.BalancerPrincipleTarget func (r *Router) GetPrincipleTarget(tag string) ([]string, error) { if b, ok := r.balancers[tag]; ok { if s, ok := b.strategy.(BalancingPrincipleTarget); ok { candidates, err := b.SelectOutbounds() if err != nil { return nil, newError("unable to select outbounds").Base(err) } return s.GetPrincipleTarget(candidates), nil } return nil, newError("unsupported GetPrincipleTarget") } return nil, newError("cannot find tag") } // SetOverrideTarget implements routing.BalancerOverrider func (r *Router) SetOverrideTarget(tag, target string) error { if b, ok := r.balancers[tag]; ok { b.override.Put(target) return nil } return newError("cannot find tag") } // GetOverrideTarget implements routing.BalancerOverrider func (r *Router) GetOverrideTarget(tag string) (string, error) { if b, ok := r.balancers[tag]; ok { return b.override.Get(), nil } return "", newError("cannot find tag") } ================================================ FILE: app/router/balancing_override.go ================================================ package router import ( sync "sync" ) func (r *Router) OverrideBalancer(balancer string, target string) error { var b *Balancer for tag, bl := range r.balancers { if tag == balancer { b = bl break } } if b == nil { return newError("balancer '", balancer, "' not found") } b.override.Put(target) return nil } type overrideSettings struct { target string } type override struct { access sync.RWMutex settings overrideSettings } // Get gets the override settings func (o *override) Get() string { o.access.RLock() defer o.access.RUnlock() return o.settings.target } // Put updates the override settings func (o *override) Put(target string) { o.access.Lock() defer o.access.Unlock() o.settings.target = target } // Clear clears the override settings func (o *override) Clear() { o.access.Lock() defer o.access.Unlock() o.settings.target = "" } ================================================ FILE: app/router/command/command.go ================================================ package command //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "time" "google.golang.org/grpc" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/features/stats" ) // routingServer is an implementation of RoutingService. type routingServer struct { router routing.Router routingStats stats.Channel } func (s *routingServer) GetBalancerInfo(ctx context.Context, request *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) { var ret GetBalancerInfoResponse ret.Balancer = &BalancerMsg{} if bo, ok := s.router.(routing.BalancerOverrider); ok { { res, err := bo.GetOverrideTarget(request.GetTag()) if err != nil { return nil, err } ret.Balancer.Override = &OverrideInfo{ Target: res, } } } if pt, ok := s.router.(routing.BalancerPrincipleTarget); ok { { res, err := pt.GetPrincipleTarget(request.GetTag()) if err != nil { newError("unable to obtain principle target").Base(err).AtInfo().WriteToLog() } else { ret.Balancer.PrincipleTarget = &PrincipleTargetInfo{Tag: res} } } } return &ret, nil } func (s *routingServer) OverrideBalancerTarget(ctx context.Context, request *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) { if bo, ok := s.router.(routing.BalancerOverrider); ok { return &OverrideBalancerTargetResponse{}, bo.SetOverrideTarget(request.BalancerTag, request.Target) } return nil, newError("unsupported router implementation") } // NewRoutingServer creates a statistics service with statistics manager. func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer { return &routingServer{ router: router, routingStats: routingStats, } } func (s *routingServer) TestRoute(ctx context.Context, request *TestRouteRequest) (*RoutingContext, error) { if request.RoutingContext == nil { return nil, newError("Invalid routing request.") } route, err := s.router.PickRoute(AsRoutingContext(request.RoutingContext)) if err != nil { return nil, err } if request.PublishResult && s.routingStats != nil { ctx, _ := context.WithTimeout(context.Background(), 4*time.Second) // nolint: govet s.routingStats.Publish(ctx, route) } return AsProtobufMessage(request.FieldSelectors)(route), nil } func (s *routingServer) SubscribeRoutingStats(request *SubscribeRoutingStatsRequest, stream RoutingService_SubscribeRoutingStatsServer) error { if s.routingStats == nil { return newError("Routing statistics not enabled.") } genMessage := AsProtobufMessage(request.FieldSelectors) subscriber, err := stats.SubscribeRunnableChannel(s.routingStats) if err != nil { return err } defer stats.UnsubscribeClosableChannel(s.routingStats, subscriber) for { select { case value, ok := <-subscriber: if !ok { return newError("Upstream closed the subscriber channel.") } route, ok := value.(routing.Route) if !ok { return newError("Upstream sent malformed statistics.") } err := stream.Send(genMessage(route)) if err != nil { return err } case <-stream.Context().Done(): return stream.Context().Err() } } } func (s *routingServer) mustEmbedUnimplementedRoutingServiceServer() {} type service struct { v *core.Instance } func (s *service) Register(server *grpc.Server) { common.Must(s.v.RequireFeatures(func(router routing.Router, stats stats.Manager) { RegisterRoutingServiceServer(server, NewRoutingServer(router, nil)) })) } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { s := core.MustFromContext(ctx) return &service{v: s}, nil })) } ================================================ FILE: app/router/command/command.pb.go ================================================ package command import ( net "github.com/v2fly/v2ray-core/v5/common/net" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // RoutingContext is the context with information relative to routing process. // It conforms to the structure of v2ray.core.features.routing.Context and // v2ray.core.features.routing.Route. type RoutingContext struct { state protoimpl.MessageState `protogen:"open.v1"` InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"` Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=v2ray.core.common.net.Network" json:"Network,omitempty"` SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"` TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"` SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"` TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"` TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"` Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"` User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"` Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"` OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RoutingContext) Reset() { *x = RoutingContext{} mi := &file_app_router_command_command_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RoutingContext) String() string { return protoimpl.X.MessageStringOf(x) } func (*RoutingContext) ProtoMessage() {} func (x *RoutingContext) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RoutingContext.ProtoReflect.Descriptor instead. func (*RoutingContext) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{0} } func (x *RoutingContext) GetInboundTag() string { if x != nil { return x.InboundTag } return "" } func (x *RoutingContext) GetNetwork() net.Network { if x != nil { return x.Network } return net.Network(0) } func (x *RoutingContext) GetSourceIPs() [][]byte { if x != nil { return x.SourceIPs } return nil } func (x *RoutingContext) GetTargetIPs() [][]byte { if x != nil { return x.TargetIPs } return nil } func (x *RoutingContext) GetSourcePort() uint32 { if x != nil { return x.SourcePort } return 0 } func (x *RoutingContext) GetTargetPort() uint32 { if x != nil { return x.TargetPort } return 0 } func (x *RoutingContext) GetTargetDomain() string { if x != nil { return x.TargetDomain } return "" } func (x *RoutingContext) GetProtocol() string { if x != nil { return x.Protocol } return "" } func (x *RoutingContext) GetUser() string { if x != nil { return x.User } return "" } func (x *RoutingContext) GetAttributes() map[string]string { if x != nil { return x.Attributes } return nil } func (x *RoutingContext) GetOutboundGroupTags() []string { if x != nil { return x.OutboundGroupTags } return nil } func (x *RoutingContext) GetOutboundTag() string { if x != nil { return x.OutboundTag } return "" } // SubscribeRoutingStatsRequest subscribes to routing statistics channel if // opened by v2ray-core. // * FieldSelectors selects a subset of fields in routing statistics to return. // Valid selectors: // - inbound: Selects connection's inbound tag. // - network: Selects connection's network. // - ip: Equivalent as "ip_source" and "ip_target", selects both source and // target IP. // - port: Equivalent as "port_source" and "port_target", selects both source // and target port. // - domain: Selects target domain. // - protocol: Select connection's protocol. // - user: Select connection's inbound user email. // - attributes: Select connection's additional attributes. // - outbound: Equivalent as "outbound" and "outbound_group", select both // outbound tag and outbound group tags. // // * If FieldSelectors is left empty, all fields will be returned. type SubscribeRoutingStatsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SubscribeRoutingStatsRequest) Reset() { *x = SubscribeRoutingStatsRequest{} mi := &file_app_router_command_command_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SubscribeRoutingStatsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*SubscribeRoutingStatsRequest) ProtoMessage() {} func (x *SubscribeRoutingStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SubscribeRoutingStatsRequest.ProtoReflect.Descriptor instead. func (*SubscribeRoutingStatsRequest) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{1} } func (x *SubscribeRoutingStatsRequest) GetFieldSelectors() []string { if x != nil { return x.FieldSelectors } return nil } // TestRouteRequest manually tests a routing result according to the routing // context message. // * RoutingContext is the routing message without outbound information. // * FieldSelectors selects the fields to return in the routing result. All // fields are returned if left empty. // * PublishResult broadcasts the routing result to routing statistics channel // if set true. type TestRouteRequest struct { state protoimpl.MessageState `protogen:"open.v1"` RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"` FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"` PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TestRouteRequest) Reset() { *x = TestRouteRequest{} mi := &file_app_router_command_command_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TestRouteRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*TestRouteRequest) ProtoMessage() {} func (x *TestRouteRequest) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TestRouteRequest.ProtoReflect.Descriptor instead. func (*TestRouteRequest) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{2} } func (x *TestRouteRequest) GetRoutingContext() *RoutingContext { if x != nil { return x.RoutingContext } return nil } func (x *TestRouteRequest) GetFieldSelectors() []string { if x != nil { return x.FieldSelectors } return nil } func (x *TestRouteRequest) GetPublishResult() bool { if x != nil { return x.PublishResult } return false } type PrincipleTargetInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PrincipleTargetInfo) Reset() { *x = PrincipleTargetInfo{} mi := &file_app_router_command_command_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PrincipleTargetInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*PrincipleTargetInfo) ProtoMessage() {} func (x *PrincipleTargetInfo) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PrincipleTargetInfo.ProtoReflect.Descriptor instead. func (*PrincipleTargetInfo) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{3} } func (x *PrincipleTargetInfo) GetTag() []string { if x != nil { return x.Tag } return nil } type OverrideInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OverrideInfo) Reset() { *x = OverrideInfo{} mi := &file_app_router_command_command_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OverrideInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*OverrideInfo) ProtoMessage() {} func (x *OverrideInfo) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OverrideInfo.ProtoReflect.Descriptor instead. func (*OverrideInfo) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{4} } func (x *OverrideInfo) GetTarget() string { if x != nil { return x.Target } return "" } type BalancerMsg struct { state protoimpl.MessageState `protogen:"open.v1"` Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"` PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BalancerMsg) Reset() { *x = BalancerMsg{} mi := &file_app_router_command_command_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BalancerMsg) String() string { return protoimpl.X.MessageStringOf(x) } func (*BalancerMsg) ProtoMessage() {} func (x *BalancerMsg) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BalancerMsg.ProtoReflect.Descriptor instead. func (*BalancerMsg) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{5} } func (x *BalancerMsg) GetOverride() *OverrideInfo { if x != nil { return x.Override } return nil } func (x *BalancerMsg) GetPrincipleTarget() *PrincipleTargetInfo { if x != nil { return x.PrincipleTarget } return nil } type GetBalancerInfoRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetBalancerInfoRequest) Reset() { *x = GetBalancerInfoRequest{} mi := &file_app_router_command_command_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetBalancerInfoRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetBalancerInfoRequest) ProtoMessage() {} func (x *GetBalancerInfoRequest) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetBalancerInfoRequest.ProtoReflect.Descriptor instead. func (*GetBalancerInfoRequest) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{6} } func (x *GetBalancerInfoRequest) GetTag() string { if x != nil { return x.Tag } return "" } type GetBalancerInfoResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetBalancerInfoResponse) Reset() { *x = GetBalancerInfoResponse{} mi := &file_app_router_command_command_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetBalancerInfoResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetBalancerInfoResponse) ProtoMessage() {} func (x *GetBalancerInfoResponse) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetBalancerInfoResponse.ProtoReflect.Descriptor instead. func (*GetBalancerInfoResponse) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{7} } func (x *GetBalancerInfoResponse) GetBalancer() *BalancerMsg { if x != nil { return x.Balancer } return nil } type OverrideBalancerTargetRequest struct { state protoimpl.MessageState `protogen:"open.v1"` BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"` Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OverrideBalancerTargetRequest) Reset() { *x = OverrideBalancerTargetRequest{} mi := &file_app_router_command_command_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OverrideBalancerTargetRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*OverrideBalancerTargetRequest) ProtoMessage() {} func (x *OverrideBalancerTargetRequest) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OverrideBalancerTargetRequest.ProtoReflect.Descriptor instead. func (*OverrideBalancerTargetRequest) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{8} } func (x *OverrideBalancerTargetRequest) GetBalancerTag() string { if x != nil { return x.BalancerTag } return "" } func (x *OverrideBalancerTargetRequest) GetTarget() string { if x != nil { return x.Target } return "" } type OverrideBalancerTargetResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OverrideBalancerTargetResponse) Reset() { *x = OverrideBalancerTargetResponse{} mi := &file_app_router_command_command_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OverrideBalancerTargetResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*OverrideBalancerTargetResponse) ProtoMessage() {} func (x *OverrideBalancerTargetResponse) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OverrideBalancerTargetResponse.ProtoReflect.Descriptor instead. func (*OverrideBalancerTargetResponse) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{9} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_router_command_command_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_router_command_command_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_router_command_command_proto_rawDescGZIP(), []int{10} } var File_app_router_command_command_proto protoreflect.FileDescriptor const file_app_router_command_command_proto_rawDesc = "" + "\n" + " app/router/command/command.proto\x12\x1dv2ray.core.app.router.command\x1a common/protoext/extensions.proto\x1a\x18common/net/network.proto\"\xa8\x04\n" + "\x0eRoutingContext\x12\x1e\n" + "\n" + "InboundTag\x18\x01 \x01(\tR\n" + "InboundTag\x128\n" + "\aNetwork\x18\x02 \x01(\x0e2\x1e.v2ray.core.common.net.NetworkR\aNetwork\x12\x1c\n" + "\tSourceIPs\x18\x03 \x03(\fR\tSourceIPs\x12\x1c\n" + "\tTargetIPs\x18\x04 \x03(\fR\tTargetIPs\x12\x1e\n" + "\n" + "SourcePort\x18\x05 \x01(\rR\n" + "SourcePort\x12\x1e\n" + "\n" + "TargetPort\x18\x06 \x01(\rR\n" + "TargetPort\x12\"\n" + "\fTargetDomain\x18\a \x01(\tR\fTargetDomain\x12\x1a\n" + "\bProtocol\x18\b \x01(\tR\bProtocol\x12\x12\n" + "\x04User\x18\t \x01(\tR\x04User\x12]\n" + "\n" + "Attributes\x18\n" + " \x03(\v2=.v2ray.core.app.router.command.RoutingContext.AttributesEntryR\n" + "Attributes\x12,\n" + "\x11OutboundGroupTags\x18\v \x03(\tR\x11OutboundGroupTags\x12 \n" + "\vOutboundTag\x18\f \x01(\tR\vOutboundTag\x1a=\n" + "\x0fAttributesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"F\n" + "\x1cSubscribeRoutingStatsRequest\x12&\n" + "\x0eFieldSelectors\x18\x01 \x03(\tR\x0eFieldSelectors\"\xb7\x01\n" + "\x10TestRouteRequest\x12U\n" + "\x0eRoutingContext\x18\x01 \x01(\v2-.v2ray.core.app.router.command.RoutingContextR\x0eRoutingContext\x12&\n" + "\x0eFieldSelectors\x18\x02 \x03(\tR\x0eFieldSelectors\x12$\n" + "\rPublishResult\x18\x03 \x01(\bR\rPublishResult\"'\n" + "\x13PrincipleTargetInfo\x12\x10\n" + "\x03tag\x18\x01 \x03(\tR\x03tag\"&\n" + "\fOverrideInfo\x12\x16\n" + "\x06target\x18\x02 \x01(\tR\x06target\"\xb5\x01\n" + "\vBalancerMsg\x12G\n" + "\boverride\x18\x05 \x01(\v2+.v2ray.core.app.router.command.OverrideInfoR\boverride\x12]\n" + "\x10principle_target\x18\x06 \x01(\v22.v2ray.core.app.router.command.PrincipleTargetInfoR\x0fprincipleTarget\"*\n" + "\x16GetBalancerInfoRequest\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\"a\n" + "\x17GetBalancerInfoResponse\x12F\n" + "\bbalancer\x18\x01 \x01(\v2*.v2ray.core.app.router.command.BalancerMsgR\bbalancer\"Y\n" + "\x1dOverrideBalancerTargetRequest\x12 \n" + "\vbalancerTag\x18\x01 \x01(\tR\vbalancerTag\x12\x16\n" + "\x06target\x18\x02 \x01(\tR\x06target\" \n" + "\x1eOverrideBalancerTargetResponse\"#\n" + "\x06Config:\x19\x82\xb5\x18\x15\n" + "\vgrpcservice\x12\x06router2\xa8\x04\n" + "\x0eRoutingService\x12\x87\x01\n" + "\x15SubscribeRoutingStats\x12;.v2ray.core.app.router.command.SubscribeRoutingStatsRequest\x1a-.v2ray.core.app.router.command.RoutingContext\"\x000\x01\x12m\n" + "\tTestRoute\x12/.v2ray.core.app.router.command.TestRouteRequest\x1a-.v2ray.core.app.router.command.RoutingContext\"\x00\x12\x82\x01\n" + "\x0fGetBalancerInfo\x125.v2ray.core.app.router.command.GetBalancerInfoRequest\x1a6.v2ray.core.app.router.command.GetBalancerInfoResponse\"\x00\x12\x97\x01\n" + "\x16OverrideBalancerTarget\x12<.v2ray.core.app.router.command.OverrideBalancerTargetRequest\x1a=.v2ray.core.app.router.command.OverrideBalancerTargetResponse\"\x00Bx\n" + "!com.v2ray.core.app.router.commandP\x01Z1github.com/v2fly/v2ray-core/v5/app/router/command\xaa\x02\x1dV2Ray.Core.App.Router.Commandb\x06proto3" var ( file_app_router_command_command_proto_rawDescOnce sync.Once file_app_router_command_command_proto_rawDescData []byte ) func file_app_router_command_command_proto_rawDescGZIP() []byte { file_app_router_command_command_proto_rawDescOnce.Do(func() { file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc))) }) return file_app_router_command_command_proto_rawDescData } var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_app_router_command_command_proto_goTypes = []any{ (*RoutingContext)(nil), // 0: v2ray.core.app.router.command.RoutingContext (*SubscribeRoutingStatsRequest)(nil), // 1: v2ray.core.app.router.command.SubscribeRoutingStatsRequest (*TestRouteRequest)(nil), // 2: v2ray.core.app.router.command.TestRouteRequest (*PrincipleTargetInfo)(nil), // 3: v2ray.core.app.router.command.PrincipleTargetInfo (*OverrideInfo)(nil), // 4: v2ray.core.app.router.command.OverrideInfo (*BalancerMsg)(nil), // 5: v2ray.core.app.router.command.BalancerMsg (*GetBalancerInfoRequest)(nil), // 6: v2ray.core.app.router.command.GetBalancerInfoRequest (*GetBalancerInfoResponse)(nil), // 7: v2ray.core.app.router.command.GetBalancerInfoResponse (*OverrideBalancerTargetRequest)(nil), // 8: v2ray.core.app.router.command.OverrideBalancerTargetRequest (*OverrideBalancerTargetResponse)(nil), // 9: v2ray.core.app.router.command.OverrideBalancerTargetResponse (*Config)(nil), // 10: v2ray.core.app.router.command.Config nil, // 11: v2ray.core.app.router.command.RoutingContext.AttributesEntry (net.Network)(0), // 12: v2ray.core.common.net.Network } var file_app_router_command_command_proto_depIdxs = []int32{ 12, // 0: v2ray.core.app.router.command.RoutingContext.Network:type_name -> v2ray.core.common.net.Network 11, // 1: v2ray.core.app.router.command.RoutingContext.Attributes:type_name -> v2ray.core.app.router.command.RoutingContext.AttributesEntry 0, // 2: v2ray.core.app.router.command.TestRouteRequest.RoutingContext:type_name -> v2ray.core.app.router.command.RoutingContext 4, // 3: v2ray.core.app.router.command.BalancerMsg.override:type_name -> v2ray.core.app.router.command.OverrideInfo 3, // 4: v2ray.core.app.router.command.BalancerMsg.principle_target:type_name -> v2ray.core.app.router.command.PrincipleTargetInfo 5, // 5: v2ray.core.app.router.command.GetBalancerInfoResponse.balancer:type_name -> v2ray.core.app.router.command.BalancerMsg 1, // 6: v2ray.core.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> v2ray.core.app.router.command.SubscribeRoutingStatsRequest 2, // 7: v2ray.core.app.router.command.RoutingService.TestRoute:input_type -> v2ray.core.app.router.command.TestRouteRequest 6, // 8: v2ray.core.app.router.command.RoutingService.GetBalancerInfo:input_type -> v2ray.core.app.router.command.GetBalancerInfoRequest 8, // 9: v2ray.core.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> v2ray.core.app.router.command.OverrideBalancerTargetRequest 0, // 10: v2ray.core.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> v2ray.core.app.router.command.RoutingContext 0, // 11: v2ray.core.app.router.command.RoutingService.TestRoute:output_type -> v2ray.core.app.router.command.RoutingContext 7, // 12: v2ray.core.app.router.command.RoutingService.GetBalancerInfo:output_type -> v2ray.core.app.router.command.GetBalancerInfoResponse 9, // 13: v2ray.core.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> v2ray.core.app.router.command.OverrideBalancerTargetResponse 10, // [10:14] is the sub-list for method output_type 6, // [6:10] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name } func init() { file_app_router_command_command_proto_init() } func file_app_router_command_command_proto_init() { if File_app_router_command_command_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc)), NumEnums: 0, NumMessages: 12, NumExtensions: 0, NumServices: 1, }, GoTypes: file_app_router_command_command_proto_goTypes, DependencyIndexes: file_app_router_command_command_proto_depIdxs, MessageInfos: file_app_router_command_command_proto_msgTypes, }.Build() File_app_router_command_command_proto = out.File file_app_router_command_command_proto_goTypes = nil file_app_router_command_command_proto_depIdxs = nil } ================================================ FILE: app/router/command/command.proto ================================================ syntax = "proto3"; package v2ray.core.app.router.command; option csharp_namespace = "V2Ray.Core.App.Router.Command"; option go_package = "github.com/v2fly/v2ray-core/v5/app/router/command"; option java_package = "com.v2ray.core.app.router.command"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "common/net/network.proto"; // RoutingContext is the context with information relative to routing process. // It conforms to the structure of v2ray.core.features.routing.Context and // v2ray.core.features.routing.Route. message RoutingContext { string InboundTag = 1; v2ray.core.common.net.Network Network = 2; repeated bytes SourceIPs = 3; repeated bytes TargetIPs = 4; uint32 SourcePort = 5; uint32 TargetPort = 6; string TargetDomain = 7; string Protocol = 8; string User = 9; map Attributes = 10; repeated string OutboundGroupTags = 11; string OutboundTag = 12; } // SubscribeRoutingStatsRequest subscribes to routing statistics channel if // opened by v2ray-core. // * FieldSelectors selects a subset of fields in routing statistics to return. // Valid selectors: // - inbound: Selects connection's inbound tag. // - network: Selects connection's network. // - ip: Equivalent as "ip_source" and "ip_target", selects both source and // target IP. // - port: Equivalent as "port_source" and "port_target", selects both source // and target port. // - domain: Selects target domain. // - protocol: Select connection's protocol. // - user: Select connection's inbound user email. // - attributes: Select connection's additional attributes. // - outbound: Equivalent as "outbound" and "outbound_group", select both // outbound tag and outbound group tags. // * If FieldSelectors is left empty, all fields will be returned. message SubscribeRoutingStatsRequest { repeated string FieldSelectors = 1; } // TestRouteRequest manually tests a routing result according to the routing // context message. // * RoutingContext is the routing message without outbound information. // * FieldSelectors selects the fields to return in the routing result. All // fields are returned if left empty. // * PublishResult broadcasts the routing result to routing statistics channel // if set true. message TestRouteRequest { RoutingContext RoutingContext = 1; repeated string FieldSelectors = 2; bool PublishResult = 3; } message PrincipleTargetInfo { repeated string tag = 1; } message OverrideInfo { string target = 2; } message BalancerMsg { OverrideInfo override = 5; PrincipleTargetInfo principle_target = 6; } message GetBalancerInfoRequest { string tag = 1; } message GetBalancerInfoResponse { BalancerMsg balancer = 1; } message OverrideBalancerTargetRequest { string balancerTag = 1; string target = 2; } message OverrideBalancerTargetResponse {} service RoutingService { rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest) returns (stream RoutingContext) {} rpc TestRoute(TestRouteRequest) returns (RoutingContext) {} rpc GetBalancerInfo(GetBalancerInfoRequest) returns (GetBalancerInfoResponse){} rpc OverrideBalancerTarget(OverrideBalancerTargetRequest) returns (OverrideBalancerTargetResponse) {} } message Config { option (v2ray.core.common.protoext.message_opt).type = "grpcservice"; option (v2ray.core.common.protoext.message_opt).short_name = "router"; } ================================================ FILE: app/router/command/command_grpc.pb.go ================================================ package command import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( RoutingService_SubscribeRoutingStats_FullMethodName = "/v2ray.core.app.router.command.RoutingService/SubscribeRoutingStats" RoutingService_TestRoute_FullMethodName = "/v2ray.core.app.router.command.RoutingService/TestRoute" RoutingService_GetBalancerInfo_FullMethodName = "/v2ray.core.app.router.command.RoutingService/GetBalancerInfo" RoutingService_OverrideBalancerTarget_FullMethodName = "/v2ray.core.app.router.command.RoutingService/OverrideBalancerTarget" ) // RoutingServiceClient is the client API for RoutingService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type RoutingServiceClient interface { SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[RoutingContext], error) TestRoute(ctx context.Context, in *TestRouteRequest, opts ...grpc.CallOption) (*RoutingContext, error) GetBalancerInfo(ctx context.Context, in *GetBalancerInfoRequest, opts ...grpc.CallOption) (*GetBalancerInfoResponse, error) OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error) } type routingServiceClient struct { cc grpc.ClientConnInterface } func NewRoutingServiceClient(cc grpc.ClientConnInterface) RoutingServiceClient { return &routingServiceClient{cc} } func (c *routingServiceClient) SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[RoutingContext], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &RoutingService_ServiceDesc.Streams[0], RoutingService_SubscribeRoutingStats_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[SubscribeRoutingStatsRequest, RoutingContext]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type RoutingService_SubscribeRoutingStatsClient = grpc.ServerStreamingClient[RoutingContext] func (c *routingServiceClient) TestRoute(ctx context.Context, in *TestRouteRequest, opts ...grpc.CallOption) (*RoutingContext, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RoutingContext) err := c.cc.Invoke(ctx, RoutingService_TestRoute_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *routingServiceClient) GetBalancerInfo(ctx context.Context, in *GetBalancerInfoRequest, opts ...grpc.CallOption) (*GetBalancerInfoResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetBalancerInfoResponse) err := c.cc.Invoke(ctx, RoutingService_GetBalancerInfo_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *routingServiceClient) OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(OverrideBalancerTargetResponse) err := c.cc.Invoke(ctx, RoutingService_OverrideBalancerTarget_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // RoutingServiceServer is the server API for RoutingService service. // All implementations must embed UnimplementedRoutingServiceServer // for forward compatibility. type RoutingServiceServer interface { SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) mustEmbedUnimplementedRoutingServiceServer() } // UnimplementedRoutingServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedRoutingServiceServer struct{} func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error { return status.Errorf(codes.Unimplemented, "method SubscribeRoutingStats not implemented") } func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) { return nil, status.Errorf(codes.Unimplemented, "method TestRoute not implemented") } func (UnimplementedRoutingServiceServer) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBalancerInfo not implemented") } func (UnimplementedRoutingServiceServer) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method OverrideBalancerTarget not implemented") } func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {} func (UnimplementedRoutingServiceServer) testEmbeddedByValue() {} // UnsafeRoutingServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to RoutingServiceServer will // result in compilation errors. type UnsafeRoutingServiceServer interface { mustEmbedUnimplementedRoutingServiceServer() } func RegisterRoutingServiceServer(s grpc.ServiceRegistrar, srv RoutingServiceServer) { // If the following call pancis, it indicates UnimplementedRoutingServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&RoutingService_ServiceDesc, srv) } func _RoutingService_SubscribeRoutingStats_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(SubscribeRoutingStatsRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(RoutingServiceServer).SubscribeRoutingStats(m, &grpc.GenericServerStream[SubscribeRoutingStatsRequest, RoutingContext]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type RoutingService_SubscribeRoutingStatsServer = grpc.ServerStreamingServer[RoutingContext] func _RoutingService_TestRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(TestRouteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(RoutingServiceServer).TestRoute(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: RoutingService_TestRoute_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RoutingServiceServer).TestRoute(ctx, req.(*TestRouteRequest)) } return interceptor(ctx, in, info, handler) } func _RoutingService_GetBalancerInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetBalancerInfoRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(RoutingServiceServer).GetBalancerInfo(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: RoutingService_GetBalancerInfo_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RoutingServiceServer).GetBalancerInfo(ctx, req.(*GetBalancerInfoRequest)) } return interceptor(ctx, in, info, handler) } func _RoutingService_OverrideBalancerTarget_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(OverrideBalancerTargetRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(RoutingServiceServer).OverrideBalancerTarget(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: RoutingService_OverrideBalancerTarget_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RoutingServiceServer).OverrideBalancerTarget(ctx, req.(*OverrideBalancerTargetRequest)) } return interceptor(ctx, in, info, handler) } // RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var RoutingService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "v2ray.core.app.router.command.RoutingService", HandlerType: (*RoutingServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "TestRoute", Handler: _RoutingService_TestRoute_Handler, }, { MethodName: "GetBalancerInfo", Handler: _RoutingService_GetBalancerInfo_Handler, }, { MethodName: "OverrideBalancerTarget", Handler: _RoutingService_OverrideBalancerTarget_Handler, }, }, Streams: []grpc.StreamDesc{ { StreamName: "SubscribeRoutingStats", Handler: _RoutingService_SubscribeRoutingStats_Handler, ServerStreams: true, }, }, Metadata: "app/router/command/command.proto", } ================================================ FILE: app/router/command/command_test.go ================================================ package command_test import ( "context" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/test/bufconn" "github.com/v2fly/v2ray-core/v5/app/router" . "github.com/v2fly/v2ray-core/v5/app/router/command" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/app/stats" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/testing/mocks" ) func TestServiceSubscribeRoutingStats(t *testing.T) { c := stats.NewChannel(&stats.ChannelConfig{ SubscriberLimit: 1, BufferSize: 0, Blocking: true, }) common.Must(c.Start()) defer c.Close() lis := bufconn.Listen(1024 * 1024) bufDialer := func(context.Context, string) (net.Conn, error) { return lis.Dial() } testCases := []*RoutingContext{ {InboundTag: "in", OutboundTag: "out"}, {TargetIPs: [][]byte{{1, 2, 3, 4}}, TargetPort: 8080, OutboundTag: "out"}, {TargetDomain: "example.com", TargetPort: 443, OutboundTag: "out"}, {SourcePort: 9999, TargetPort: 9999, OutboundTag: "out"}, {Network: net.Network_UDP, OutboundGroupTags: []string{"outergroup", "innergroup"}, OutboundTag: "out"}, {Protocol: "bittorrent", OutboundTag: "blocked"}, {User: "example@v2fly.org", OutboundTag: "out"}, {SourceIPs: [][]byte{{127, 0, 0, 1}}, Attributes: map[string]string{"attr": "value"}, OutboundTag: "out"}, } errCh := make(chan error) nextPub := make(chan struct{}) // Server goroutine go func() { server := grpc.NewServer() RegisterRoutingServiceServer(server, NewRoutingServer(nil, c)) errCh <- server.Serve(lis) }() // Publisher goroutine go func() { publishTestCases := func() error { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() for { // Wait until there's one subscriber in routing stats channel if len(c.Subscribers()) > 0 { break } if ctx.Err() != nil { return ctx.Err() } } for _, tc := range testCases { c.Publish(context.Background(), AsRoutingRoute(tc)) time.Sleep(time.Millisecond) } return nil } if err := publishTestCases(); err != nil { errCh <- err } // Wait for next round of publishing <-nextPub if err := publishTestCases(); err != nil { errCh <- err } }() // Client goroutine go func() { defer lis.Close() conn, err := grpc.DialContext( context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { errCh <- err return } defer conn.Close() client := NewRoutingServiceClient(conn) // Test retrieving all fields testRetrievingAllFields := func() error { streamCtx, streamClose := context.WithCancel(context.Background()) // Test the unsubscription of stream works well defer func() { streamClose() timeOutCtx, timeout := context.WithTimeout(context.Background(), time.Second) defer timeout() for { // Wait until there's no subscriber in routing stats channel if len(c.Subscribers()) == 0 { break } if timeOutCtx.Err() != nil { t.Error("unexpected subscribers not decreased in channel", timeOutCtx.Err()) } } }() stream, err := client.SubscribeRoutingStats(streamCtx, &SubscribeRoutingStatsRequest{}) if err != nil { return err } for _, tc := range testCases { msg, err := stream.Recv() if err != nil { return err } if r := cmp.Diff(msg, tc, cmpopts.IgnoreUnexported(RoutingContext{})); r != "" { t.Error(r) } } // Test that double subscription will fail errStream, err := client.SubscribeRoutingStats(context.Background(), &SubscribeRoutingStatsRequest{ FieldSelectors: []string{"ip", "port", "domain", "outbound"}, }) if err != nil { return err } if _, err := errStream.Recv(); err == nil { t.Error("unexpected successful subscription") } return nil } // Test retrieving only a subset of fields testRetrievingSubsetOfFields := func() error { streamCtx, streamClose := context.WithCancel(context.Background()) defer streamClose() stream, err := client.SubscribeRoutingStats(streamCtx, &SubscribeRoutingStatsRequest{ FieldSelectors: []string{"ip", "port", "domain", "outbound"}, }) if err != nil { return err } // Send nextPub signal to start next round of publishing close(nextPub) for _, tc := range testCases { msg, err := stream.Recv() if err != nil { return err } stat := &RoutingContext{ // Only a subset of stats is retrieved SourceIPs: tc.SourceIPs, TargetIPs: tc.TargetIPs, SourcePort: tc.SourcePort, TargetPort: tc.TargetPort, TargetDomain: tc.TargetDomain, OutboundGroupTags: tc.OutboundGroupTags, OutboundTag: tc.OutboundTag, } if r := cmp.Diff(msg, stat, cmpopts.IgnoreUnexported(RoutingContext{})); r != "" { t.Error(r) } } return nil } if err := testRetrievingAllFields(); err != nil { errCh <- err } if err := testRetrievingSubsetOfFields(); err != nil { errCh <- err } errCh <- nil // Client passed all tests successfully }() // Wait for goroutines to complete select { case <-time.After(2 * time.Second): t.Fatal("Test timeout after 2s") case err := <-errCh: if err != nil { t.Fatal(err) } } } func TestSerivceTestRoute(t *testing.T) { c := stats.NewChannel(&stats.ChannelConfig{ SubscriberLimit: 1, BufferSize: 16, Blocking: true, }) common.Must(c.Start()) defer c.Close() r := new(router.Router) mockCtl := gomock.NewController(t) defer mockCtl.Finish() common.Must(r.Init(context.TODO(), &router.Config{ Rule: []*router.RoutingRule{ { InboundTag: []string{"in"}, TargetTag: &router.RoutingRule_Tag{Tag: "out"}, }, { Protocol: []string{"bittorrent"}, TargetTag: &router.RoutingRule_Tag{Tag: "blocked"}, }, { PortList: &net.PortList{Range: []*net.PortRange{{From: 8080, To: 8080}}}, TargetTag: &router.RoutingRule_Tag{Tag: "out"}, }, { SourcePortList: &net.PortList{Range: []*net.PortRange{{From: 9999, To: 9999}}}, TargetTag: &router.RoutingRule_Tag{Tag: "out"}, }, { Domain: []*routercommon.Domain{{Type: routercommon.Domain_RootDomain, Value: "com"}}, TargetTag: &router.RoutingRule_Tag{Tag: "out"}, }, { SourceGeoip: []*routercommon.GeoIP{{CountryCode: "private", Cidr: []*routercommon.CIDR{{Ip: []byte{127, 0, 0, 0}, Prefix: 8}}}}, TargetTag: &router.RoutingRule_Tag{Tag: "out"}, }, { UserEmail: []string{"example@v2fly.org"}, TargetTag: &router.RoutingRule_Tag{Tag: "out"}, }, { Networks: []net.Network{net.Network_UDP, net.Network_TCP}, TargetTag: &router.RoutingRule_Tag{Tag: "out"}, }, }, }, mocks.NewDNSClient(mockCtl), mocks.NewOutboundManager(mockCtl), nil)) lis := bufconn.Listen(1024 * 1024) bufDialer := func(context.Context, string) (net.Conn, error) { return lis.Dial() } errCh := make(chan error) // Server goroutine go func() { server := grpc.NewServer() RegisterRoutingServiceServer(server, NewRoutingServer(r, c)) errCh <- server.Serve(lis) }() // Client goroutine go func() { defer lis.Close() conn, err := grpc.DialContext( context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { errCh <- err } defer conn.Close() client := NewRoutingServiceClient(conn) testCases := []*RoutingContext{ {InboundTag: "in", OutboundTag: "out"}, {TargetIPs: [][]byte{{1, 2, 3, 4}}, TargetPort: 8080, OutboundTag: "out"}, {TargetDomain: "example.com", TargetPort: 443, OutboundTag: "out"}, {SourcePort: 9999, TargetPort: 9999, OutboundTag: "out"}, {Network: net.Network_UDP, Protocol: "bittorrent", OutboundTag: "blocked"}, {User: "example@v2fly.org", OutboundTag: "out"}, {SourceIPs: [][]byte{{127, 0, 0, 1}}, Attributes: map[string]string{"attr": "value"}, OutboundTag: "out"}, } // Test simple TestRoute testSimple := func() error { for _, tc := range testCases { route, err := client.TestRoute(context.Background(), &TestRouteRequest{RoutingContext: tc}) if err != nil { return err } if r := cmp.Diff(route, tc, cmpopts.IgnoreUnexported(RoutingContext{})); r != "" { t.Error(r) } } return nil } // Test TestRoute with special options testOptions := func() error { sub, err := c.Subscribe() if err != nil { return err } for _, tc := range testCases { route, err := client.TestRoute(context.Background(), &TestRouteRequest{ RoutingContext: tc, FieldSelectors: []string{"ip", "port", "domain", "outbound"}, PublishResult: true, }) if err != nil { return err } stat := &RoutingContext{ // Only a subset of stats is retrieved SourceIPs: tc.SourceIPs, TargetIPs: tc.TargetIPs, SourcePort: tc.SourcePort, TargetPort: tc.TargetPort, TargetDomain: tc.TargetDomain, OutboundGroupTags: tc.OutboundGroupTags, OutboundTag: tc.OutboundTag, } if r := cmp.Diff(route, stat, cmpopts.IgnoreUnexported(RoutingContext{})); r != "" { t.Error(r) } select { // Check that routing result has been published to statistics channel case msg, received := <-sub: if route, ok := msg.(routing.Route); received && ok { if r := cmp.Diff(AsProtobufMessage(nil)(route), tc, cmpopts.IgnoreUnexported(RoutingContext{})); r != "" { t.Error(r) } } else { t.Error("unexpected failure in receiving published routing result for testcase", tc) } case <-time.After(100 * time.Millisecond): t.Error("unexpected failure in receiving published routing result", tc) } } return nil } if err := testSimple(); err != nil { errCh <- err } if err := testOptions(); err != nil { errCh <- err } errCh <- nil // Client passed all tests successfully }() // Wait for goroutines to complete select { case <-time.After(2 * time.Second): t.Fatal("Test timeout after 2s") case err := <-errCh: if err != nil { t.Fatal(err) } } } ================================================ FILE: app/router/command/config.go ================================================ package command import ( "strings" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/routing" ) // routingContext is an wrapper of protobuf RoutingContext as implementation of routing.Context and routing.Route. type routingContext struct { *RoutingContext } func (c routingContext) GetSourceIPs() []net.IP { return mapBytesToIPs(c.RoutingContext.GetSourceIPs()) } func (c routingContext) GetSourcePort() net.Port { return net.Port(c.RoutingContext.GetSourcePort()) } func (c routingContext) GetTargetIPs() []net.IP { return mapBytesToIPs(c.RoutingContext.GetTargetIPs()) } func (c routingContext) GetTargetPort() net.Port { return net.Port(c.RoutingContext.GetTargetPort()) } // GetSkipDNSResolve is a mock implementation here to match the interface, // SkipDNSResolve is set from dns module, no use if coming from a protobuf object? // TODO: please confirm @Vigilans func (c routingContext) GetSkipDNSResolve() bool { return false } // AsRoutingContext converts a protobuf RoutingContext into an implementation of routing.Context. func AsRoutingContext(r *RoutingContext) routing.Context { return routingContext{r} } // AsRoutingRoute converts a protobuf RoutingContext into an implementation of routing.Route. func AsRoutingRoute(r *RoutingContext) routing.Route { return routingContext{r} } var fieldMap = map[string]func(*RoutingContext, routing.Route){ "inbound": func(s *RoutingContext, r routing.Route) { s.InboundTag = r.GetInboundTag() }, "network": func(s *RoutingContext, r routing.Route) { s.Network = r.GetNetwork() }, "ip_source": func(s *RoutingContext, r routing.Route) { s.SourceIPs = mapIPsToBytes(r.GetSourceIPs()) }, "ip_target": func(s *RoutingContext, r routing.Route) { s.TargetIPs = mapIPsToBytes(r.GetTargetIPs()) }, "port_source": func(s *RoutingContext, r routing.Route) { s.SourcePort = uint32(r.GetSourcePort()) }, "port_target": func(s *RoutingContext, r routing.Route) { s.TargetPort = uint32(r.GetTargetPort()) }, "domain": func(s *RoutingContext, r routing.Route) { s.TargetDomain = r.GetTargetDomain() }, "protocol": func(s *RoutingContext, r routing.Route) { s.Protocol = r.GetProtocol() }, "user": func(s *RoutingContext, r routing.Route) { s.User = r.GetUser() }, "attributes": func(s *RoutingContext, r routing.Route) { s.Attributes = r.GetAttributes() }, "outbound_group": func(s *RoutingContext, r routing.Route) { s.OutboundGroupTags = r.GetOutboundGroupTags() }, "outbound": func(s *RoutingContext, r routing.Route) { s.OutboundTag = r.GetOutboundTag() }, } // AsProtobufMessage takes selectors of fields and returns a function to convert routing.Route to protobuf RoutingContext. func AsProtobufMessage(fieldSelectors []string) func(routing.Route) *RoutingContext { initializers := []func(*RoutingContext, routing.Route){} for field, init := range fieldMap { if len(fieldSelectors) == 0 { // If selectors not set, retrieve all fields initializers = append(initializers, init) continue } for _, selector := range fieldSelectors { if strings.HasPrefix(field, selector) { initializers = append(initializers, init) break } } } return func(ctx routing.Route) *RoutingContext { message := new(RoutingContext) for _, init := range initializers { init(message, ctx) } return message } } func mapBytesToIPs(bytes [][]byte) []net.IP { var ips []net.IP for _, rawIP := range bytes { ips = append(ips, net.IP(rawIP)) } return ips } func mapIPsToBytes(ips []net.IP) [][]byte { var bytes [][]byte for _, ip := range ips { bytes = append(bytes, []byte(ip)) } return bytes } ================================================ FILE: app/router/command/errors.generated.go ================================================ package command import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/router/condition.go ================================================ package router import ( "strings" "go.starlark.net/starlark" "go.starlark.net/syntax" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/strmatcher" "github.com/v2fly/v2ray-core/v5/features/routing" ) type Condition interface { Apply(ctx routing.Context) bool } type ConditionChan []Condition func NewConditionChan() *ConditionChan { var condChan ConditionChan = make([]Condition, 0, 8) return &condChan } func (v *ConditionChan) Add(cond Condition) *ConditionChan { *v = append(*v, cond) return v } // Apply applies all conditions registered in this chan. func (v *ConditionChan) Apply(ctx routing.Context) bool { for _, cond := range *v { if !cond.Apply(ctx) { return false } } return true } func (v *ConditionChan) Len() int { return len(*v) } var matcherTypeMap = map[routercommon.Domain_Type]strmatcher.Type{ routercommon.Domain_Plain: strmatcher.Substr, routercommon.Domain_Regex: strmatcher.Regex, routercommon.Domain_RootDomain: strmatcher.Domain, routercommon.Domain_Full: strmatcher.Full, } func domainToMatcher(domain *routercommon.Domain) (strmatcher.Matcher, error) { matcherType, f := matcherTypeMap[domain.Type] if !f { return nil, newError("unsupported domain type", domain.Type) } matcher, err := matcherType.New(domain.Value) if err != nil { return nil, newError("failed to create domain matcher").Base(err) } return matcher, nil } type DomainMatcher struct { matcher strmatcher.IndexMatcher } func NewDomainMatcher(matcherType string, domains []*routercommon.Domain) (*DomainMatcher, error) { var indexMatcher strmatcher.IndexMatcher switch matcherType { case "mph", "hybrid": indexMatcher = strmatcher.NewMphIndexMatcher() case "linear": indexMatcher = strmatcher.NewLinearIndexMatcher() default: indexMatcher = strmatcher.NewLinearIndexMatcher() } for _, domain := range domains { matcher, err := domainToMatcher(domain) if err != nil { return nil, err } indexMatcher.Add(matcher) } if err := indexMatcher.Build(); err != nil { return nil, err } return &DomainMatcher{matcher: indexMatcher}, nil } func (m *DomainMatcher) Match(domain string) bool { return m.matcher.MatchAny(domain) } // Apply implements Condition. func (m *DomainMatcher) Apply(ctx routing.Context) bool { domain := ctx.GetTargetDomain() if len(domain) == 0 { return false } return m.Match(domain) } type MultiGeoIPMatcher struct { matchers []*GeoIPMatcher onSource bool } func NewMultiGeoIPMatcher(geoips []*routercommon.GeoIP, onSource bool) (*MultiGeoIPMatcher, error) { var matchers []*GeoIPMatcher for _, geoip := range geoips { matcher, err := globalGeoIPContainer.Add(geoip) if err != nil { return nil, err } matchers = append(matchers, matcher) } matcher := &MultiGeoIPMatcher{ matchers: matchers, onSource: onSource, } return matcher, nil } // Apply implements Condition. func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool { var ips []net.IP if m.onSource { ips = ctx.GetSourceIPs() } else { ips = ctx.GetTargetIPs() } for _, ip := range ips { for _, matcher := range m.matchers { if matcher.Match(ip) { return true } } } return false } type PortMatcher struct { port net.MemoryPortList onSource bool } // NewPortMatcher creates a new port matcher that can match source or destination port func NewPortMatcher(list *net.PortList, onSource bool) *PortMatcher { return &PortMatcher{ port: net.PortListFromProto(list), onSource: onSource, } } // Apply implements Condition. func (v *PortMatcher) Apply(ctx routing.Context) bool { if v.onSource { return v.port.Contains(ctx.GetSourcePort()) } return v.port.Contains(ctx.GetTargetPort()) } type NetworkMatcher struct { list [8]bool } func NewNetworkMatcher(network []net.Network) NetworkMatcher { var matcher NetworkMatcher for _, n := range network { matcher.list[int(n)] = true } return matcher } // Apply implements Condition. func (v NetworkMatcher) Apply(ctx routing.Context) bool { return v.list[int(ctx.GetNetwork())] } type UserMatcher struct { user []string } func NewUserMatcher(users []string) *UserMatcher { usersCopy := make([]string, 0, len(users)) for _, user := range users { if len(user) > 0 { usersCopy = append(usersCopy, user) } } return &UserMatcher{ user: usersCopy, } } // Apply implements Condition. func (v *UserMatcher) Apply(ctx routing.Context) bool { user := ctx.GetUser() if len(user) == 0 { return false } for _, u := range v.user { if u == user { return true } } return false } type InboundTagMatcher struct { tags []string } func NewInboundTagMatcher(tags []string) *InboundTagMatcher { tagsCopy := make([]string, 0, len(tags)) for _, tag := range tags { if len(tag) > 0 { tagsCopy = append(tagsCopy, tag) } } return &InboundTagMatcher{ tags: tagsCopy, } } // Apply implements Condition. func (v *InboundTagMatcher) Apply(ctx routing.Context) bool { tag := ctx.GetInboundTag() if len(tag) == 0 { return false } for _, t := range v.tags { if t == tag { return true } } return false } type ProtocolMatcher struct { protocols []string } func NewProtocolMatcher(protocols []string) *ProtocolMatcher { pCopy := make([]string, 0, len(protocols)) for _, p := range protocols { if len(p) > 0 { pCopy = append(pCopy, p) } } return &ProtocolMatcher{ protocols: pCopy, } } // Apply implements Condition. func (m *ProtocolMatcher) Apply(ctx routing.Context) bool { protocol := ctx.GetProtocol() if len(protocol) == 0 { return false } for _, p := range m.protocols { if strings.HasPrefix(protocol, p) { return true } } return false } type AttributeMatcher struct { program *starlark.Program } func NewAttributeMatcher(code string) (*AttributeMatcher, error) { starFile, err := syntax.Parse("attr.star", "satisfied=("+code+")", 0) if err != nil { return nil, newError("attr rule").Base(err) } p, err := starlark.FileProgram(starFile, func(name string) bool { return name == "attrs" }) if err != nil { return nil, err } return &AttributeMatcher{ program: p, }, nil } // Match implements attributes matching. func (m *AttributeMatcher) Match(attrs map[string]string) bool { attrsDict := new(starlark.Dict) for key, value := range attrs { attrsDict.SetKey(starlark.String(key), starlark.String(value)) } predefined := make(starlark.StringDict) predefined["attrs"] = attrsDict thread := &starlark.Thread{ Name: "matcher", } results, err := m.program.Init(thread, predefined) if err != nil { newError("attr matcher").Base(err).WriteToLog() } satisfied := results["satisfied"] return satisfied != nil && bool(satisfied.Truth()) } // Apply implements Condition. func (m *AttributeMatcher) Apply(ctx routing.Context) bool { attributes := ctx.GetAttributes() if attributes == nil { return false } return m.Match(attributes) } ================================================ FILE: app/router/condition_geoip.go ================================================ package router import ( "net/netip" "go4.org/netipx" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common/net" ) type GeoIPMatcher struct { countryCode string reverseMatch bool ip4 *netipx.IPSet ip6 *netipx.IPSet } func (m *GeoIPMatcher) Init(cidrs []*routercommon.CIDR) error { var builder4, builder6 netipx.IPSetBuilder for _, cidr := range cidrs { netaddrIP, ok := netip.AddrFromSlice(cidr.GetIp()) if !ok { return newError("invalid IP address ", cidr) } netaddrIP = netaddrIP.Unmap() ipPrefix := netip.PrefixFrom(netaddrIP, int(cidr.GetPrefix())) switch { case netaddrIP.Is4(): builder4.AddPrefix(ipPrefix) case netaddrIP.Is6(): builder6.AddPrefix(ipPrefix) } } var err error m.ip4, err = builder4.IPSet() if err != nil { return err } m.ip6, err = builder6.IPSet() if err != nil { return err } return nil } func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) { m.reverseMatch = isReverseMatch } func (m *GeoIPMatcher) match4(ip net.IP) bool { nip, ok := netipx.FromStdIP(ip) if !ok { return false } return m.ip4.Contains(nip) } func (m *GeoIPMatcher) match6(ip net.IP) bool { nip, ok := netipx.FromStdIP(ip) if !ok { return false } return m.ip6.Contains(nip) } // Match returns true if the given ip is included by the GeoIP. func (m *GeoIPMatcher) Match(ip net.IP) bool { isMatched := false switch len(ip) { case net.IPv4len: isMatched = m.match4(ip) case net.IPv6len: isMatched = m.match6(ip) } if m.reverseMatch { return !isMatched } return isMatched } // GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code. type GeoIPMatcherContainer struct { matchers []*GeoIPMatcher } // Add adds a new GeoIP set into the container. // If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one. func (c *GeoIPMatcherContainer) Add(geoip *routercommon.GeoIP) (*GeoIPMatcher, error) { if geoip.CountryCode != "" { for _, m := range c.matchers { if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.InverseMatch { return m, nil } } } m := &GeoIPMatcher{ countryCode: geoip.CountryCode, reverseMatch: geoip.InverseMatch, } if err := m.Init(geoip.Cidr); err != nil { return nil, err } if geoip.CountryCode != "" { c.matchers = append(c.matchers, m) } return m, nil } var globalGeoIPContainer GeoIPMatcherContainer ================================================ FILE: app/router/condition_geoip_test.go ================================================ package router_test import ( "errors" "io/fs" "os" "path/filepath" "strings" "testing" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" ) func init() { const geoipURL = "https://github.com/v2fly/geoip/releases/download/202507050144/geoip.dat" wd, err := os.Getwd() common.Must(err) tempPath := filepath.Join(wd, "..", "..", "testing", "temp") geoipPath := filepath.Join(tempPath, "geoip-202507050144.dat") os.Setenv("v2ray.location.asset", tempPath) if _, err := os.Stat(geoipPath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geoipBytes, err := common.FetchHTTPContent(geoipURL) common.Must(err) common.Must(filesystem.WriteFile(geoipPath, geoipBytes)) } } func TestGeoIPMatcherContainer(t *testing.T) { container := &router.GeoIPMatcherContainer{} m1, err := container.Add(&routercommon.GeoIP{ CountryCode: "CN", }) common.Must(err) m2, err := container.Add(&routercommon.GeoIP{ CountryCode: "US", }) common.Must(err) m3, err := container.Add(&routercommon.GeoIP{ CountryCode: "CN", }) common.Must(err) if m1 != m3 { t.Error("expect same matcher for same geoip, but not") } if m1 == m2 { t.Error("expect different matcher for different geoip, but actually same") } } func TestGeoIPMatcher(t *testing.T) { cidrList := []*routercommon.CIDR{ {Ip: []byte{0, 0, 0, 0}, Prefix: 8}, {Ip: []byte{10, 0, 0, 0}, Prefix: 8}, {Ip: []byte{100, 64, 0, 0}, Prefix: 10}, {Ip: []byte{127, 0, 0, 0}, Prefix: 8}, {Ip: []byte{169, 254, 0, 0}, Prefix: 16}, {Ip: []byte{172, 16, 0, 0}, Prefix: 12}, {Ip: []byte{192, 0, 0, 0}, Prefix: 24}, {Ip: []byte{192, 0, 2, 0}, Prefix: 24}, {Ip: []byte{192, 168, 0, 0}, Prefix: 16}, {Ip: []byte{192, 18, 0, 0}, Prefix: 15}, {Ip: []byte{198, 51, 100, 0}, Prefix: 24}, {Ip: []byte{203, 0, 113, 0}, Prefix: 24}, {Ip: []byte{8, 8, 8, 8}, Prefix: 32}, {Ip: []byte{91, 108, 4, 0}, Prefix: 16}, } matcher := &router.GeoIPMatcher{} common.Must(matcher.Init(cidrList)) testCases := []struct { Input string Output bool }{ { Input: "192.168.1.1", Output: true, }, { Input: "192.0.0.0", Output: true, }, { Input: "192.0.1.0", Output: false, }, { Input: "0.1.0.0", Output: true, }, { Input: "1.0.0.1", Output: false, }, { Input: "8.8.8.7", Output: false, }, { Input: "8.8.8.8", Output: true, }, { Input: "2001:cdba::3257:9652", Output: false, }, { Input: "91.108.255.254", Output: true, }, } for _, testCase := range testCases { ip := net.ParseAddress(testCase.Input).IP() actual := matcher.Match(ip) if actual != testCase.Output { t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual) } } } func TestGeoIPReverseMatcher(t *testing.T) { cidrList := []*routercommon.CIDR{ {Ip: []byte{8, 8, 8, 8}, Prefix: 32}, {Ip: []byte{91, 108, 4, 0}, Prefix: 16}, } matcher := &router.GeoIPMatcher{} matcher.SetReverseMatch(true) // Reverse match common.Must(matcher.Init(cidrList)) testCases := []struct { Input string Output bool }{ { Input: "8.8.8.8", Output: false, }, { Input: "2001:cdba::3257:9652", Output: true, }, { Input: "91.108.255.254", Output: false, }, } for _, testCase := range testCases { ip := net.ParseAddress(testCase.Input).IP() actual := matcher.Match(ip) if actual != testCase.Output { t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual) } } } func TestGeoIPMatcher4CN(t *testing.T) { ips, err := loadGeoIP("CN") common.Must(err) matcher := &router.GeoIPMatcher{} common.Must(matcher.Init(ips)) if matcher.Match([]byte{8, 8, 8, 8}) { t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does") } } func TestGeoIPMatcher6US(t *testing.T) { ips, err := loadGeoIP("US") common.Must(err) matcher := &router.GeoIPMatcher{} common.Must(matcher.Init(ips)) if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) { t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not") } } func loadGeoIP(country string) ([]*routercommon.CIDR, error) { geoipBytes, err := filesystem.ReadAsset("geoip-202507050144.dat") if err != nil { return nil, err } var geoipList routercommon.GeoIPList if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { return nil, err } for _, geoip := range geoipList.Entry { if strings.EqualFold(geoip.CountryCode, country) { return geoip.Cidr, nil } } panic("country not found: " + country) } func BenchmarkGeoIPMatcher4CN(b *testing.B) { ips, err := loadGeoIP("CN") common.Must(err) matcher := &router.GeoIPMatcher{} common.Must(matcher.Init(ips)) b.ResetTimer() for i := 0; i < b.N; i++ { _ = matcher.Match([]byte{8, 8, 8, 8}) } } func BenchmarkGeoIPMatcher6US(b *testing.B) { ips, err := loadGeoIP("US") common.Must(err) matcher := &router.GeoIPMatcher{} common.Must(matcher.Init(ips)) b.ResetTimer() for i := 0; i < b.N; i++ { _ = matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) } } ================================================ FILE: app/router/condition_test.go ================================================ package router_test import ( "errors" "io/fs" "os" "path/filepath" "strconv" "strings" "testing" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/protocol/http" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/routing" routing_session "github.com/v2fly/v2ray-core/v5/features/routing/session" ) func init() { const ( geoipURL = "https://raw.githubusercontent.com/v2fly/geoip/release/geoip.dat" geositeURL = "https://raw.githubusercontent.com/v2fly/domain-list-community/release/dlc.dat" ) wd, err := os.Getwd() common.Must(err) tempPath := filepath.Join(wd, "..", "..", "testing", "temp") geoipPath := filepath.Join(tempPath, "geoip.dat") geositePath := filepath.Join(tempPath, "geosite.dat") os.Setenv("v2ray.location.asset", tempPath) if _, err := os.Stat(geoipPath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geoipBytes, err := common.FetchHTTPContent(geoipURL) common.Must(err) common.Must(filesystem.WriteFile(geoipPath, geoipBytes)) } if _, err := os.Stat(geositePath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geositeBytes, err := common.FetchHTTPContent(geositeURL) common.Must(err) common.Must(filesystem.WriteFile(geositePath, geositeBytes)) } } func withBackground() routing.Context { return &routing_session.Context{} } func withOutbound(outbound *session.Outbound) routing.Context { return &routing_session.Context{Outbound: outbound} } func withInbound(inbound *session.Inbound) routing.Context { return &routing_session.Context{Inbound: inbound} } func withContent(content *session.Content) routing.Context { return &routing_session.Context{Content: content} } func TestRoutingRule(t *testing.T) { type ruleTest struct { input routing.Context output bool } cases := []struct { rule *router.RoutingRule test []ruleTest }{ { rule: &router.RoutingRule{ Domain: []*routercommon.Domain{ { Value: "v2fly.org", Type: routercommon.Domain_Plain, }, { Value: "google.com", Type: routercommon.Domain_RootDomain, }, { Value: "^facebook\\.com$", Type: routercommon.Domain_Regex, }, }, }, test: []ruleTest{ { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2fly.org"), 80)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("www.v2fly.org.www"), 80)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.co"), 80)}), output: false, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("www.google.com"), 80)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("facebook.com"), 80)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("www.facebook.com"), 80)}), output: false, }, { input: withBackground(), output: false, }, }, }, { rule: &router.RoutingRule{ Cidr: []*routercommon.CIDR{ { Ip: []byte{8, 8, 8, 8}, Prefix: 32, }, { Ip: []byte{8, 8, 8, 8}, Prefix: 32, }, { Ip: net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(), Prefix: 128, }, }, }, test: []ruleTest{ { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.4.4"), 80)}), output: false, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 80)}), output: true, }, { input: withBackground(), output: false, }, }, }, { rule: &router.RoutingRule{ Geoip: []*routercommon.GeoIP{ { Cidr: []*routercommon.CIDR{ { Ip: []byte{8, 8, 8, 8}, Prefix: 32, }, { Ip: []byte{8, 8, 8, 8}, Prefix: 32, }, { Ip: net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(), Prefix: 128, }, }, }, }, }, test: []ruleTest{ { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.4.4"), 80)}), output: false, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 80)}), output: true, }, { input: withBackground(), output: false, }, }, }, { rule: &router.RoutingRule{ SourceCidr: []*routercommon.CIDR{ { Ip: []byte{192, 168, 0, 0}, Prefix: 16, }, }, }, test: []ruleTest{ { input: withInbound(&session.Inbound{Source: net.TCPDestination(net.ParseAddress("192.168.0.1"), 80)}), output: true, }, { input: withInbound(&session.Inbound{Source: net.TCPDestination(net.ParseAddress("10.0.0.1"), 80)}), output: false, }, }, }, { rule: &router.RoutingRule{ UserEmail: []string{ "admin@v2fly.org", }, }, test: []ruleTest{ { input: withInbound(&session.Inbound{User: &protocol.MemoryUser{Email: "admin@v2fly.org"}}), output: true, }, { input: withInbound(&session.Inbound{User: &protocol.MemoryUser{Email: "love@v2fly.org"}}), output: false, }, { input: withBackground(), output: false, }, }, }, { rule: &router.RoutingRule{ Protocol: []string{"http"}, }, test: []ruleTest{ { input: withContent(&session.Content{Protocol: (&http.SniffHeader{}).Protocol()}), output: true, }, }, }, { rule: &router.RoutingRule{ InboundTag: []string{"test", "test1"}, }, test: []ruleTest{ { input: withInbound(&session.Inbound{Tag: "test"}), output: true, }, { input: withInbound(&session.Inbound{Tag: "test2"}), output: false, }, }, }, { rule: &router.RoutingRule{ PortList: &net.PortList{ Range: []*net.PortRange{ {From: 443, To: 443}, {From: 1000, To: 1100}, }, }, }, test: []ruleTest{ { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 443)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 1100)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 1005)}), output: true, }, { input: withOutbound(&session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 53)}), output: false, }, }, }, { rule: &router.RoutingRule{ SourcePortList: &net.PortList{ Range: []*net.PortRange{ {From: 123, To: 123}, {From: 9993, To: 9999}, }, }, }, test: []ruleTest{ { input: withInbound(&session.Inbound{Source: net.UDPDestination(net.LocalHostIP, 123)}), output: true, }, { input: withInbound(&session.Inbound{Source: net.UDPDestination(net.LocalHostIP, 9999)}), output: true, }, { input: withInbound(&session.Inbound{Source: net.UDPDestination(net.LocalHostIP, 9994)}), output: true, }, { input: withInbound(&session.Inbound{Source: net.UDPDestination(net.LocalHostIP, 53)}), output: false, }, }, }, { rule: &router.RoutingRule{ Protocol: []string{"http"}, Attributes: "attrs[':path'].startswith('/test')", }, test: []ruleTest{ { input: withContent(&session.Content{Protocol: "http/1.1", Attributes: map[string]string{":path": "/test/1"}}), output: true, }, }, }, } for _, test := range cases { cond, err := test.rule.BuildCondition() common.Must(err) for _, subtest := range test.test { actual := cond.Apply(subtest.input) if actual != subtest.output { t.Error("test case failed: ", subtest.input, " expected ", subtest.output, " but got ", actual) } } } } func loadGeoSite(country string) ([]*routercommon.Domain, error) { geositeBytes, err := filesystem.ReadAsset("geosite.dat") if err != nil { return nil, err } var geositeList routercommon.GeoSiteList if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { return nil, err } for _, site := range geositeList.Entry { if strings.EqualFold(site.CountryCode, country) { return site.Domain, nil } } return nil, errors.New("country not found: " + country) } func TestChinaSites(t *testing.T) { domains, err := loadGeoSite("CN") common.Must(err) matcher, err := router.NewDomainMatcher("linear", domains) common.Must(err) mphMatcher, err := router.NewDomainMatcher("mph", domains) common.Must(err) type TestCase struct { Domain string Output bool } testCases := []TestCase{ { Domain: "163.com", Output: true, }, { Domain: "163.com", Output: true, }, { Domain: "164.com", Output: false, }, { Domain: "164.com", Output: false, }, } for i := 0; i < 1024; i++ { testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false}) } for _, testCase := range testCases { r1 := matcher.Match(testCase.Domain) r2 := mphMatcher.Match(testCase.Domain) if r1 != testCase.Output { t.Error("DomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r1) } else if r2 != testCase.Output { t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r2) } } } func BenchmarkMphDomainMatcher(b *testing.B) { domains, err := loadGeoSite("CN") common.Must(err) matcher, err := router.NewDomainMatcher("mph", domains) common.Must(err) type TestCase struct { Domain string Output bool } testCases := []TestCase{ { Domain: "163.com", Output: true, }, { Domain: "163.com", Output: true, }, { Domain: "164.com", Output: false, }, { Domain: "164.com", Output: false, }, } for i := 0; i < 1024; i++ { testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false}) } b.ResetTimer() for i := 0; i < b.N; i++ { for _, testCase := range testCases { _ = matcher.Match(testCase.Domain) } } } func BenchmarkDomainMatcher(b *testing.B) { domains, err := loadGeoSite("CN") common.Must(err) matcher, err := router.NewDomainMatcher("linear", domains) common.Must(err) type TestCase struct { Domain string Output bool } testCases := []TestCase{ { Domain: "163.com", Output: true, }, { Domain: "163.com", Output: true, }, { Domain: "164.com", Output: false, }, { Domain: "164.com", Output: false, }, } for i := 0; i < 1024; i++ { testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false}) } b.ResetTimer() for i := 0; i < b.N; i++ { for _, testCase := range testCases { _ = matcher.Match(testCase.Domain) } } } func BenchmarkMultiGeoIPMatcher(b *testing.B) { var geoips []*routercommon.GeoIP { ips, err := loadGeoIP("CN") common.Must(err) geoips = append(geoips, &routercommon.GeoIP{ CountryCode: "CN", Cidr: ips, }) } { ips, err := loadGeoIP("JP") common.Must(err) geoips = append(geoips, &routercommon.GeoIP{ CountryCode: "JP", Cidr: ips, }) } { ips, err := loadGeoIP("CA") common.Must(err) geoips = append(geoips, &routercommon.GeoIP{ CountryCode: "CA", Cidr: ips, }) } { ips, err := loadGeoIP("US") common.Must(err) geoips = append(geoips, &routercommon.GeoIP{ CountryCode: "US", Cidr: ips, }) } matcher, err := router.NewMultiGeoIPMatcher(geoips, false) common.Must(err) ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}) b.ResetTimer() for i := 0; i < b.N; i++ { _ = matcher.Apply(ctx) } } ================================================ FILE: app/router/config.go ================================================ //go:build !confonly // +build !confonly package router import ( "context" "encoding/json" "github.com/golang/protobuf/jsonpb" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/infra/conf/v5cfg" ) type Rule struct { Tag string Balancer *Balancer Condition Condition } func (r *Rule) GetTag() (string, error) { if r.Balancer != nil { return r.Balancer.PickOutbound() } return r.Tag, nil } // Apply checks rule matching of current routing context. func (r *Rule) Apply(ctx routing.Context) bool { return r.Condition.Apply(ctx) } func (rr *RoutingRule) BuildCondition() (Condition, error) { conds := NewConditionChan() if len(rr.Domain) > 0 { cond, err := NewDomainMatcher(rr.DomainMatcher, rr.Domain) if err != nil { return nil, newError("failed to build domain condition").Base(err) } conds.Add(cond) } var geoDomains []*routercommon.Domain for _, geo := range rr.GeoDomain { geoDomains = append(geoDomains, geo.Domain...) } if len(geoDomains) > 0 { cond, err := NewDomainMatcher(rr.DomainMatcher, geoDomains) if err != nil { return nil, newError("failed to build geo domain condition").Base(err) } conds.Add(cond) } if len(rr.UserEmail) > 0 { conds.Add(NewUserMatcher(rr.UserEmail)) } if len(rr.InboundTag) > 0 { conds.Add(NewInboundTagMatcher(rr.InboundTag)) } if rr.PortList != nil { conds.Add(NewPortMatcher(rr.PortList, false)) } else if rr.PortRange != nil { conds.Add(NewPortMatcher(&net.PortList{Range: []*net.PortRange{rr.PortRange}}, false)) } if rr.SourcePortList != nil { conds.Add(NewPortMatcher(rr.SourcePortList, true)) } if len(rr.Networks) > 0 { conds.Add(NewNetworkMatcher(rr.Networks)) } else if rr.NetworkList != nil { conds.Add(NewNetworkMatcher(rr.NetworkList.Network)) } if len(rr.Geoip) > 0 { cond, err := NewMultiGeoIPMatcher(rr.Geoip, false) if err != nil { return nil, err } conds.Add(cond) } else if len(rr.Cidr) > 0 { cond, err := NewMultiGeoIPMatcher([]*routercommon.GeoIP{{Cidr: rr.Cidr}}, false) if err != nil { return nil, err } conds.Add(cond) } if len(rr.SourceGeoip) > 0 { cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, true) if err != nil { return nil, err } conds.Add(cond) } else if len(rr.SourceCidr) > 0 { cond, err := NewMultiGeoIPMatcher([]*routercommon.GeoIP{{Cidr: rr.SourceCidr}}, true) if err != nil { return nil, err } conds.Add(cond) } if len(rr.Protocol) > 0 { conds.Add(NewProtocolMatcher(rr.Protocol)) } if len(rr.Attributes) > 0 { cond, err := NewAttributeMatcher(rr.Attributes) if err != nil { return nil, err } conds.Add(cond) } if conds.Len() == 0 { return nil, newError("this rule has no effective fields").AtWarning() } return conds, nil } // Build builds the balancing rule func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatcher) (*Balancer, error) { switch br.Strategy { case "leastping": i, err := serial.GetInstanceOf(br.StrategySettings) if err != nil { return nil, err } s, ok := i.(*StrategyLeastPingConfig) if !ok { return nil, newError("not a StrategyLeastPingConfig").AtError() } return &Balancer{ selectors: br.OutboundSelector, strategy: &LeastPingStrategy{config: s}, ohm: ohm, fallbackTag: br.FallbackTag, }, nil case "leastload": i, err := serial.GetInstanceOf(br.StrategySettings) if err != nil { return nil, err } s, ok := i.(*StrategyLeastLoadConfig) if !ok { return nil, newError("not a StrategyLeastLoadConfig").AtError() } leastLoadStrategy := NewLeastLoadStrategy(s) return &Balancer{ selectors: br.OutboundSelector, ohm: ohm, fallbackTag: br.FallbackTag, strategy: leastLoadStrategy, }, nil case "random": fallthrough case "": var randomStrategy *RandomStrategy if br.StrategySettings != nil { i, err := serial.GetInstanceOf(br.StrategySettings) if err != nil { return nil, err } s, ok := i.(*StrategyRandomConfig) if !ok { return nil, newError("not a StrategyRandomConfig").AtError() } randomStrategy = NewRandomStrategy(s) } return &Balancer{ selectors: br.OutboundSelector, ohm: ohm, fallbackTag: br.FallbackTag, strategy: randomStrategy, }, nil default: return nil, newError("unrecognized balancer type") } } func (br *BalancingRule) UnmarshalJSONPB(unmarshaler *jsonpb.Unmarshaler, bytes []byte) error { type BalancingRuleStub struct { Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` OutboundSelector []string `protobuf:"bytes,2,rep,name=outbound_selector,json=outboundSelector,proto3" json:"outbound_selector,omitempty"` Strategy string `protobuf:"bytes,3,opt,name=strategy,proto3" json:"strategy,omitempty"` StrategySettings json.RawMessage `protobuf:"bytes,4,opt,name=strategy_settings,json=strategySettings,proto3" json:"strategy_settings,omitempty"` FallbackTag string `protobuf:"bytes,5,opt,name=fallback_tag,json=fallbackTag,proto3" json:"fallback_tag,omitempty"` } var stub BalancingRuleStub if err := json.Unmarshal(bytes, &stub); err != nil { return err } if stub.Strategy == "" { stub.Strategy = "random" } settingsPack, err := v5cfg.LoadHeterogeneousConfigFromRawJSON(context.TODO(), "balancer", stub.Strategy, stub.StrategySettings) if err != nil { return err } br.StrategySettings = serial.ToTypedMessage(settingsPack) br.Tag = stub.Tag br.Strategy = stub.Strategy br.OutboundSelector = stub.OutboundSelector br.FallbackTag = stub.FallbackTag return nil } ================================================ FILE: app/router/config.pb.go ================================================ package router import ( routercommon "github.com/v2fly/v2ray-core/v5/app/router/routercommon" net "github.com/v2fly/v2ray-core/v5/common/net" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type DomainStrategy int32 const ( // Use domain as is. DomainStrategy_AsIs DomainStrategy = 0 // Always resolve IP for domains. DomainStrategy_UseIp DomainStrategy = 1 // Resolve to IP if the domain doesn't match any rules. DomainStrategy_IpIfNonMatch DomainStrategy = 2 // Resolve to IP if any rule requires IP matching. DomainStrategy_IpOnDemand DomainStrategy = 3 ) // Enum value maps for DomainStrategy. var ( DomainStrategy_name = map[int32]string{ 0: "AsIs", 1: "UseIp", 2: "IpIfNonMatch", 3: "IpOnDemand", } DomainStrategy_value = map[string]int32{ "AsIs": 0, "UseIp": 1, "IpIfNonMatch": 2, "IpOnDemand": 3, } ) func (x DomainStrategy) Enum() *DomainStrategy { p := new(DomainStrategy) *p = x return p } func (x DomainStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (DomainStrategy) Descriptor() protoreflect.EnumDescriptor { return file_app_router_config_proto_enumTypes[0].Descriptor() } func (DomainStrategy) Type() protoreflect.EnumType { return &file_app_router_config_proto_enumTypes[0] } func (x DomainStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use DomainStrategy.Descriptor instead. func (DomainStrategy) EnumDescriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{0} } type RoutingRule struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to TargetTag: // // *RoutingRule_Tag // *RoutingRule_BalancingTag TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"` // List of domains for target domain matching. Domain []*routercommon.Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"` // List of CIDRs for target IP address matching. // Deprecated. Use geoip below. // // Deprecated: Marked as deprecated in app/router/config.proto. Cidr []*routercommon.CIDR `protobuf:"bytes,3,rep,name=cidr,proto3" json:"cidr,omitempty"` // List of GeoIPs for target IP address matching. If this entry exists, the // cidr above will have no effect. GeoIP fields with the same country code are // supposed to contain exactly same content. They will be merged during // runtime. For customized GeoIPs, please leave country code empty. Geoip []*routercommon.GeoIP `protobuf:"bytes,10,rep,name=geoip,proto3" json:"geoip,omitempty"` // A range of port [from, to]. If the destination port is in this range, this // rule takes effect. Deprecated. Use port_list. // // Deprecated: Marked as deprecated in app/router/config.proto. PortRange *net.PortRange `protobuf:"bytes,4,opt,name=port_range,json=portRange,proto3" json:"port_range,omitempty"` // List of ports. PortList *net.PortList `protobuf:"bytes,14,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"` // List of networks. Deprecated. Use networks. // // Deprecated: Marked as deprecated in app/router/config.proto. NetworkList *net.NetworkList `protobuf:"bytes,5,opt,name=network_list,json=networkList,proto3" json:"network_list,omitempty"` // List of networks for matching. Networks []net.Network `protobuf:"varint,13,rep,packed,name=networks,proto3,enum=v2ray.core.common.net.Network" json:"networks,omitempty"` // List of CIDRs for source IP address matching. // // Deprecated: Marked as deprecated in app/router/config.proto. SourceCidr []*routercommon.CIDR `protobuf:"bytes,6,rep,name=source_cidr,json=sourceCidr,proto3" json:"source_cidr,omitempty"` // List of GeoIPs for source IP address matching. If this entry exists, the // source_cidr above will have no effect. SourceGeoip []*routercommon.GeoIP `protobuf:"bytes,11,rep,name=source_geoip,json=sourceGeoip,proto3" json:"source_geoip,omitempty"` // List of ports for source port matching. SourcePortList *net.PortList `protobuf:"bytes,16,opt,name=source_port_list,json=sourcePortList,proto3" json:"source_port_list,omitempty"` UserEmail []string `protobuf:"bytes,7,rep,name=user_email,json=userEmail,proto3" json:"user_email,omitempty"` InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"` Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"` Attributes string `protobuf:"bytes,15,opt,name=attributes,proto3" json:"attributes,omitempty"` DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"` // geo_domain instruct simplified config loader to load geo domain rule and fill in domain field. GeoDomain []*routercommon.GeoSite `protobuf:"bytes,68001,rep,name=geo_domain,json=geoDomain,proto3" json:"geo_domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RoutingRule) Reset() { *x = RoutingRule{} mi := &file_app_router_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RoutingRule) String() string { return protoimpl.X.MessageStringOf(x) } func (*RoutingRule) ProtoMessage() {} func (x *RoutingRule) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RoutingRule.ProtoReflect.Descriptor instead. func (*RoutingRule) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{0} } func (x *RoutingRule) GetTargetTag() isRoutingRule_TargetTag { if x != nil { return x.TargetTag } return nil } func (x *RoutingRule) GetTag() string { if x != nil { if x, ok := x.TargetTag.(*RoutingRule_Tag); ok { return x.Tag } } return "" } func (x *RoutingRule) GetBalancingTag() string { if x != nil { if x, ok := x.TargetTag.(*RoutingRule_BalancingTag); ok { return x.BalancingTag } } return "" } func (x *RoutingRule) GetDomain() []*routercommon.Domain { if x != nil { return x.Domain } return nil } // Deprecated: Marked as deprecated in app/router/config.proto. func (x *RoutingRule) GetCidr() []*routercommon.CIDR { if x != nil { return x.Cidr } return nil } func (x *RoutingRule) GetGeoip() []*routercommon.GeoIP { if x != nil { return x.Geoip } return nil } // Deprecated: Marked as deprecated in app/router/config.proto. func (x *RoutingRule) GetPortRange() *net.PortRange { if x != nil { return x.PortRange } return nil } func (x *RoutingRule) GetPortList() *net.PortList { if x != nil { return x.PortList } return nil } // Deprecated: Marked as deprecated in app/router/config.proto. func (x *RoutingRule) GetNetworkList() *net.NetworkList { if x != nil { return x.NetworkList } return nil } func (x *RoutingRule) GetNetworks() []net.Network { if x != nil { return x.Networks } return nil } // Deprecated: Marked as deprecated in app/router/config.proto. func (x *RoutingRule) GetSourceCidr() []*routercommon.CIDR { if x != nil { return x.SourceCidr } return nil } func (x *RoutingRule) GetSourceGeoip() []*routercommon.GeoIP { if x != nil { return x.SourceGeoip } return nil } func (x *RoutingRule) GetSourcePortList() *net.PortList { if x != nil { return x.SourcePortList } return nil } func (x *RoutingRule) GetUserEmail() []string { if x != nil { return x.UserEmail } return nil } func (x *RoutingRule) GetInboundTag() []string { if x != nil { return x.InboundTag } return nil } func (x *RoutingRule) GetProtocol() []string { if x != nil { return x.Protocol } return nil } func (x *RoutingRule) GetAttributes() string { if x != nil { return x.Attributes } return "" } func (x *RoutingRule) GetDomainMatcher() string { if x != nil { return x.DomainMatcher } return "" } func (x *RoutingRule) GetGeoDomain() []*routercommon.GeoSite { if x != nil { return x.GeoDomain } return nil } type isRoutingRule_TargetTag interface { isRoutingRule_TargetTag() } type RoutingRule_Tag struct { // Tag of outbound that this rule is pointing to. Tag string `protobuf:"bytes,1,opt,name=tag,proto3,oneof"` } type RoutingRule_BalancingTag struct { // Tag of routing balancer. BalancingTag string `protobuf:"bytes,12,opt,name=balancing_tag,json=balancingTag,proto3,oneof"` } func (*RoutingRule_Tag) isRoutingRule_TargetTag() {} func (*RoutingRule_BalancingTag) isRoutingRule_TargetTag() {} type BalancingRule struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` OutboundSelector []string `protobuf:"bytes,2,rep,name=outbound_selector,json=outboundSelector,proto3" json:"outbound_selector,omitempty"` Strategy string `protobuf:"bytes,3,opt,name=strategy,proto3" json:"strategy,omitempty"` StrategySettings *anypb.Any `protobuf:"bytes,4,opt,name=strategy_settings,json=strategySettings,proto3" json:"strategy_settings,omitempty"` FallbackTag string `protobuf:"bytes,5,opt,name=fallback_tag,json=fallbackTag,proto3" json:"fallback_tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BalancingRule) Reset() { *x = BalancingRule{} mi := &file_app_router_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BalancingRule) String() string { return protoimpl.X.MessageStringOf(x) } func (*BalancingRule) ProtoMessage() {} func (x *BalancingRule) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BalancingRule.ProtoReflect.Descriptor instead. func (*BalancingRule) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{1} } func (x *BalancingRule) GetTag() string { if x != nil { return x.Tag } return "" } func (x *BalancingRule) GetOutboundSelector() []string { if x != nil { return x.OutboundSelector } return nil } func (x *BalancingRule) GetStrategy() string { if x != nil { return x.Strategy } return "" } func (x *BalancingRule) GetStrategySettings() *anypb.Any { if x != nil { return x.StrategySettings } return nil } func (x *BalancingRule) GetFallbackTag() string { if x != nil { return x.FallbackTag } return "" } type StrategyWeight struct { state protoimpl.MessageState `protogen:"open.v1"` Regexp bool `protobuf:"varint,1,opt,name=regexp,proto3" json:"regexp,omitempty"` Match string `protobuf:"bytes,2,opt,name=match,proto3" json:"match,omitempty"` Value float32 `protobuf:"fixed32,3,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StrategyWeight) Reset() { *x = StrategyWeight{} mi := &file_app_router_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StrategyWeight) String() string { return protoimpl.X.MessageStringOf(x) } func (*StrategyWeight) ProtoMessage() {} func (x *StrategyWeight) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StrategyWeight.ProtoReflect.Descriptor instead. func (*StrategyWeight) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{2} } func (x *StrategyWeight) GetRegexp() bool { if x != nil { return x.Regexp } return false } func (x *StrategyWeight) GetMatch() string { if x != nil { return x.Match } return "" } func (x *StrategyWeight) GetValue() float32 { if x != nil { return x.Value } return 0 } type StrategyRandomConfig struct { state protoimpl.MessageState `protogen:"open.v1"` ObserverTag string `protobuf:"bytes,7,opt,name=observer_tag,json=observerTag,proto3" json:"observer_tag,omitempty"` AliveOnly bool `protobuf:"varint,8,opt,name=alive_only,json=aliveOnly,proto3" json:"alive_only,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StrategyRandomConfig) Reset() { *x = StrategyRandomConfig{} mi := &file_app_router_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StrategyRandomConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*StrategyRandomConfig) ProtoMessage() {} func (x *StrategyRandomConfig) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StrategyRandomConfig.ProtoReflect.Descriptor instead. func (*StrategyRandomConfig) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{3} } func (x *StrategyRandomConfig) GetObserverTag() string { if x != nil { return x.ObserverTag } return "" } func (x *StrategyRandomConfig) GetAliveOnly() bool { if x != nil { return x.AliveOnly } return false } type StrategyLeastPingConfig struct { state protoimpl.MessageState `protogen:"open.v1"` ObserverTag string `protobuf:"bytes,7,opt,name=observer_tag,json=observerTag,proto3" json:"observer_tag,omitempty"` StickyChoice bool `protobuf:"varint,8,opt,name=sticky_choice,json=stickyChoice,proto3" json:"sticky_choice,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StrategyLeastPingConfig) Reset() { *x = StrategyLeastPingConfig{} mi := &file_app_router_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StrategyLeastPingConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*StrategyLeastPingConfig) ProtoMessage() {} func (x *StrategyLeastPingConfig) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StrategyLeastPingConfig.ProtoReflect.Descriptor instead. func (*StrategyLeastPingConfig) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{4} } func (x *StrategyLeastPingConfig) GetObserverTag() string { if x != nil { return x.ObserverTag } return "" } func (x *StrategyLeastPingConfig) GetStickyChoice() bool { if x != nil { return x.StickyChoice } return false } type StrategyLeastLoadConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // weight settings Costs []*StrategyWeight `protobuf:"bytes,2,rep,name=costs,proto3" json:"costs,omitempty"` // RTT baselines for selecting, int64 values of time.Duration Baselines []int64 `protobuf:"varint,3,rep,packed,name=baselines,proto3" json:"baselines,omitempty"` // expected nodes count to select Expected int32 `protobuf:"varint,4,opt,name=expected,proto3" json:"expected,omitempty"` // max acceptable rtt, filter away high delay nodes. defalut 0 MaxRTT int64 `protobuf:"varint,5,opt,name=maxRTT,proto3" json:"maxRTT,omitempty"` // acceptable failure rate Tolerance float32 `protobuf:"fixed32,6,opt,name=tolerance,proto3" json:"tolerance,omitempty"` ObserverTag string `protobuf:"bytes,7,opt,name=observer_tag,json=observerTag,proto3" json:"observer_tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StrategyLeastLoadConfig) Reset() { *x = StrategyLeastLoadConfig{} mi := &file_app_router_config_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StrategyLeastLoadConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*StrategyLeastLoadConfig) ProtoMessage() {} func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StrategyLeastLoadConfig.ProtoReflect.Descriptor instead. func (*StrategyLeastLoadConfig) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{5} } func (x *StrategyLeastLoadConfig) GetCosts() []*StrategyWeight { if x != nil { return x.Costs } return nil } func (x *StrategyLeastLoadConfig) GetBaselines() []int64 { if x != nil { return x.Baselines } return nil } func (x *StrategyLeastLoadConfig) GetExpected() int32 { if x != nil { return x.Expected } return 0 } func (x *StrategyLeastLoadConfig) GetMaxRTT() int64 { if x != nil { return x.MaxRTT } return 0 } func (x *StrategyLeastLoadConfig) GetTolerance() float32 { if x != nil { return x.Tolerance } return 0 } func (x *StrategyLeastLoadConfig) GetObserverTag() string { if x != nil { return x.ObserverTag } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` DomainStrategy DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=v2ray.core.app.router.DomainStrategy" json:"domain_strategy,omitempty"` Rule []*RoutingRule `protobuf:"bytes,2,rep,name=rule,proto3" json:"rule,omitempty"` BalancingRule []*BalancingRule `protobuf:"bytes,3,rep,name=balancing_rule,json=balancingRule,proto3" json:"balancing_rule,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_router_config_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{6} } func (x *Config) GetDomainStrategy() DomainStrategy { if x != nil { return x.DomainStrategy } return DomainStrategy_AsIs } func (x *Config) GetRule() []*RoutingRule { if x != nil { return x.Rule } return nil } func (x *Config) GetBalancingRule() []*BalancingRule { if x != nil { return x.BalancingRule } return nil } type SimplifiedRoutingRule struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to TargetTag: // // *SimplifiedRoutingRule_Tag // *SimplifiedRoutingRule_BalancingTag TargetTag isSimplifiedRoutingRule_TargetTag `protobuf_oneof:"target_tag"` // List of domains for target domain matching. Domain []*routercommon.Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"` // List of GeoIPs for target IP address matching. If this entry exists, the // cidr above will have no effect. GeoIP fields with the same country code are // supposed to contain exactly same content. They will be merged during // runtime. For customized GeoIPs, please leave country code empty. Geoip []*routercommon.GeoIP `protobuf:"bytes,10,rep,name=geoip,proto3" json:"geoip,omitempty"` // List of ports. PortList string `protobuf:"bytes,14,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"` // List of networks for matching. Networks *net.NetworkList `protobuf:"bytes,13,opt,name=networks,proto3" json:"networks,omitempty"` // List of GeoIPs for source IP address matching. If this entry exists, the // source_cidr above will have no effect. SourceGeoip []*routercommon.GeoIP `protobuf:"bytes,11,rep,name=source_geoip,json=sourceGeoip,proto3" json:"source_geoip,omitempty"` // List of ports for source port matching. SourcePortList string `protobuf:"bytes,16,opt,name=source_port_list,json=sourcePortList,proto3" json:"source_port_list,omitempty"` UserEmail []string `protobuf:"bytes,7,rep,name=user_email,json=userEmail,proto3" json:"user_email,omitempty"` InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"` Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"` Attributes string `protobuf:"bytes,15,opt,name=attributes,proto3" json:"attributes,omitempty"` DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"` // geo_domain instruct simplified config loader to load geo domain rule and fill in domain field. GeoDomain []*routercommon.GeoSite `protobuf:"bytes,68001,rep,name=geo_domain,json=geoDomain,proto3" json:"geo_domain,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedRoutingRule) Reset() { *x = SimplifiedRoutingRule{} mi := &file_app_router_config_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedRoutingRule) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedRoutingRule) ProtoMessage() {} func (x *SimplifiedRoutingRule) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedRoutingRule.ProtoReflect.Descriptor instead. func (*SimplifiedRoutingRule) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{7} } func (x *SimplifiedRoutingRule) GetTargetTag() isSimplifiedRoutingRule_TargetTag { if x != nil { return x.TargetTag } return nil } func (x *SimplifiedRoutingRule) GetTag() string { if x != nil { if x, ok := x.TargetTag.(*SimplifiedRoutingRule_Tag); ok { return x.Tag } } return "" } func (x *SimplifiedRoutingRule) GetBalancingTag() string { if x != nil { if x, ok := x.TargetTag.(*SimplifiedRoutingRule_BalancingTag); ok { return x.BalancingTag } } return "" } func (x *SimplifiedRoutingRule) GetDomain() []*routercommon.Domain { if x != nil { return x.Domain } return nil } func (x *SimplifiedRoutingRule) GetGeoip() []*routercommon.GeoIP { if x != nil { return x.Geoip } return nil } func (x *SimplifiedRoutingRule) GetPortList() string { if x != nil { return x.PortList } return "" } func (x *SimplifiedRoutingRule) GetNetworks() *net.NetworkList { if x != nil { return x.Networks } return nil } func (x *SimplifiedRoutingRule) GetSourceGeoip() []*routercommon.GeoIP { if x != nil { return x.SourceGeoip } return nil } func (x *SimplifiedRoutingRule) GetSourcePortList() string { if x != nil { return x.SourcePortList } return "" } func (x *SimplifiedRoutingRule) GetUserEmail() []string { if x != nil { return x.UserEmail } return nil } func (x *SimplifiedRoutingRule) GetInboundTag() []string { if x != nil { return x.InboundTag } return nil } func (x *SimplifiedRoutingRule) GetProtocol() []string { if x != nil { return x.Protocol } return nil } func (x *SimplifiedRoutingRule) GetAttributes() string { if x != nil { return x.Attributes } return "" } func (x *SimplifiedRoutingRule) GetDomainMatcher() string { if x != nil { return x.DomainMatcher } return "" } func (x *SimplifiedRoutingRule) GetGeoDomain() []*routercommon.GeoSite { if x != nil { return x.GeoDomain } return nil } type isSimplifiedRoutingRule_TargetTag interface { isSimplifiedRoutingRule_TargetTag() } type SimplifiedRoutingRule_Tag struct { // Tag of outbound that this rule is pointing to. Tag string `protobuf:"bytes,1,opt,name=tag,proto3,oneof"` } type SimplifiedRoutingRule_BalancingTag struct { // Tag of routing balancer. BalancingTag string `protobuf:"bytes,12,opt,name=balancing_tag,json=balancingTag,proto3,oneof"` } func (*SimplifiedRoutingRule_Tag) isSimplifiedRoutingRule_TargetTag() {} func (*SimplifiedRoutingRule_BalancingTag) isSimplifiedRoutingRule_TargetTag() {} type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` DomainStrategy DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=v2ray.core.app.router.DomainStrategy" json:"domain_strategy,omitempty"` Rule []*SimplifiedRoutingRule `protobuf:"bytes,2,rep,name=rule,proto3" json:"rule,omitempty"` BalancingRule []*BalancingRule `protobuf:"bytes,3,rep,name=balancing_rule,json=balancingRule,proto3" json:"balancing_rule,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_app_router_config_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_app_router_config_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_app_router_config_proto_rawDescGZIP(), []int{8} } func (x *SimplifiedConfig) GetDomainStrategy() DomainStrategy { if x != nil { return x.DomainStrategy } return DomainStrategy_AsIs } func (x *SimplifiedConfig) GetRule() []*SimplifiedRoutingRule { if x != nil { return x.Rule } return nil } func (x *SimplifiedConfig) GetBalancingRule() []*BalancingRule { if x != nil { return x.BalancingRule } return nil } var File_app_router_config_proto protoreflect.FileDescriptor const file_app_router_config_proto_rawDesc = "" + "\n" + "\x17app/router/config.proto\x12\x15v2ray.core.app.router\x1a\x19google/protobuf/any.proto\x1a\x15common/net/port.proto\x1a\x18common/net/network.proto\x1a common/protoext/extensions.proto\x1a$app/router/routercommon/common.proto\"\x80\b\n" + "\vRoutingRule\x12\x12\n" + "\x03tag\x18\x01 \x01(\tH\x00R\x03tag\x12%\n" + "\rbalancing_tag\x18\f \x01(\tH\x00R\fbalancingTag\x12B\n" + "\x06domain\x18\x02 \x03(\v2*.v2ray.core.app.router.routercommon.DomainR\x06domain\x12@\n" + "\x04cidr\x18\x03 \x03(\v2(.v2ray.core.app.router.routercommon.CIDRB\x02\x18\x01R\x04cidr\x12?\n" + "\x05geoip\x18\n" + " \x03(\v2).v2ray.core.app.router.routercommon.GeoIPR\x05geoip\x12C\n" + "\n" + "port_range\x18\x04 \x01(\v2 .v2ray.core.common.net.PortRangeB\x02\x18\x01R\tportRange\x12<\n" + "\tport_list\x18\x0e \x01(\v2\x1f.v2ray.core.common.net.PortListR\bportList\x12I\n" + "\fnetwork_list\x18\x05 \x01(\v2\".v2ray.core.common.net.NetworkListB\x02\x18\x01R\vnetworkList\x12:\n" + "\bnetworks\x18\r \x03(\x0e2\x1e.v2ray.core.common.net.NetworkR\bnetworks\x12M\n" + "\vsource_cidr\x18\x06 \x03(\v2(.v2ray.core.app.router.routercommon.CIDRB\x02\x18\x01R\n" + "sourceCidr\x12L\n" + "\fsource_geoip\x18\v \x03(\v2).v2ray.core.app.router.routercommon.GeoIPR\vsourceGeoip\x12I\n" + "\x10source_port_list\x18\x10 \x01(\v2\x1f.v2ray.core.common.net.PortListR\x0esourcePortList\x12\x1d\n" + "\n" + "user_email\x18\a \x03(\tR\tuserEmail\x12\x1f\n" + "\vinbound_tag\x18\b \x03(\tR\n" + "inboundTag\x12\x1a\n" + "\bprotocol\x18\t \x03(\tR\bprotocol\x12\x1e\n" + "\n" + "attributes\x18\x0f \x01(\tR\n" + "attributes\x12%\n" + "\x0edomain_matcher\x18\x11 \x01(\tR\rdomainMatcher\x12L\n" + "\n" + "geo_domain\x18\xa1\x93\x04 \x03(\v2+.v2ray.core.app.router.routercommon.GeoSiteR\tgeoDomainB\f\n" + "\n" + "target_tag\"\xd0\x01\n" + "\rBalancingRule\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12+\n" + "\x11outbound_selector\x18\x02 \x03(\tR\x10outboundSelector\x12\x1a\n" + "\bstrategy\x18\x03 \x01(\tR\bstrategy\x12A\n" + "\x11strategy_settings\x18\x04 \x01(\v2\x14.google.protobuf.AnyR\x10strategySettings\x12!\n" + "\ffallback_tag\x18\x05 \x01(\tR\vfallbackTag\"T\n" + "\x0eStrategyWeight\x12\x16\n" + "\x06regexp\x18\x01 \x01(\bR\x06regexp\x12\x14\n" + "\x05match\x18\x02 \x01(\tR\x05match\x12\x14\n" + "\x05value\x18\x03 \x01(\x02R\x05value\"p\n" + "\x14StrategyRandomConfig\x12!\n" + "\fobserver_tag\x18\a \x01(\tR\vobserverTag\x12\x1d\n" + "\n" + "alive_only\x18\b \x01(\bR\taliveOnly:\x16\x82\xb5\x18\x12\n" + "\bbalancer\x12\x06random\"|\n" + "\x17StrategyLeastPingConfig\x12!\n" + "\fobserver_tag\x18\a \x01(\tR\vobserverTag\x12#\n" + "\rsticky_choice\x18\b \x01(\bR\fstickyChoice:\x19\x82\xb5\x18\x15\n" + "\bbalancer\x12\tleastping\"\x84\x02\n" + "\x17StrategyLeastLoadConfig\x12;\n" + "\x05costs\x18\x02 \x03(\v2%.v2ray.core.app.router.StrategyWeightR\x05costs\x12\x1c\n" + "\tbaselines\x18\x03 \x03(\x03R\tbaselines\x12\x1a\n" + "\bexpected\x18\x04 \x01(\x05R\bexpected\x12\x16\n" + "\x06maxRTT\x18\x05 \x01(\x03R\x06maxRTT\x12\x1c\n" + "\ttolerance\x18\x06 \x01(\x02R\ttolerance\x12!\n" + "\fobserver_tag\x18\a \x01(\tR\vobserverTag:\x19\x82\xb5\x18\x15\n" + "\bbalancer\x12\tleastload\"\xdd\x01\n" + "\x06Config\x12N\n" + "\x0fdomain_strategy\x18\x01 \x01(\x0e2%.v2ray.core.app.router.DomainStrategyR\x0edomainStrategy\x126\n" + "\x04rule\x18\x02 \x03(\v2\".v2ray.core.app.router.RoutingRuleR\x04rule\x12K\n" + "\x0ebalancing_rule\x18\x03 \x03(\v2$.v2ray.core.app.router.BalancingRuleR\rbalancingRule\"\xab\x05\n" + "\x15SimplifiedRoutingRule\x12\x12\n" + "\x03tag\x18\x01 \x01(\tH\x00R\x03tag\x12%\n" + "\rbalancing_tag\x18\f \x01(\tH\x00R\fbalancingTag\x12B\n" + "\x06domain\x18\x02 \x03(\v2*.v2ray.core.app.router.routercommon.DomainR\x06domain\x12?\n" + "\x05geoip\x18\n" + " \x03(\v2).v2ray.core.app.router.routercommon.GeoIPR\x05geoip\x12\x1b\n" + "\tport_list\x18\x0e \x01(\tR\bportList\x12>\n" + "\bnetworks\x18\r \x01(\v2\".v2ray.core.common.net.NetworkListR\bnetworks\x12L\n" + "\fsource_geoip\x18\v \x03(\v2).v2ray.core.app.router.routercommon.GeoIPR\vsourceGeoip\x12(\n" + "\x10source_port_list\x18\x10 \x01(\tR\x0esourcePortList\x12\x1d\n" + "\n" + "user_email\x18\a \x03(\tR\tuserEmail\x12\x1f\n" + "\vinbound_tag\x18\b \x03(\tR\n" + "inboundTag\x12\x1a\n" + "\bprotocol\x18\t \x03(\tR\bprotocol\x12\x1e\n" + "\n" + "attributes\x18\x0f \x01(\tR\n" + "attributes\x12%\n" + "\x0edomain_matcher\x18\x11 \x01(\tR\rdomainMatcher\x12L\n" + "\n" + "geo_domain\x18\xa1\x93\x04 \x03(\v2+.v2ray.core.app.router.routercommon.GeoSiteR\tgeoDomainB\f\n" + "\n" + "target_tag\"\x88\x02\n" + "\x10SimplifiedConfig\x12N\n" + "\x0fdomain_strategy\x18\x01 \x01(\x0e2%.v2ray.core.app.router.DomainStrategyR\x0edomainStrategy\x12@\n" + "\x04rule\x18\x02 \x03(\v2,.v2ray.core.app.router.SimplifiedRoutingRuleR\x04rule\x12K\n" + "\x0ebalancing_rule\x18\x03 \x03(\v2$.v2ray.core.app.router.BalancingRuleR\rbalancingRule:\x15\x82\xb5\x18\x11\n" + "\aservice\x12\x06router*G\n" + "\x0eDomainStrategy\x12\b\n" + "\x04AsIs\x10\x00\x12\t\n" + "\x05UseIp\x10\x01\x12\x10\n" + "\fIpIfNonMatch\x10\x02\x12\x0e\n" + "\n" + "IpOnDemand\x10\x03B`\n" + "\x19com.v2ray.core.app.routerP\x01Z)github.com/v2fly/v2ray-core/v5/app/router\xaa\x02\x15V2Ray.Core.App.Routerb\x06proto3" var ( file_app_router_config_proto_rawDescOnce sync.Once file_app_router_config_proto_rawDescData []byte ) func file_app_router_config_proto_rawDescGZIP() []byte { file_app_router_config_proto_rawDescOnce.Do(func() { file_app_router_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_router_config_proto_rawDesc), len(file_app_router_config_proto_rawDesc))) }) return file_app_router_config_proto_rawDescData } var file_app_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_app_router_config_proto_goTypes = []any{ (DomainStrategy)(0), // 0: v2ray.core.app.router.DomainStrategy (*RoutingRule)(nil), // 1: v2ray.core.app.router.RoutingRule (*BalancingRule)(nil), // 2: v2ray.core.app.router.BalancingRule (*StrategyWeight)(nil), // 3: v2ray.core.app.router.StrategyWeight (*StrategyRandomConfig)(nil), // 4: v2ray.core.app.router.StrategyRandomConfig (*StrategyLeastPingConfig)(nil), // 5: v2ray.core.app.router.StrategyLeastPingConfig (*StrategyLeastLoadConfig)(nil), // 6: v2ray.core.app.router.StrategyLeastLoadConfig (*Config)(nil), // 7: v2ray.core.app.router.Config (*SimplifiedRoutingRule)(nil), // 8: v2ray.core.app.router.SimplifiedRoutingRule (*SimplifiedConfig)(nil), // 9: v2ray.core.app.router.SimplifiedConfig (*routercommon.Domain)(nil), // 10: v2ray.core.app.router.routercommon.Domain (*routercommon.CIDR)(nil), // 11: v2ray.core.app.router.routercommon.CIDR (*routercommon.GeoIP)(nil), // 12: v2ray.core.app.router.routercommon.GeoIP (*net.PortRange)(nil), // 13: v2ray.core.common.net.PortRange (*net.PortList)(nil), // 14: v2ray.core.common.net.PortList (*net.NetworkList)(nil), // 15: v2ray.core.common.net.NetworkList (net.Network)(0), // 16: v2ray.core.common.net.Network (*routercommon.GeoSite)(nil), // 17: v2ray.core.app.router.routercommon.GeoSite (*anypb.Any)(nil), // 18: google.protobuf.Any } var file_app_router_config_proto_depIdxs = []int32{ 10, // 0: v2ray.core.app.router.RoutingRule.domain:type_name -> v2ray.core.app.router.routercommon.Domain 11, // 1: v2ray.core.app.router.RoutingRule.cidr:type_name -> v2ray.core.app.router.routercommon.CIDR 12, // 2: v2ray.core.app.router.RoutingRule.geoip:type_name -> v2ray.core.app.router.routercommon.GeoIP 13, // 3: v2ray.core.app.router.RoutingRule.port_range:type_name -> v2ray.core.common.net.PortRange 14, // 4: v2ray.core.app.router.RoutingRule.port_list:type_name -> v2ray.core.common.net.PortList 15, // 5: v2ray.core.app.router.RoutingRule.network_list:type_name -> v2ray.core.common.net.NetworkList 16, // 6: v2ray.core.app.router.RoutingRule.networks:type_name -> v2ray.core.common.net.Network 11, // 7: v2ray.core.app.router.RoutingRule.source_cidr:type_name -> v2ray.core.app.router.routercommon.CIDR 12, // 8: v2ray.core.app.router.RoutingRule.source_geoip:type_name -> v2ray.core.app.router.routercommon.GeoIP 14, // 9: v2ray.core.app.router.RoutingRule.source_port_list:type_name -> v2ray.core.common.net.PortList 17, // 10: v2ray.core.app.router.RoutingRule.geo_domain:type_name -> v2ray.core.app.router.routercommon.GeoSite 18, // 11: v2ray.core.app.router.BalancingRule.strategy_settings:type_name -> google.protobuf.Any 3, // 12: v2ray.core.app.router.StrategyLeastLoadConfig.costs:type_name -> v2ray.core.app.router.StrategyWeight 0, // 13: v2ray.core.app.router.Config.domain_strategy:type_name -> v2ray.core.app.router.DomainStrategy 1, // 14: v2ray.core.app.router.Config.rule:type_name -> v2ray.core.app.router.RoutingRule 2, // 15: v2ray.core.app.router.Config.balancing_rule:type_name -> v2ray.core.app.router.BalancingRule 10, // 16: v2ray.core.app.router.SimplifiedRoutingRule.domain:type_name -> v2ray.core.app.router.routercommon.Domain 12, // 17: v2ray.core.app.router.SimplifiedRoutingRule.geoip:type_name -> v2ray.core.app.router.routercommon.GeoIP 15, // 18: v2ray.core.app.router.SimplifiedRoutingRule.networks:type_name -> v2ray.core.common.net.NetworkList 12, // 19: v2ray.core.app.router.SimplifiedRoutingRule.source_geoip:type_name -> v2ray.core.app.router.routercommon.GeoIP 17, // 20: v2ray.core.app.router.SimplifiedRoutingRule.geo_domain:type_name -> v2ray.core.app.router.routercommon.GeoSite 0, // 21: v2ray.core.app.router.SimplifiedConfig.domain_strategy:type_name -> v2ray.core.app.router.DomainStrategy 8, // 22: v2ray.core.app.router.SimplifiedConfig.rule:type_name -> v2ray.core.app.router.SimplifiedRoutingRule 2, // 23: v2ray.core.app.router.SimplifiedConfig.balancing_rule:type_name -> v2ray.core.app.router.BalancingRule 24, // [24:24] is the sub-list for method output_type 24, // [24:24] is the sub-list for method input_type 24, // [24:24] is the sub-list for extension type_name 24, // [24:24] is the sub-list for extension extendee 0, // [0:24] is the sub-list for field type_name } func init() { file_app_router_config_proto_init() } func file_app_router_config_proto_init() { if File_app_router_config_proto != nil { return } file_app_router_config_proto_msgTypes[0].OneofWrappers = []any{ (*RoutingRule_Tag)(nil), (*RoutingRule_BalancingTag)(nil), } file_app_router_config_proto_msgTypes[7].OneofWrappers = []any{ (*SimplifiedRoutingRule_Tag)(nil), (*SimplifiedRoutingRule_BalancingTag)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_config_proto_rawDesc), len(file_app_router_config_proto_rawDesc)), NumEnums: 1, NumMessages: 9, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_router_config_proto_goTypes, DependencyIndexes: file_app_router_config_proto_depIdxs, EnumInfos: file_app_router_config_proto_enumTypes, MessageInfos: file_app_router_config_proto_msgTypes, }.Build() File_app_router_config_proto = out.File file_app_router_config_proto_goTypes = nil file_app_router_config_proto_depIdxs = nil } ================================================ FILE: app/router/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.router; option csharp_namespace = "V2Ray.Core.App.Router"; option go_package = "github.com/v2fly/v2ray-core/v5/app/router"; option java_package = "com.v2ray.core.app.router"; option java_multiple_files = true; import "google/protobuf/any.proto"; import "common/net/port.proto"; import "common/net/network.proto"; import "common/protoext/extensions.proto"; import "app/router/routercommon/common.proto"; message RoutingRule { oneof target_tag { // Tag of outbound that this rule is pointing to. string tag = 1; // Tag of routing balancer. string balancing_tag = 12; } // List of domains for target domain matching. repeated v2ray.core.app.router.routercommon.Domain domain = 2; // List of CIDRs for target IP address matching. // Deprecated. Use geoip below. repeated v2ray.core.app.router.routercommon.CIDR cidr = 3 [deprecated = true]; // List of GeoIPs for target IP address matching. If this entry exists, the // cidr above will have no effect. GeoIP fields with the same country code are // supposed to contain exactly same content. They will be merged during // runtime. For customized GeoIPs, please leave country code empty. repeated v2ray.core.app.router.routercommon.GeoIP geoip = 10; // A range of port [from, to]. If the destination port is in this range, this // rule takes effect. Deprecated. Use port_list. v2ray.core.common.net.PortRange port_range = 4 [deprecated = true]; // List of ports. v2ray.core.common.net.PortList port_list = 14; // List of networks. Deprecated. Use networks. v2ray.core.common.net.NetworkList network_list = 5 [deprecated = true]; // List of networks for matching. repeated v2ray.core.common.net.Network networks = 13; // List of CIDRs for source IP address matching. repeated v2ray.core.app.router.routercommon.CIDR source_cidr = 6 [deprecated = true]; // List of GeoIPs for source IP address matching. If this entry exists, the // source_cidr above will have no effect. repeated v2ray.core.app.router.routercommon.GeoIP source_geoip = 11; // List of ports for source port matching. v2ray.core.common.net.PortList source_port_list = 16; repeated string user_email = 7; repeated string inbound_tag = 8; repeated string protocol = 9; string attributes = 15; string domain_matcher = 17; // geo_domain instruct simplified config loader to load geo domain rule and fill in domain field. repeated v2ray.core.app.router.routercommon.GeoSite geo_domain = 68001; } message BalancingRule { string tag = 1; repeated string outbound_selector = 2; string strategy = 3; google.protobuf.Any strategy_settings = 4; string fallback_tag = 5; } message StrategyWeight { bool regexp = 1; string match = 2; float value = 3; } message StrategyRandomConfig { option (v2ray.core.common.protoext.message_opt).type = "balancer"; option (v2ray.core.common.protoext.message_opt).short_name = "random"; string observer_tag = 7; bool alive_only = 8; } message StrategyLeastPingConfig { option (v2ray.core.common.protoext.message_opt).type = "balancer"; option (v2ray.core.common.protoext.message_opt).short_name = "leastping"; string observer_tag = 7; bool sticky_choice = 8; } message StrategyLeastLoadConfig { option (v2ray.core.common.protoext.message_opt).type = "balancer"; option (v2ray.core.common.protoext.message_opt).short_name = "leastload"; // weight settings repeated StrategyWeight costs = 2; // RTT baselines for selecting, int64 values of time.Duration repeated int64 baselines = 3; // expected nodes count to select int32 expected = 4; // max acceptable rtt, filter away high delay nodes. defalut 0 int64 maxRTT = 5; // acceptable failure rate float tolerance = 6; string observer_tag = 7; } enum DomainStrategy { // Use domain as is. AsIs = 0; // Always resolve IP for domains. UseIp = 1; // Resolve to IP if the domain doesn't match any rules. IpIfNonMatch = 2; // Resolve to IP if any rule requires IP matching. IpOnDemand = 3; } message Config { DomainStrategy domain_strategy = 1; repeated RoutingRule rule = 2; repeated BalancingRule balancing_rule = 3; } message SimplifiedRoutingRule { oneof target_tag { // Tag of outbound that this rule is pointing to. string tag = 1; // Tag of routing balancer. string balancing_tag = 12; } // List of domains for target domain matching. repeated v2ray.core.app.router.routercommon.Domain domain = 2; // List of GeoIPs for target IP address matching. If this entry exists, the // cidr above will have no effect. GeoIP fields with the same country code are // supposed to contain exactly same content. They will be merged during // runtime. For customized GeoIPs, please leave country code empty. repeated v2ray.core.app.router.routercommon.GeoIP geoip = 10; // List of ports. string port_list = 14; // List of networks for matching. v2ray.core.common.net.NetworkList networks = 13; // List of GeoIPs for source IP address matching. If this entry exists, the // source_cidr above will have no effect. repeated v2ray.core.app.router.routercommon.GeoIP source_geoip = 11; // List of ports for source port matching. string source_port_list = 16; repeated string user_email = 7; repeated string inbound_tag = 8; repeated string protocol = 9; string attributes = 15; string domain_matcher = 17; // geo_domain instruct simplified config loader to load geo domain rule and fill in domain field. repeated v2ray.core.app.router.routercommon.GeoSite geo_domain = 68001; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "router"; DomainStrategy domain_strategy = 1; repeated SimplifiedRoutingRule rule = 2; repeated BalancingRule balancing_rule = 3; } ================================================ FILE: app/router/errors.generated.go ================================================ package router import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/router/router.go ================================================ package router //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/features/routing" routing_dns "github.com/v2fly/v2ray-core/v5/features/routing/dns" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" ) // Router is an implementation of routing.Router. type Router struct { domainStrategy DomainStrategy rules []*Rule balancers map[string]*Balancer dns dns.Client } // Route is an implementation of routing.Route. type Route struct { routing.Context outboundGroupTags []string outboundTag string } // Init initializes the Router. func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm outbound.Manager, dispatcher routing.Dispatcher) error { r.domainStrategy = config.DomainStrategy r.dns = d r.balancers = make(map[string]*Balancer, len(config.BalancingRule)) for _, rule := range config.BalancingRule { balancer, err := rule.Build(ohm, dispatcher) if err != nil { return err } balancer.InjectContext(ctx) r.balancers[rule.Tag] = balancer } r.rules = make([]*Rule, 0, len(config.Rule)) for _, rule := range config.Rule { cond, err := rule.BuildCondition() if err != nil { return err } rr := &Rule{ Condition: cond, Tag: rule.GetTag(), } btag := rule.GetBalancingTag() if len(btag) > 0 { brule, found := r.balancers[btag] if !found { return newError("balancer ", btag, " not found") } rr.Balancer = brule } r.rules = append(r.rules, rr) } return nil } // PickRoute implements routing.Router. func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) { rule, ctx, err := r.pickRouteInternal(ctx) if err != nil { return nil, err } tag, err := rule.GetTag() if err != nil { return nil, err } return &Route{Context: ctx, outboundTag: tag}, nil } func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) { // SkipDNSResolve is set from DNS module. // the DOH remote server maybe a domain name, // this prevents cycle resolving dead loop skipDNSResolve := ctx.GetSkipDNSResolve() if r.domainStrategy == DomainStrategy_IpOnDemand && !skipDNSResolve { ctx = routing_dns.ContextWithDNSClient(ctx, r.dns) } for _, rule := range r.rules { if rule.Apply(ctx) { return rule, ctx, nil } } if r.domainStrategy != DomainStrategy_IpIfNonMatch || len(ctx.GetTargetDomain()) == 0 || skipDNSResolve { return nil, ctx, common.ErrNoClue } ctx = routing_dns.ContextWithDNSClient(ctx, r.dns) // Try applying rules again if we have IPs. for _, rule := range r.rules { if rule.Apply(ctx) { return rule, ctx, nil } } return nil, ctx, common.ErrNoClue } // Start implements common.Runnable. func (r *Router) Start() error { return nil } // Close implements common.Closable. func (r *Router) Close() error { return nil } // Type implements common.HasType. func (*Router) Type() interface{} { return routing.RouterType() } // GetOutboundGroupTags implements routing.Route. func (r *Route) GetOutboundGroupTags() []string { return r.outboundGroupTags } // GetOutboundTag implements routing.Route. func (r *Route) GetOutboundTag() string { return r.outboundTag } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { r := new(Router) if err := core.RequireFeatures(ctx, func(d dns.Client, ohm outbound.Manager, dispatcher routing.Dispatcher) error { return r.Init(ctx, config.(*Config), d, ohm, dispatcher) }); err != nil { return nil, err } return r, nil })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { ctx = cfgcommon.NewConfigureLoadingContext(ctx) geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string { return "standard" }) if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil { cfgcommon.SetGeoDataLoader(ctx, loader) } else { return nil, newError("unable to create geo data loader ").Base(err) } cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx) geoLoader := cfgEnv.GetGeoLoader() simplifiedConfig := config.(*SimplifiedConfig) var routingRules []*RoutingRule for _, v := range simplifiedConfig.Rule { rule := new(RoutingRule) for _, geo := range v.Geoip { if geo.Code != "" { filepath := "geoip.dat" if geo.FilePath != "" { filepath = geo.FilePath } else { geo.CountryCode = geo.Code } var err error geo.Cidr, err = geoLoader.LoadIP(filepath, geo.Code) if err != nil { return nil, newError("unable to load geoip").Base(err) } } } rule.Geoip = v.Geoip for _, geo := range v.SourceGeoip { if geo.Code != "" { filepath := "geoip.dat" if geo.FilePath != "" { filepath = geo.FilePath } else { geo.CountryCode = geo.Code } var err error geo.Cidr, err = geoLoader.LoadIP(filepath, geo.Code) if err != nil { return nil, newError("unable to load geoip").Base(err) } } } rule.SourceGeoip = v.SourceGeoip for _, geo := range v.GeoDomain { if geo.Code != "" { filepath := "geosite.dat" if geo.FilePath != "" { filepath = geo.FilePath } var err error geo.Domain, err = geoLoader.LoadGeoSiteWithAttr(filepath, geo.Code) if err != nil { return nil, newError("unable to load geodomain").Base(err) } } } if v.PortList != "" { portList := &cfgcommon.PortList{} err := portList.UnmarshalText(v.PortList) if err != nil { return nil, err } rule.PortList = portList.Build() } if v.SourcePortList != "" { portList := &cfgcommon.PortList{} err := portList.UnmarshalText(v.SourcePortList) if err != nil { return nil, err } rule.SourcePortList = portList.Build() } rule.Domain = v.Domain rule.GeoDomain = v.GeoDomain rule.Networks = v.Networks.GetNetwork() rule.Protocol = v.Protocol rule.Attributes = v.Attributes rule.UserEmail = v.UserEmail rule.InboundTag = v.InboundTag rule.DomainMatcher = v.DomainMatcher switch s := v.TargetTag.(type) { case *SimplifiedRoutingRule_Tag: rule.TargetTag = &RoutingRule_Tag{s.Tag} case *SimplifiedRoutingRule_BalancingTag: rule.TargetTag = &RoutingRule_BalancingTag{s.BalancingTag} } routingRules = append(routingRules, rule) } fullConfig := &Config{ DomainStrategy: simplifiedConfig.DomainStrategy, Rule: routingRules, BalancingRule: simplifiedConfig.BalancingRule, } return common.CreateObject(ctx, fullConfig) })) } ================================================ FILE: app/router/router_test.go ================================================ package router_test import ( "context" "testing" "github.com/golang/mock/gomock" . "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/outbound" routing_session "github.com/v2fly/v2ray-core/v5/features/routing/session" "github.com/v2fly/v2ray-core/v5/testing/mocks" ) type mockOutboundManager struct { outbound.Manager outbound.HandlerSelector } func TestSimpleRouter(t *testing.T) { config := &Config{ Rule: []*RoutingRule{ { TargetTag: &RoutingRule_Tag{ Tag: "test", }, Networks: []net.Network{net.Network_TCP}, }, }, } mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockDNS := mocks.NewDNSClient(mockCtl) mockOhm := mocks.NewOutboundManager(mockCtl) mockHs := mocks.NewOutboundHandlerSelector(mockCtl) r := new(Router) common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{ Manager: mockOhm, HandlerSelector: mockHs, }, nil)) ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2fly.org"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { t.Error("expect tag 'test', bug actually ", tag) } } func TestSimpleBalancer(t *testing.T) { config := &Config{ Rule: []*RoutingRule{ { TargetTag: &RoutingRule_BalancingTag{ BalancingTag: "balance", }, Networks: []net.Network{net.Network_TCP}, }, }, BalancingRule: []*BalancingRule{ { Tag: "balance", OutboundSelector: []string{"test-"}, }, }, } mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockDNS := mocks.NewDNSClient(mockCtl) mockOhm := mocks.NewOutboundManager(mockCtl) mockHs := mocks.NewOutboundHandlerSelector(mockCtl) mockHs.EXPECT().Select(gomock.Eq([]string{"test-"})).Return([]string{"test"}) r := new(Router) common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{ Manager: mockOhm, HandlerSelector: mockHs, }, nil)) ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2fly.org"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { t.Error("expect tag 'test', bug actually ", tag) } } /* Do not work right now: need a full client setup func TestLeastLoadBalancer(t *testing.T) { config := &Config{ Rule: []*RoutingRule{ { TargetTag: &RoutingRule_BalancingTag{ BalancingTag: "balance", }, Networks: []net.Network{net.Network_TCP}, }, }, BalancingRule: []*BalancingRule{ { Tag: "balance", OutboundSelector: []string{"test-"}, Strategy: "leastLoad", StrategySettings: serial.ToTypedMessage(&StrategyLeastLoadConfig{ Baselines: nil, Expected: 1, }), }, }, } mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockDNS := mocks.NewDNSClient(mockCtl) mockOhm := mocks.NewOutboundManager(mockCtl) mockHs := mocks.NewOutboundHandlerSelector(mockCtl) mockHs.EXPECT().Select(gomock.Eq([]string{"test-"})).Return([]string{"test1"}) r := new(Router) common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{ Manager: mockOhm, HandlerSelector: mockHs, }, nil)) ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test1" { t.Error("expect tag 'test1', bug actually ", tag) } }*/ func TestIPOnDemand(t *testing.T) { config := &Config{ DomainStrategy: DomainStrategy_IpOnDemand, Rule: []*RoutingRule{ { TargetTag: &RoutingRule_Tag{ Tag: "test", }, Cidr: []*routercommon.CIDR{ { Ip: []byte{192, 168, 0, 0}, Prefix: 16, }, }, }, }, } mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockDNS := mocks.NewDNSClient(mockCtl) mockDNS.EXPECT().LookupIP(gomock.Eq("v2fly.org")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() r := new(Router) common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil)) ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2fly.org"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { t.Error("expect tag 'test', bug actually ", tag) } } func TestIPIfNonMatchDomain(t *testing.T) { config := &Config{ DomainStrategy: DomainStrategy_IpIfNonMatch, Rule: []*RoutingRule{ { TargetTag: &RoutingRule_Tag{ Tag: "test", }, Cidr: []*routercommon.CIDR{ { Ip: []byte{192, 168, 0, 0}, Prefix: 16, }, }, }, }, } mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockDNS := mocks.NewDNSClient(mockCtl) mockDNS.EXPECT().LookupIP(gomock.Eq("v2fly.org")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() r := new(Router) common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil)) ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2fly.org"), 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { t.Error("expect tag 'test', bug actually ", tag) } } func TestIPIfNonMatchIP(t *testing.T) { config := &Config{ DomainStrategy: DomainStrategy_IpIfNonMatch, Rule: []*RoutingRule{ { TargetTag: &RoutingRule_Tag{ Tag: "test", }, Cidr: []*routercommon.CIDR{ { Ip: []byte{127, 0, 0, 0}, Prefix: 8, }, }, }, }, } mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockDNS := mocks.NewDNSClient(mockCtl) r := new(Router) common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil)) ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 80)}) route, err := r.PickRoute(routing_session.AsRoutingContext(ctx)) common.Must(err) if tag := route.GetOutboundTag(); tag != "test" { t.Error("expect tag 'test', bug actually ", tag) } } ================================================ FILE: app/router/routercommon/common.pb.go ================================================ package routercommon import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Type of domain value. type Domain_Type int32 const ( // The value is used as is. Domain_Plain Domain_Type = 0 // The value is used as a regular expression. Domain_Regex Domain_Type = 1 // The value is a root domain. Domain_RootDomain Domain_Type = 2 // The value is a domain. Domain_Full Domain_Type = 3 ) // Enum value maps for Domain_Type. var ( Domain_Type_name = map[int32]string{ 0: "Plain", 1: "Regex", 2: "RootDomain", 3: "Full", } Domain_Type_value = map[string]int32{ "Plain": 0, "Regex": 1, "RootDomain": 2, "Full": 3, } ) func (x Domain_Type) Enum() *Domain_Type { p := new(Domain_Type) *p = x return p } func (x Domain_Type) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Domain_Type) Descriptor() protoreflect.EnumDescriptor { return file_app_router_routercommon_common_proto_enumTypes[0].Descriptor() } func (Domain_Type) Type() protoreflect.EnumType { return &file_app_router_routercommon_common_proto_enumTypes[0] } func (x Domain_Type) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Domain_Type.Descriptor instead. func (Domain_Type) EnumDescriptor() ([]byte, []int) { return file_app_router_routercommon_common_proto_rawDescGZIP(), []int{0, 0} } // Domain for routing decision. type Domain struct { state protoimpl.MessageState `protogen:"open.v1"` // Domain matching type. Type Domain_Type `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.router.routercommon.Domain_Type" json:"type,omitempty"` // Domain value. Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` // Attributes of this domain. May be used for filtering. Attribute []*Domain_Attribute `protobuf:"bytes,3,rep,name=attribute,proto3" json:"attribute,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Domain) Reset() { *x = Domain{} mi := &file_app_router_routercommon_common_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Domain) String() string { return protoimpl.X.MessageStringOf(x) } func (*Domain) ProtoMessage() {} func (x *Domain) ProtoReflect() protoreflect.Message { mi := &file_app_router_routercommon_common_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Domain.ProtoReflect.Descriptor instead. func (*Domain) Descriptor() ([]byte, []int) { return file_app_router_routercommon_common_proto_rawDescGZIP(), []int{0} } func (x *Domain) GetType() Domain_Type { if x != nil { return x.Type } return Domain_Plain } func (x *Domain) GetValue() string { if x != nil { return x.Value } return "" } func (x *Domain) GetAttribute() []*Domain_Attribute { if x != nil { return x.Attribute } return nil } // IP for routing decision, in CIDR form. type CIDR struct { state protoimpl.MessageState `protogen:"open.v1"` // IP address, should be either 4 or 16 bytes. Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` // Number of leading ones in the network mask. Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"` IpAddr string `protobuf:"bytes,68000,opt,name=ip_addr,json=ipAddr,proto3" json:"ip_addr,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CIDR) Reset() { *x = CIDR{} mi := &file_app_router_routercommon_common_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CIDR) String() string { return protoimpl.X.MessageStringOf(x) } func (*CIDR) ProtoMessage() {} func (x *CIDR) ProtoReflect() protoreflect.Message { mi := &file_app_router_routercommon_common_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CIDR.ProtoReflect.Descriptor instead. func (*CIDR) Descriptor() ([]byte, []int) { return file_app_router_routercommon_common_proto_rawDescGZIP(), []int{1} } func (x *CIDR) GetIp() []byte { if x != nil { return x.Ip } return nil } func (x *CIDR) GetPrefix() uint32 { if x != nil { return x.Prefix } return 0 } func (x *CIDR) GetIpAddr() string { if x != nil { return x.IpAddr } return "" } type GeoIP struct { state protoimpl.MessageState `protogen:"open.v1"` CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"` Cidr []*CIDR `protobuf:"bytes,2,rep,name=cidr,proto3" json:"cidr,omitempty"` InverseMatch bool `protobuf:"varint,3,opt,name=inverse_match,json=inverseMatch,proto3" json:"inverse_match,omitempty"` // resource_hash instruct simplified config converter to load domain from geo file. ResourceHash []byte `protobuf:"bytes,4,opt,name=resource_hash,json=resourceHash,proto3" json:"resource_hash,omitempty"` Code string `protobuf:"bytes,5,opt,name=code,proto3" json:"code,omitempty"` FilePath string `protobuf:"bytes,68000,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GeoIP) Reset() { *x = GeoIP{} mi := &file_app_router_routercommon_common_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GeoIP) String() string { return protoimpl.X.MessageStringOf(x) } func (*GeoIP) ProtoMessage() {} func (x *GeoIP) ProtoReflect() protoreflect.Message { mi := &file_app_router_routercommon_common_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GeoIP.ProtoReflect.Descriptor instead. func (*GeoIP) Descriptor() ([]byte, []int) { return file_app_router_routercommon_common_proto_rawDescGZIP(), []int{2} } func (x *GeoIP) GetCountryCode() string { if x != nil { return x.CountryCode } return "" } func (x *GeoIP) GetCidr() []*CIDR { if x != nil { return x.Cidr } return nil } func (x *GeoIP) GetInverseMatch() bool { if x != nil { return x.InverseMatch } return false } func (x *GeoIP) GetResourceHash() []byte { if x != nil { return x.ResourceHash } return nil } func (x *GeoIP) GetCode() string { if x != nil { return x.Code } return "" } func (x *GeoIP) GetFilePath() string { if x != nil { return x.FilePath } return "" } type GeoIPList struct { state protoimpl.MessageState `protogen:"open.v1"` Entry []*GeoIP `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GeoIPList) Reset() { *x = GeoIPList{} mi := &file_app_router_routercommon_common_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GeoIPList) String() string { return protoimpl.X.MessageStringOf(x) } func (*GeoIPList) ProtoMessage() {} func (x *GeoIPList) ProtoReflect() protoreflect.Message { mi := &file_app_router_routercommon_common_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GeoIPList.ProtoReflect.Descriptor instead. func (*GeoIPList) Descriptor() ([]byte, []int) { return file_app_router_routercommon_common_proto_rawDescGZIP(), []int{3} } func (x *GeoIPList) GetEntry() []*GeoIP { if x != nil { return x.Entry } return nil } type GeoSite struct { state protoimpl.MessageState `protogen:"open.v1"` CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"` Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"` // resource_hash instruct simplified config converter to load domain from geo file. ResourceHash []byte `protobuf:"bytes,3,opt,name=resource_hash,json=resourceHash,proto3" json:"resource_hash,omitempty"` Code string `protobuf:"bytes,4,opt,name=code,proto3" json:"code,omitempty"` FilePath string `protobuf:"bytes,68000,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GeoSite) Reset() { *x = GeoSite{} mi := &file_app_router_routercommon_common_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GeoSite) String() string { return protoimpl.X.MessageStringOf(x) } func (*GeoSite) ProtoMessage() {} func (x *GeoSite) ProtoReflect() protoreflect.Message { mi := &file_app_router_routercommon_common_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GeoSite.ProtoReflect.Descriptor instead. func (*GeoSite) Descriptor() ([]byte, []int) { return file_app_router_routercommon_common_proto_rawDescGZIP(), []int{4} } func (x *GeoSite) GetCountryCode() string { if x != nil { return x.CountryCode } return "" } func (x *GeoSite) GetDomain() []*Domain { if x != nil { return x.Domain } return nil } func (x *GeoSite) GetResourceHash() []byte { if x != nil { return x.ResourceHash } return nil } func (x *GeoSite) GetCode() string { if x != nil { return x.Code } return "" } func (x *GeoSite) GetFilePath() string { if x != nil { return x.FilePath } return "" } type GeoSiteList struct { state protoimpl.MessageState `protogen:"open.v1"` Entry []*GeoSite `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GeoSiteList) Reset() { *x = GeoSiteList{} mi := &file_app_router_routercommon_common_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GeoSiteList) String() string { return protoimpl.X.MessageStringOf(x) } func (*GeoSiteList) ProtoMessage() {} func (x *GeoSiteList) ProtoReflect() protoreflect.Message { mi := &file_app_router_routercommon_common_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GeoSiteList.ProtoReflect.Descriptor instead. func (*GeoSiteList) Descriptor() ([]byte, []int) { return file_app_router_routercommon_common_proto_rawDescGZIP(), []int{5} } func (x *GeoSiteList) GetEntry() []*GeoSite { if x != nil { return x.Entry } return nil } type Domain_Attribute struct { state protoimpl.MessageState `protogen:"open.v1"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // Types that are valid to be assigned to TypedValue: // // *Domain_Attribute_BoolValue // *Domain_Attribute_IntValue TypedValue isDomain_Attribute_TypedValue `protobuf_oneof:"typed_value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Domain_Attribute) Reset() { *x = Domain_Attribute{} mi := &file_app_router_routercommon_common_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Domain_Attribute) String() string { return protoimpl.X.MessageStringOf(x) } func (*Domain_Attribute) ProtoMessage() {} func (x *Domain_Attribute) ProtoReflect() protoreflect.Message { mi := &file_app_router_routercommon_common_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Domain_Attribute.ProtoReflect.Descriptor instead. func (*Domain_Attribute) Descriptor() ([]byte, []int) { return file_app_router_routercommon_common_proto_rawDescGZIP(), []int{0, 0} } func (x *Domain_Attribute) GetKey() string { if x != nil { return x.Key } return "" } func (x *Domain_Attribute) GetTypedValue() isDomain_Attribute_TypedValue { if x != nil { return x.TypedValue } return nil } func (x *Domain_Attribute) GetBoolValue() bool { if x != nil { if x, ok := x.TypedValue.(*Domain_Attribute_BoolValue); ok { return x.BoolValue } } return false } func (x *Domain_Attribute) GetIntValue() int64 { if x != nil { if x, ok := x.TypedValue.(*Domain_Attribute_IntValue); ok { return x.IntValue } } return 0 } type isDomain_Attribute_TypedValue interface { isDomain_Attribute_TypedValue() } type Domain_Attribute_BoolValue struct { BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"` } type Domain_Attribute_IntValue struct { IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"` } func (*Domain_Attribute_BoolValue) isDomain_Attribute_TypedValue() {} func (*Domain_Attribute_IntValue) isDomain_Attribute_TypedValue() {} var File_app_router_routercommon_common_proto protoreflect.FileDescriptor const file_app_router_routercommon_common_proto_rawDesc = "" + "\n" + "$app/router/routercommon/common.proto\x12\"v2ray.core.app.router.routercommon\x1a common/protoext/extensions.proto\"\xdd\x02\n" + "\x06Domain\x12C\n" + "\x04type\x18\x01 \x01(\x0e2/.v2ray.core.app.router.routercommon.Domain.TypeR\x04type\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value\x12R\n" + "\tattribute\x18\x03 \x03(\v24.v2ray.core.app.router.routercommon.Domain.AttributeR\tattribute\x1al\n" + "\tAttribute\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x1f\n" + "\n" + "bool_value\x18\x02 \x01(\bH\x00R\tboolValue\x12\x1d\n" + "\tint_value\x18\x03 \x01(\x03H\x00R\bintValueB\r\n" + "\vtyped_value\"6\n" + "\x04Type\x12\t\n" + "\x05Plain\x10\x00\x12\t\n" + "\x05Regex\x10\x01\x12\x0e\n" + "\n" + "RootDomain\x10\x02\x12\b\n" + "\x04Full\x10\x03\"S\n" + "\x04CIDR\x12\x0e\n" + "\x02ip\x18\x01 \x01(\fR\x02ip\x12\x16\n" + "\x06prefix\x18\x02 \x01(\rR\x06prefix\x12#\n" + "\aip_addr\x18\xa0\x93\x04 \x01(\tB\b\x82\xb5\x18\x04:\x02ipR\x06ipAddr\"\xfa\x01\n" + "\x05GeoIP\x12!\n" + "\fcountry_code\x18\x01 \x01(\tR\vcountryCode\x12<\n" + "\x04cidr\x18\x02 \x03(\v2(.v2ray.core.app.router.routercommon.CIDRR\x04cidr\x12#\n" + "\rinverse_match\x18\x03 \x01(\bR\finverseMatch\x12#\n" + "\rresource_hash\x18\x04 \x01(\fR\fresourceHash\x12\x12\n" + "\x04code\x18\x05 \x01(\tR\x04code\x122\n" + "\tfile_path\x18\xa0\x93\x04 \x01(\tB\x13\x82\xb5\x18\x0f2\rresource_hashR\bfilePath\"L\n" + "\tGeoIPList\x12?\n" + "\x05entry\x18\x01 \x03(\v2).v2ray.core.app.router.routercommon.GeoIPR\x05entry\"\xdd\x01\n" + "\aGeoSite\x12!\n" + "\fcountry_code\x18\x01 \x01(\tR\vcountryCode\x12B\n" + "\x06domain\x18\x02 \x03(\v2*.v2ray.core.app.router.routercommon.DomainR\x06domain\x12#\n" + "\rresource_hash\x18\x03 \x01(\fR\fresourceHash\x12\x12\n" + "\x04code\x18\x04 \x01(\tR\x04code\x122\n" + "\tfile_path\x18\xa0\x93\x04 \x01(\tB\x13\x82\xb5\x18\x0f2\rresource_hashR\bfilePath\"P\n" + "\vGeoSiteList\x12A\n" + "\x05entry\x18\x01 \x03(\v2+.v2ray.core.app.router.routercommon.GeoSiteR\x05entryB\x87\x01\n" + "&com.v2ray.core.app.router.routercommonP\x01Z6github.com/v2fly/v2ray-core/v5/app/router/routercommon\xaa\x02\"V2Ray.Core.App.Router.Routercommonb\x06proto3" var ( file_app_router_routercommon_common_proto_rawDescOnce sync.Once file_app_router_routercommon_common_proto_rawDescData []byte ) func file_app_router_routercommon_common_proto_rawDescGZIP() []byte { file_app_router_routercommon_common_proto_rawDescOnce.Do(func() { file_app_router_routercommon_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_router_routercommon_common_proto_rawDesc), len(file_app_router_routercommon_common_proto_rawDesc))) }) return file_app_router_routercommon_common_proto_rawDescData } var file_app_router_routercommon_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_app_router_routercommon_common_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_app_router_routercommon_common_proto_goTypes = []any{ (Domain_Type)(0), // 0: v2ray.core.app.router.routercommon.Domain.Type (*Domain)(nil), // 1: v2ray.core.app.router.routercommon.Domain (*CIDR)(nil), // 2: v2ray.core.app.router.routercommon.CIDR (*GeoIP)(nil), // 3: v2ray.core.app.router.routercommon.GeoIP (*GeoIPList)(nil), // 4: v2ray.core.app.router.routercommon.GeoIPList (*GeoSite)(nil), // 5: v2ray.core.app.router.routercommon.GeoSite (*GeoSiteList)(nil), // 6: v2ray.core.app.router.routercommon.GeoSiteList (*Domain_Attribute)(nil), // 7: v2ray.core.app.router.routercommon.Domain.Attribute } var file_app_router_routercommon_common_proto_depIdxs = []int32{ 0, // 0: v2ray.core.app.router.routercommon.Domain.type:type_name -> v2ray.core.app.router.routercommon.Domain.Type 7, // 1: v2ray.core.app.router.routercommon.Domain.attribute:type_name -> v2ray.core.app.router.routercommon.Domain.Attribute 2, // 2: v2ray.core.app.router.routercommon.GeoIP.cidr:type_name -> v2ray.core.app.router.routercommon.CIDR 3, // 3: v2ray.core.app.router.routercommon.GeoIPList.entry:type_name -> v2ray.core.app.router.routercommon.GeoIP 1, // 4: v2ray.core.app.router.routercommon.GeoSite.domain:type_name -> v2ray.core.app.router.routercommon.Domain 5, // 5: v2ray.core.app.router.routercommon.GeoSiteList.entry:type_name -> v2ray.core.app.router.routercommon.GeoSite 6, // [6:6] is the sub-list for method output_type 6, // [6:6] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name } func init() { file_app_router_routercommon_common_proto_init() } func file_app_router_routercommon_common_proto_init() { if File_app_router_routercommon_common_proto != nil { return } file_app_router_routercommon_common_proto_msgTypes[6].OneofWrappers = []any{ (*Domain_Attribute_BoolValue)(nil), (*Domain_Attribute_IntValue)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_routercommon_common_proto_rawDesc), len(file_app_router_routercommon_common_proto_rawDesc)), NumEnums: 1, NumMessages: 7, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_router_routercommon_common_proto_goTypes, DependencyIndexes: file_app_router_routercommon_common_proto_depIdxs, EnumInfos: file_app_router_routercommon_common_proto_enumTypes, MessageInfos: file_app_router_routercommon_common_proto_msgTypes, }.Build() File_app_router_routercommon_common_proto = out.File file_app_router_routercommon_common_proto_goTypes = nil file_app_router_routercommon_common_proto_depIdxs = nil } ================================================ FILE: app/router/routercommon/common.proto ================================================ syntax = "proto3"; package v2ray.core.app.router.routercommon; option csharp_namespace = "V2Ray.Core.App.Router.Routercommon"; option go_package = "github.com/v2fly/v2ray-core/v5/app/router/routercommon"; option java_package = "com.v2ray.core.app.router.routercommon"; option java_multiple_files = true; import "common/protoext/extensions.proto"; // Domain for routing decision. message Domain { // Type of domain value. enum Type { // The value is used as is. Plain = 0; // The value is used as a regular expression. Regex = 1; // The value is a root domain. RootDomain = 2; // The value is a domain. Full = 3; } // Domain matching type. Type type = 1; // Domain value. string value = 2; message Attribute { string key = 1; oneof typed_value { bool bool_value = 2; int64 int_value = 3; } } // Attributes of this domain. May be used for filtering. repeated Attribute attribute = 3; } // IP for routing decision, in CIDR form. message CIDR { // IP address, should be either 4 or 16 bytes. bytes ip = 1; // Number of leading ones in the network mask. uint32 prefix = 2; string ip_addr = 68000 [(v2ray.core.common.protoext.field_opt).convert_time_parse_ip = "ip"]; } message GeoIP { string country_code = 1; repeated CIDR cidr = 2; bool inverse_match = 3; // resource_hash instruct simplified config converter to load domain from geo file. bytes resource_hash = 4; string code = 5; string file_path = 68000[(v2ray.core.common.protoext.field_opt).convert_time_resource_loading = "resource_hash"]; } message GeoIPList { repeated GeoIP entry = 1; } message GeoSite { string country_code = 1; repeated Domain domain = 2; // resource_hash instruct simplified config converter to load domain from geo file. bytes resource_hash = 3; string code = 4; string file_path = 68000[(v2ray.core.common.protoext.field_opt).convert_time_resource_loading = "resource_hash"]; } message GeoSiteList { repeated GeoSite entry = 1; } ================================================ FILE: app/router/strategy_leastload.go ================================================ package router import ( "context" "math" "sort" "time" "github.com/golang/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/observatory" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/extension" ) // LeastLoadStrategy represents a least load balancing strategy type LeastLoadStrategy struct { settings *StrategyLeastLoadConfig costs *WeightManager observer extension.Observatory ctx context.Context } func (l *LeastLoadStrategy) GetPrincipleTarget(strings []string) []string { var ret []string nodes := l.pickOutbounds(strings) for _, v := range nodes { ret = append(ret, v.Tag) } return ret } // NewLeastLoadStrategy creates a new LeastLoadStrategy with settings func NewLeastLoadStrategy(settings *StrategyLeastLoadConfig) *LeastLoadStrategy { return &LeastLoadStrategy{ settings: settings, costs: NewWeightManager( settings.Costs, 1, func(value, cost float64) float64 { return value * math.Pow(cost, 0.5) }, ), } } // node is a minimal copy of HealthCheckResult // we don't use HealthCheckResult directly because // it may change by health checker during routing type node struct { Tag string CountAll int CountFail int RTTAverage time.Duration RTTDeviation time.Duration RTTDeviationCost time.Duration } func (l *LeastLoadStrategy) InjectContext(ctx context.Context) { l.ctx = ctx } func (l *LeastLoadStrategy) PickOutbound(candidates []string) string { selects := l.pickOutbounds(candidates) count := len(selects) if count == 0 { // goes to fallbackTag return "" } return selects[dice.Roll(count)].Tag } func (l *LeastLoadStrategy) pickOutbounds(candidates []string) []*node { qualified := l.getNodes(candidates, time.Duration(l.settings.MaxRTT)) selects := l.selectLeastLoad(qualified) return selects } // selectLeastLoad selects nodes according to Baselines and Expected Count. // // The strategy always improves network response speed, not matter which mode below is configured. // But they can still have different priorities. // // 1. Bandwidth priority: no Baseline + Expected Count > 0.: selects `Expected Count` of nodes. // (one if Expected Count <= 0) // // 2. Bandwidth priority advanced: Baselines + Expected Count > 0. // Select `Expected Count` amount of nodes, and also those near them according to baselines. // In other words, it selects according to different Baselines, until one of them matches // the Expected Count, if no Baseline matches, Expected Count applied. // // 3. Speed priority: Baselines + `Expected Count <= 0`. // go through all baselines until find selects, if not, select none. Used in combination // with 'balancer.fallbackTag', it means: selects qualified nodes or use the fallback. func (l *LeastLoadStrategy) selectLeastLoad(nodes []*node) []*node { if len(nodes) == 0 { newError("least load: no qualified outbound").AtInfo().WriteToLog() return nil } expected := int(l.settings.Expected) availableCount := len(nodes) if expected > availableCount { return nodes } if expected <= 0 { expected = 1 } if len(l.settings.Baselines) == 0 { return nodes[:expected] } count := 0 // go through all base line until find expected selects for _, b := range l.settings.Baselines { baseline := time.Duration(b) for i := count; i < availableCount; i++ { if nodes[i].RTTDeviationCost >= baseline { break } count = i + 1 } // don't continue if find expected selects if count >= expected { newError("applied baseline: ", baseline).AtDebug().WriteToLog() break } } if l.settings.Expected > 0 && count < expected { count = expected } return nodes[:count] } func (l *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration) []*node { if l.observer == nil { common.Must(core.RequireFeatures(l.ctx, func(observatory extension.Observatory) error { l.observer = observatory return nil })) } var result proto.Message if l.settings.ObserverTag == "" { observeResult, err := l.observer.GetObservation(l.ctx) if err != nil { newError("cannot get observation").Base(err).WriteToLog() return make([]*node, 0) } result = observeResult } else { observeResult, err := common.Must2(l.observer.(features.TaggedFeatures).GetFeaturesByTag(l.settings.ObserverTag)).(extension.Observatory).GetObservation(l.ctx) if err != nil { newError("cannot get observation").Base(err).WriteToLog() return make([]*node, 0) } result = observeResult } results := result.(*observatory.ObservationResult) outboundlist := outboundList(candidates) var ret []*node for _, v := range results.Status { if v.Alive && (v.Delay < maxRTT.Milliseconds() || maxRTT == 0) && outboundlist.contains(v.OutboundTag) { record := &node{ Tag: v.OutboundTag, CountAll: 1, CountFail: 1, RTTAverage: time.Duration(v.Delay) * time.Millisecond, RTTDeviation: time.Duration(v.Delay) * time.Millisecond, RTTDeviationCost: time.Duration(l.costs.Apply(v.OutboundTag, float64(time.Duration(v.Delay)*time.Millisecond))), } if v.HealthPing != nil { record.RTTAverage = time.Duration(v.HealthPing.Average) record.RTTDeviation = time.Duration(v.HealthPing.Deviation) record.RTTDeviationCost = time.Duration(l.costs.Apply(v.OutboundTag, float64(v.HealthPing.Deviation))) record.CountAll = int(v.HealthPing.All) record.CountFail = int(v.HealthPing.Fail) } ret = append(ret, record) } } leastloadSort(ret) return ret } func leastloadSort(nodes []*node) { sort.Slice(nodes, func(i, j int) bool { left := nodes[i] right := nodes[j] if left.RTTDeviationCost != right.RTTDeviationCost { return left.RTTDeviationCost < right.RTTDeviationCost } if left.RTTAverage != right.RTTAverage { return left.RTTAverage < right.RTTAverage } if left.CountFail != right.CountFail { return left.CountFail < right.CountFail } if left.CountAll != right.CountAll { return left.CountAll > right.CountAll } return left.Tag < right.Tag }) } func init() { common.Must(common.RegisterConfig((*StrategyLeastLoadConfig)(nil), nil)) } ================================================ FILE: app/router/strategy_leastload_test.go ================================================ package router import ( "testing" ) /* Split into multiple package, need to be tested separately func TestSelectLeastLoad(t *testing.T) { settings := &StrategyLeastLoadConfig{ HealthCheck: &HealthPingConfig{ SamplingCount: 10, }, Expected: 1, MaxRTT: int64(time.Millisecond * time.Duration(800)), } strategy := NewLeastLoadStrategy(settings) // std 40 strategy.PutResult("a", time.Millisecond*time.Duration(60)) strategy.PutResult("a", time.Millisecond*time.Duration(140)) strategy.PutResult("a", time.Millisecond*time.Duration(60)) strategy.PutResult("a", time.Millisecond*time.Duration(140)) // std 60 strategy.PutResult("b", time.Millisecond*time.Duration(40)) strategy.PutResult("b", time.Millisecond*time.Duration(160)) strategy.PutResult("b", time.Millisecond*time.Duration(40)) strategy.PutResult("b", time.Millisecond*time.Duration(160)) // std 0, but >MaxRTT strategy.PutResult("c", time.Millisecond*time.Duration(1000)) strategy.PutResult("c", time.Millisecond*time.Duration(1000)) strategy.PutResult("c", time.Millisecond*time.Duration(1000)) strategy.PutResult("c", time.Millisecond*time.Duration(1000)) expected := "a" actual := strategy.SelectAndPick([]string{"a", "b", "c", "untested"}) if actual != expected { t.Errorf("expected: %v, actual: %v", expected, actual) } } func TestSelectLeastLoadWithCost(t *testing.T) { settings := &StrategyLeastLoadConfig{ HealthCheck: &HealthPingConfig{ SamplingCount: 10, }, Costs: []*StrategyWeight{ {Match: "a", Value: 9}, }, Expected: 1, } strategy := NewLeastLoadStrategy(settings, nil) // std 40, std+c 120 strategy.PutResult("a", time.Millisecond*time.Duration(60)) strategy.PutResult("a", time.Millisecond*time.Duration(140)) strategy.PutResult("a", time.Millisecond*time.Duration(60)) strategy.PutResult("a", time.Millisecond*time.Duration(140)) // std 60 strategy.PutResult("b", time.Millisecond*time.Duration(40)) strategy.PutResult("b", time.Millisecond*time.Duration(160)) strategy.PutResult("b", time.Millisecond*time.Duration(40)) strategy.PutResult("b", time.Millisecond*time.Duration(160)) expected := "b" actual := strategy.SelectAndPick([]string{"a", "b", "untested"}) if actual != expected { t.Errorf("expected: %v, actual: %v", expected, actual) } } */ func TestSelectLeastExpected(t *testing.T) { strategy := &LeastLoadStrategy{ settings: &StrategyLeastLoadConfig{ Baselines: nil, Expected: 3, }, } nodes := []*node{ {Tag: "a", RTTDeviationCost: 100}, {Tag: "b", RTTDeviationCost: 200}, {Tag: "c", RTTDeviationCost: 300}, {Tag: "d", RTTDeviationCost: 350}, } expected := 3 ns := strategy.selectLeastLoad(nodes) if len(ns) != expected { t.Errorf("expected: %v, actual: %v", expected, len(ns)) } } func TestSelectLeastExpected2(t *testing.T) { strategy := &LeastLoadStrategy{ settings: &StrategyLeastLoadConfig{ Baselines: nil, Expected: 3, }, } nodes := []*node{ {Tag: "a", RTTDeviationCost: 100}, {Tag: "b", RTTDeviationCost: 200}, } expected := 2 ns := strategy.selectLeastLoad(nodes) if len(ns) != expected { t.Errorf("expected: %v, actual: %v", expected, len(ns)) } } func TestSelectLeastExpectedAndBaselines(t *testing.T) { strategy := &LeastLoadStrategy{ settings: &StrategyLeastLoadConfig{ Baselines: []int64{200, 300, 400}, Expected: 3, }, } nodes := []*node{ {Tag: "a", RTTDeviationCost: 100}, {Tag: "b", RTTDeviationCost: 200}, {Tag: "c", RTTDeviationCost: 250}, {Tag: "d", RTTDeviationCost: 300}, {Tag: "e", RTTDeviationCost: 310}, } expected := 3 ns := strategy.selectLeastLoad(nodes) if len(ns) != expected { t.Errorf("expected: %v, actual: %v", expected, len(ns)) } } func TestSelectLeastExpectedAndBaselines2(t *testing.T) { strategy := &LeastLoadStrategy{ settings: &StrategyLeastLoadConfig{ Baselines: []int64{200, 300, 400}, Expected: 3, }, } nodes := []*node{ {Tag: "a", RTTDeviationCost: 500}, {Tag: "b", RTTDeviationCost: 600}, {Tag: "c", RTTDeviationCost: 700}, {Tag: "d", RTTDeviationCost: 800}, {Tag: "e", RTTDeviationCost: 900}, } expected := 3 ns := strategy.selectLeastLoad(nodes) if len(ns) != expected { t.Errorf("expected: %v, actual: %v", expected, len(ns)) } } func TestSelectLeastLoadBaselines(t *testing.T) { strategy := &LeastLoadStrategy{ settings: &StrategyLeastLoadConfig{ Baselines: []int64{200, 400, 600}, Expected: 0, }, } nodes := []*node{ {Tag: "a", RTTDeviationCost: 100}, {Tag: "b", RTTDeviationCost: 200}, {Tag: "c", RTTDeviationCost: 300}, } expected := 1 ns := strategy.selectLeastLoad(nodes) if len(ns) != expected { t.Errorf("expected: %v, actual: %v", expected, len(ns)) } } func TestSelectLeastLoadBaselinesNoQualified(t *testing.T) { strategy := &LeastLoadStrategy{ settings: &StrategyLeastLoadConfig{ Baselines: []int64{200, 400, 600}, Expected: 0, }, } nodes := []*node{ {Tag: "a", RTTDeviationCost: 800}, {Tag: "b", RTTDeviationCost: 1000}, } expected := 0 ns := strategy.selectLeastLoad(nodes) if len(ns) != expected { t.Errorf("expected: %v, actual: %v", expected, len(ns)) } } ================================================ FILE: app/router/strategy_leastping.go ================================================ //go:build !confonly // +build !confonly package router import ( "context" "sync" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/observatory" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/extension" ) type LeastPingStrategy struct { ctx context.Context observatory extension.Observatory config *StrategyLeastPingConfig lock sync.Mutex lastChoice string } func (l *LeastPingStrategy) GetPrincipleTarget(strings []string) []string { return []string{l.PickOutbound(strings)} } func (l *LeastPingStrategy) InjectContext(ctx context.Context) { l.ctx = ctx } func (l *LeastPingStrategy) PickOutbound(strings []string) string { if l.observatory == nil { common.Must(core.RequireFeatures(l.ctx, func(observatory extension.Observatory) error { if l.config.ObserverTag != "" { l.observatory = common.Must2(observatory.(features.TaggedFeatures).GetFeaturesByTag(l.config.ObserverTag)).(extension.Observatory) } else { l.observatory = observatory } return nil })) } if l.observatory == nil { newError("cannot find observatory").WriteToLog() return "" } observeReport, err := l.observatory.GetObservation(l.ctx) if err != nil { newError("cannot get observe report").Base(err).WriteToLog() return "" } outboundsList := outboundList(strings) if result, ok := observeReport.(*observatory.ObservationResult); ok { status := result.Status leastPing := int64(99999999) selectedOutboundName := "" for _, v := range status { if outboundsList.contains(v.OutboundTag) && v.Alive && v.Delay < leastPing { selectedOutboundName = v.OutboundTag leastPing = v.Delay } } { l.lock.Lock() if selectedOutboundName == "" && l.config.StickyChoice { selectedOutboundName = l.lastChoice } l.lastChoice = selectedOutboundName l.lock.Unlock() } return selectedOutboundName } // No way to understand observeReport return "" } type outboundList []string func (o outboundList) contains(name string) bool { for _, v := range o { if v == name { return true } } return false } func init() { common.Must(common.RegisterConfig((*StrategyLeastPingConfig)(nil), nil)) } ================================================ FILE: app/router/strategy_random.go ================================================ package router import ( "context" "google.golang.org/protobuf/runtime/protoiface" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/observatory" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/extension" ) // RandomStrategy represents a random balancing strategy type RandomStrategy struct { ctx context.Context settings *StrategyRandomConfig observatory extension.Observatory } func (s *RandomStrategy) GetPrincipleTarget(strings []string) []string { return strings } // NewRandomStrategy creates a new RandomStrategy with settings func NewRandomStrategy(settings *StrategyRandomConfig) *RandomStrategy { return &RandomStrategy{ settings: settings, } } func (s *RandomStrategy) InjectContext(ctx context.Context) { if s != nil { s.ctx = ctx } } func (s *RandomStrategy) PickOutbound(candidates []string) string { if s != nil && s.settings.AliveOnly { // candidates are considered alive unless observed otherwise if s.observatory == nil { core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error { s.observatory = observatory return nil }) } if s.observatory != nil { var observeReport protoiface.MessageV1 var err error if s.settings.ObserverTag == "" { observeReport, err = s.observatory.GetObservation(s.ctx) } else { observeReport, err = common.Must2(s.observatory.(features.TaggedFeatures).GetFeaturesByTag(s.settings.ObserverTag)).(extension.Observatory).GetObservation(s.ctx) } if err == nil { aliveTags := make([]string, 0) if result, ok := observeReport.(*observatory.ObservationResult); ok { status := result.Status statusMap := make(map[string]*observatory.OutboundStatus) for _, outboundStatus := range status { statusMap[outboundStatus.OutboundTag] = outboundStatus } for _, candidate := range candidates { if outboundStatus, found := statusMap[candidate]; found { if outboundStatus.Alive { aliveTags = append(aliveTags, candidate) } } else { // unfound candidate is considered alive aliveTags = append(aliveTags, candidate) } } candidates = aliveTags } } } } count := len(candidates) if count == 0 { // goes to fallbackTag return "" } return candidates[dice.Roll(count)] } func init() { common.Must(common.RegisterConfig((*StrategyRandomConfig)(nil), nil)) } ================================================ FILE: app/router/weight.go ================================================ package router import ( "regexp" "strconv" "strings" "sync" ) type weightScaler func(value, weight float64) float64 var numberFinder = regexp.MustCompile(`\d+(\.\d+)?`) // NewWeightManager creates a new WeightManager with settings func NewWeightManager(s []*StrategyWeight, defaultWeight float64, scaler weightScaler) *WeightManager { return &WeightManager{ settings: s, cache: make(map[string]float64), scaler: scaler, defaultWeight: defaultWeight, } } // WeightManager manages weights for specific settings type WeightManager struct { settings []*StrategyWeight cache map[string]float64 scaler weightScaler defaultWeight float64 mu sync.Mutex } // Get gets the weight of specified tag func (s *WeightManager) Get(tag string) float64 { s.mu.Lock() defer s.mu.Unlock() weight, ok := s.cache[tag] if ok { return weight } weight = s.findValue(tag) s.cache[tag] = weight return weight } // Apply applies weight to the value func (s *WeightManager) Apply(tag string, value float64) float64 { return s.scaler(value, s.Get(tag)) } func (s *WeightManager) findValue(tag string) float64 { for _, w := range s.settings { matched := s.getMatch(tag, w.Match, w.Regexp) if matched == "" { continue } if w.Value > 0 { return float64(w.Value) } // auto weight from matched numStr := numberFinder.FindString(matched) if numStr == "" { return s.defaultWeight } weight, err := strconv.ParseFloat(numStr, 64) if err != nil { newError("unexpected error from ParseFloat: ", err).AtError().WriteToLog() return s.defaultWeight } return weight } return s.defaultWeight } func (s *WeightManager) getMatch(tag, find string, isRegexp bool) string { if !isRegexp { idx := strings.Index(tag, find) if idx < 0 { return "" } return find } r, err := regexp.Compile(find) if err != nil { newError("invalid regexp: ", find, "err: ", err).AtError().WriteToLog() return "" } return r.FindString(tag) } ================================================ FILE: app/router/weight_test.go ================================================ package router_test import ( "reflect" "testing" "github.com/v2fly/v2ray-core/v5/app/router" ) func TestWeight(t *testing.T) { manager := router.NewWeightManager( []*router.StrategyWeight{ { Match: "x5", Value: 100, }, { Match: "x8", }, { Regexp: true, Match: `\bx0+(\.\d+)?\b`, Value: 1, }, { Regexp: true, Match: `\bx\d+(\.\d+)?\b`, }, }, 1, func(v, w float64) float64 { return v * w }, ) tags := []string{ "node name, x5, and more", "node name, x8", "node name, x15", "node name, x0100, and more", "node name, x10.1", "node name, x00.1, and more", } // test weight expected := []float64{100, 8, 15, 100, 10.1, 1} actual := make([]float64, 0) for _, tag := range tags { actual = append(actual, manager.Get(tag)) } if !reflect.DeepEqual(expected, actual) { t.Errorf("expected: %v, actual: %v", expected, actual) } // test scale expected2 := []float64{1000, 80, 150, 1000, 101, 10} actual2 := make([]float64, 0) for _, tag := range tags { actual2 = append(actual2, manager.Apply(tag, 10)) } if !reflect.DeepEqual(expected2, actual2) { t.Errorf("expected2: %v, actual2: %v", expected2, actual2) } } ================================================ FILE: app/stats/channel.go ================================================ package stats import ( "context" "sync" "github.com/v2fly/v2ray-core/v5/common" ) // Channel is an implementation of stats.Channel. type Channel struct { channel chan channelMessage subscribers []chan interface{} // Synchronization components access sync.RWMutex closed chan struct{} // Channel options blocking bool // Set blocking state if channel buffer reaches limit bufferSize int // Set to 0 as no buffering subsLimit int // Set to 0 as no subscriber limit } // NewChannel creates an instance of Statistics Channel. func NewChannel(config *ChannelConfig) *Channel { return &Channel{ channel: make(chan channelMessage, config.BufferSize), subsLimit: int(config.SubscriberLimit), bufferSize: int(config.BufferSize), blocking: config.Blocking, } } // Subscribers implements stats.Channel. func (c *Channel) Subscribers() []chan interface{} { c.access.RLock() defer c.access.RUnlock() return c.subscribers } // Subscribe implements stats.Channel. func (c *Channel) Subscribe() (chan interface{}, error) { c.access.Lock() defer c.access.Unlock() if c.subsLimit > 0 && len(c.subscribers) >= c.subsLimit { return nil, newError("Number of subscribers has reached limit") } subscriber := make(chan interface{}, c.bufferSize) c.subscribers = append(c.subscribers, subscriber) return subscriber, nil } // Unsubscribe implements stats.Channel. func (c *Channel) Unsubscribe(subscriber chan interface{}) error { c.access.Lock() defer c.access.Unlock() for i, s := range c.subscribers { if s == subscriber { // Copy to new memory block to prevent modifying original data subscribers := make([]chan interface{}, len(c.subscribers)-1) copy(subscribers[:i], c.subscribers[:i]) copy(subscribers[i:], c.subscribers[i+1:]) c.subscribers = subscribers } } return nil } // Publish implements stats.Channel. func (c *Channel) Publish(ctx context.Context, msg interface{}) { select { // Early exit if channel closed case <-c.closed: return default: pub := channelMessage{context: ctx, message: msg} if c.blocking { pub.publish(c.channel) } else { pub.publishNonBlocking(c.channel) } } } // Running returns whether the channel is running. func (c *Channel) Running() bool { select { case <-c.closed: // Channel closed default: // Channel running or not initialized if c.closed != nil { // Channel initialized return true } } return false } // Start implements common.Runnable. func (c *Channel) Start() error { c.access.Lock() defer c.access.Unlock() if !c.Running() { c.closed = make(chan struct{}) // Reset close signal go func() { for { select { case pub := <-c.channel: // Published message received for _, sub := range c.Subscribers() { // Concurrency-safe subscribers retrievement if c.blocking { pub.broadcast(sub) } else { pub.broadcastNonBlocking(sub) } } case <-c.closed: // Channel closed for _, sub := range c.Subscribers() { // Remove all subscribers common.Must(c.Unsubscribe(sub)) close(sub) } return } } }() } return nil } // Close implements common.Closable. func (c *Channel) Close() error { c.access.Lock() defer c.access.Unlock() if c.Running() { close(c.closed) // Send closed signal } return nil } // channelMessage is the published message with guaranteed delivery. // message is discarded only when the context is early cancelled. type channelMessage struct { context context.Context message interface{} } func (c channelMessage) publish(publisher chan channelMessage) { select { case publisher <- c: case <-c.context.Done(): } } func (c channelMessage) publishNonBlocking(publisher chan channelMessage) { select { case publisher <- c: default: // Create another goroutine to keep sending message go c.publish(publisher) } } func (c channelMessage) broadcast(subscriber chan interface{}) { select { case subscriber <- c.message: case <-c.context.Done(): } } func (c channelMessage) broadcastNonBlocking(subscriber chan interface{}) { select { case subscriber <- c.message: default: // Create another goroutine to keep sending message go c.broadcast(subscriber) } } ================================================ FILE: app/stats/channel_test.go ================================================ package stats_test import ( "context" "fmt" "testing" "time" . "github.com/v2fly/v2ray-core/v5/app/stats" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/stats" ) func TestStatsChannel(t *testing.T) { // At most 2 subscribers could be registered c := NewChannel(&ChannelConfig{SubscriberLimit: 2, Blocking: true}) a, err := stats.SubscribeRunnableChannel(c) common.Must(err) if !c.Running() { t.Fatal("unexpected failure in running channel after first subscription") } b, err := c.Subscribe() common.Must(err) // Test that third subscriber is forbidden _, err = c.Subscribe() if err == nil { t.Fatal("unexpected successful subscription") } t.Log("expected error: ", err) stopCh := make(chan struct{}) errCh := make(chan string) go func() { c.Publish(context.Background(), 1) c.Publish(context.Background(), 2) c.Publish(context.Background(), "3") c.Publish(context.Background(), []int{4}) stopCh <- struct{}{} }() go func() { if v, ok := (<-a).(int); !ok || v != 1 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 1) } if v, ok := (<-a).(int); !ok || v != 2 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 2) } if v, ok := (<-a).(string); !ok || v != "3" { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", "3") } if v, ok := (<-a).([]int); !ok || v[0] != 4 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", []int{4}) } stopCh <- struct{}{} }() go func() { if v, ok := (<-b).(int); !ok || v != 1 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 1) } if v, ok := (<-b).(int); !ok || v != 2 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 2) } if v, ok := (<-b).(string); !ok || v != "3" { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", "3") } if v, ok := (<-b).([]int); !ok || v[0] != 4 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", []int{4}) } stopCh <- struct{}{} }() timeout := time.After(2 * time.Second) for i := 0; i < 3; i++ { select { case <-timeout: t.Fatal("Test timeout after 2s") case e := <-errCh: t.Fatal(e) case <-stopCh: } } // Test the unsubscription of channel common.Must(c.Unsubscribe(b)) // Test the last subscriber will close channel with `UnsubscribeClosableChannel` common.Must(stats.UnsubscribeClosableChannel(c, a)) if c.Running() { t.Fatal("unexpected running channel after unsubscribing the last subscriber") } } func TestStatsChannelUnsubcribe(t *testing.T) { c := NewChannel(&ChannelConfig{Blocking: true}) common.Must(c.Start()) defer c.Close() a, err := c.Subscribe() common.Must(err) defer c.Unsubscribe(a) b, err := c.Subscribe() common.Must(err) pauseCh := make(chan struct{}) stopCh := make(chan struct{}) errCh := make(chan string) { var aSet, bSet bool for _, s := range c.Subscribers() { if s == a { aSet = true } if s == b { bSet = true } } if !(aSet && bSet) { t.Fatal("unexpected subscribers: ", c.Subscribers()) } } go func() { // Blocking publish c.Publish(context.Background(), 1) <-pauseCh // Wait for `b` goroutine to resume sending message c.Publish(context.Background(), 2) }() go func() { if v, ok := (<-a).(int); !ok || v != 1 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 1) } if v, ok := (<-a).(int); !ok || v != 2 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 2) } }() go func() { if v, ok := (<-b).(int); !ok || v != 1 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 1) } // Unsubscribe `b` while publishing is paused c.Unsubscribe(b) { // Test `b` is not in subscribers var aSet, bSet bool for _, s := range c.Subscribers() { if s == a { aSet = true } if s == b { bSet = true } } if !(aSet && !bSet) { errCh <- fmt.Sprint("unexpected subscribers: ", c.Subscribers()) } } // Resume publishing progress close(pauseCh) // Test `b` is neither closed nor able to receive any data select { case v, ok := <-b: if ok { errCh <- fmt.Sprint("unexpected data received: ", v) } else { errCh <- fmt.Sprint("unexpected closed channel: ", b) } default: } close(stopCh) }() select { case <-time.After(2 * time.Second): t.Fatal("Test timeout after 2s") case e := <-errCh: t.Fatal(e) case <-stopCh: } } func TestStatsChannelBlocking(t *testing.T) { // Do not use buffer so as to create blocking scenario c := NewChannel(&ChannelConfig{BufferSize: 0, Blocking: true}) common.Must(c.Start()) defer c.Close() a, err := c.Subscribe() common.Must(err) defer c.Unsubscribe(a) pauseCh := make(chan struct{}) stopCh := make(chan struct{}) errCh := make(chan string) ctx, cancel := context.WithCancel(context.Background()) // Test blocking channel publishing go func() { // Dummy message with no subscriber receiving, will block broadcasting goroutine c.Publish(context.Background(), nil) <-pauseCh // Publishing should be blocked here, for last message was not cleared and buffer was full c.Publish(context.Background(), nil) pauseCh <- struct{}{} // Publishing should still be blocked here c.Publish(ctx, nil) // Check publishing is done because context is canceled select { case <-ctx.Done(): if ctx.Err() != context.Canceled { errCh <- fmt.Sprint("unexpected error: ", ctx.Err()) } default: errCh <- "unexpected non-blocked publishing" } close(stopCh) }() go func() { pauseCh <- struct{}{} select { case <-pauseCh: errCh <- "unexpected non-blocked publishing" case <-time.After(100 * time.Millisecond): } // Receive first published message <-a select { case <-pauseCh: case <-time.After(100 * time.Millisecond): errCh <- "unexpected blocking publishing" } // Manually cancel the context to end publishing cancel() }() select { case <-time.After(2 * time.Second): t.Fatal("Test timeout after 2s") case e := <-errCh: t.Fatal(e) case <-stopCh: } } func TestStatsChannelNonBlocking(t *testing.T) { // Do not use buffer so as to create blocking scenario c := NewChannel(&ChannelConfig{BufferSize: 0, Blocking: false}) common.Must(c.Start()) defer c.Close() a, err := c.Subscribe() common.Must(err) defer c.Unsubscribe(a) pauseCh := make(chan struct{}) stopCh := make(chan struct{}) errCh := make(chan string) ctx, cancel := context.WithCancel(context.Background()) // Test blocking channel publishing go func() { c.Publish(context.Background(), nil) c.Publish(context.Background(), nil) pauseCh <- struct{}{} <-pauseCh c.Publish(ctx, nil) c.Publish(ctx, nil) // Check publishing is done because context is canceled select { case <-ctx.Done(): if ctx.Err() != context.Canceled { errCh <- fmt.Sprint("unexpected error: ", ctx.Err()) } case <-time.After(100 * time.Millisecond): errCh <- "unexpected non-cancelled publishing" } }() go func() { // Check publishing won't block even if there is no subscriber receiving message select { case <-pauseCh: case <-time.After(100 * time.Millisecond): errCh <- "unexpected blocking publishing" } // Receive first and second published message <-a <-a pauseCh <- struct{}{} // Manually cancel the context to end publishing cancel() // Check third and forth published message is cancelled and cannot receive <-time.After(100 * time.Millisecond) select { case <-a: errCh <- "unexpected non-cancelled publishing" default: } select { case <-a: errCh <- "unexpected non-cancelled publishing" default: } close(stopCh) }() select { case <-time.After(2 * time.Second): t.Fatal("Test timeout after 2s") case e := <-errCh: t.Fatal(e) case <-stopCh: } } func TestStatsChannelConcurrency(t *testing.T) { // Do not use buffer so as to create blocking scenario c := NewChannel(&ChannelConfig{BufferSize: 0, Blocking: true}) common.Must(c.Start()) defer c.Close() a, err := c.Subscribe() common.Must(err) defer c.Unsubscribe(a) b, err := c.Subscribe() common.Must(err) defer c.Unsubscribe(b) stopCh := make(chan struct{}) errCh := make(chan string) go func() { // Blocking publish c.Publish(context.Background(), 1) c.Publish(context.Background(), 2) }() go func() { if v, ok := (<-a).(int); !ok || v != 1 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 1) } if v, ok := (<-a).(int); !ok || v != 2 { errCh <- fmt.Sprint("unexpected receiving: ", v, ", wanted ", 2) } }() go func() { // Block `b` for a time so as to ensure source channel is trying to send message to `b`. <-time.After(25 * time.Millisecond) // This causes concurrency scenario: unsubscribe `b` while trying to send message to it c.Unsubscribe(b) // Test `b` is not closed and can still receive data 1: // Because unsubscribe won't affect the ongoing process of sending message. select { case v, ok := <-b: if v1, ok1 := v.(int); !(ok && ok1 && v1 == 1) { errCh <- fmt.Sprint("unexpected failure in receiving data: ", 1) } default: errCh <- fmt.Sprint("unexpected block from receiving data: ", 1) } // Test `b` is not closed but cannot receive data 2: // Because in a new round of messaging, `b` has been unsubscribed. select { case v, ok := <-b: if ok { errCh <- fmt.Sprint("unexpected receiving: ", v) } else { errCh <- "unexpected closing of channel" } default: } close(stopCh) }() select { case <-time.After(2 * time.Second): t.Fatal("Test timeout after 2s") case e := <-errCh: t.Fatal(e) case <-stopCh: } } ================================================ FILE: app/stats/command/command.go ================================================ package command //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "runtime" "time" grpc "google.golang.org/grpc" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/stats" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/strmatcher" feature_stats "github.com/v2fly/v2ray-core/v5/features/stats" ) // statsServer is an implementation of StatsService. type statsServer struct { stats feature_stats.Manager startTime time.Time } func NewStatsServer(manager feature_stats.Manager) StatsServiceServer { return &statsServer{ stats: manager, startTime: time.Now(), } } func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { c := s.stats.GetCounter(request.Name) if c == nil { return nil, newError(request.Name, " not found.") } var value int64 if request.Reset_ { value = c.Set(0) } else { value = c.Value() } return &GetStatsResponse{ Stat: &Stat{ Name: request.Name, Value: value, }, }, nil } func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) { mgroup := &strmatcher.LinearIndexMatcher{} if request.Pattern != "" { request.Patterns = append(request.Patterns, request.Pattern) } t := strmatcher.Substr if request.Regexp { t = strmatcher.Regex } for _, p := range request.Patterns { m, err := t.New(p) if err != nil { return nil, err } mgroup.Add(m) } response := &QueryStatsResponse{} manager, ok := s.stats.(*stats.Manager) if !ok { return nil, newError("QueryStats only works its own stats.Manager.") } manager.VisitCounters(func(name string, c feature_stats.Counter) bool { if mgroup.Size() == 0 || len(mgroup.Match(name)) > 0 { var value int64 if request.Reset_ { value = c.Set(0) } else { value = c.Value() } response.Stat = append(response.Stat, &Stat{ Name: name, Value: value, }) } return true }) return response, nil } func (s *statsServer) GetSysStats(ctx context.Context, request *SysStatsRequest) (*SysStatsResponse, error) { var rtm runtime.MemStats runtime.ReadMemStats(&rtm) uptime := time.Since(s.startTime) response := &SysStatsResponse{ Uptime: uint32(uptime.Seconds()), NumGoroutine: uint32(runtime.NumGoroutine()), Alloc: rtm.Alloc, TotalAlloc: rtm.TotalAlloc, Sys: rtm.Sys, Mallocs: rtm.Mallocs, Frees: rtm.Frees, LiveObjects: rtm.Mallocs - rtm.Frees, NumGC: rtm.NumGC, PauseTotalNs: rtm.PauseTotalNs, } return response, nil } func (s *statsServer) mustEmbedUnimplementedStatsServiceServer() {} type service struct { statsManager feature_stats.Manager } func (s *service) Register(server *grpc.Server) { RegisterStatsServiceServer(server, NewStatsServer(s.statsManager)) } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { s := new(service) core.RequireFeatures(ctx, func(sm feature_stats.Manager) { s.statsManager = sm }) return s, nil })) } ================================================ FILE: app/stats/command/command.pb.go ================================================ package command import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type GetStatsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Name of the stat counter. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Whether or not to reset the counter to fetching its value. Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetStatsRequest) Reset() { *x = GetStatsRequest{} mi := &file_app_stats_command_command_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetStatsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetStatsRequest) ProtoMessage() {} func (x *GetStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetStatsRequest.ProtoReflect.Descriptor instead. func (*GetStatsRequest) Descriptor() ([]byte, []int) { return file_app_stats_command_command_proto_rawDescGZIP(), []int{0} } func (x *GetStatsRequest) GetName() string { if x != nil { return x.Name } return "" } func (x *GetStatsRequest) GetReset_() bool { if x != nil { return x.Reset_ } return false } type Stat struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Stat) Reset() { *x = Stat{} mi := &file_app_stats_command_command_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Stat) String() string { return protoimpl.X.MessageStringOf(x) } func (*Stat) ProtoMessage() {} func (x *Stat) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Stat.ProtoReflect.Descriptor instead. func (*Stat) Descriptor() ([]byte, []int) { return file_app_stats_command_command_proto_rawDescGZIP(), []int{1} } func (x *Stat) GetName() string { if x != nil { return x.Name } return "" } func (x *Stat) GetValue() int64 { if x != nil { return x.Value } return 0 } type GetStatsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Stat *Stat `protobuf:"bytes,1,opt,name=stat,proto3" json:"stat,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetStatsResponse) Reset() { *x = GetStatsResponse{} mi := &file_app_stats_command_command_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetStatsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetStatsResponse) ProtoMessage() {} func (x *GetStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetStatsResponse.ProtoReflect.Descriptor instead. func (*GetStatsResponse) Descriptor() ([]byte, []int) { return file_app_stats_command_command_proto_rawDescGZIP(), []int{2} } func (x *GetStatsResponse) GetStat() *Stat { if x != nil { return x.Stat } return nil } type QueryStatsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Deprecated, use Patterns instead Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"` Patterns []string `protobuf:"bytes,3,rep,name=patterns,proto3" json:"patterns,omitempty"` Regexp bool `protobuf:"varint,4,opt,name=regexp,proto3" json:"regexp,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *QueryStatsRequest) Reset() { *x = QueryStatsRequest{} mi := &file_app_stats_command_command_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *QueryStatsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*QueryStatsRequest) ProtoMessage() {} func (x *QueryStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use QueryStatsRequest.ProtoReflect.Descriptor instead. func (*QueryStatsRequest) Descriptor() ([]byte, []int) { return file_app_stats_command_command_proto_rawDescGZIP(), []int{3} } func (x *QueryStatsRequest) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *QueryStatsRequest) GetReset_() bool { if x != nil { return x.Reset_ } return false } func (x *QueryStatsRequest) GetPatterns() []string { if x != nil { return x.Patterns } return nil } func (x *QueryStatsRequest) GetRegexp() bool { if x != nil { return x.Regexp } return false } type QueryStatsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Stat []*Stat `protobuf:"bytes,1,rep,name=stat,proto3" json:"stat,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *QueryStatsResponse) Reset() { *x = QueryStatsResponse{} mi := &file_app_stats_command_command_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *QueryStatsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*QueryStatsResponse) ProtoMessage() {} func (x *QueryStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use QueryStatsResponse.ProtoReflect.Descriptor instead. func (*QueryStatsResponse) Descriptor() ([]byte, []int) { return file_app_stats_command_command_proto_rawDescGZIP(), []int{4} } func (x *QueryStatsResponse) GetStat() []*Stat { if x != nil { return x.Stat } return nil } type SysStatsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SysStatsRequest) Reset() { *x = SysStatsRequest{} mi := &file_app_stats_command_command_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SysStatsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*SysStatsRequest) ProtoMessage() {} func (x *SysStatsRequest) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SysStatsRequest.ProtoReflect.Descriptor instead. func (*SysStatsRequest) Descriptor() ([]byte, []int) { return file_app_stats_command_command_proto_rawDescGZIP(), []int{5} } type SysStatsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` NumGoroutine uint32 `protobuf:"varint,1,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"` NumGC uint32 `protobuf:"varint,2,opt,name=NumGC,proto3" json:"NumGC,omitempty"` Alloc uint64 `protobuf:"varint,3,opt,name=Alloc,proto3" json:"Alloc,omitempty"` TotalAlloc uint64 `protobuf:"varint,4,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"` Sys uint64 `protobuf:"varint,5,opt,name=Sys,proto3" json:"Sys,omitempty"` Mallocs uint64 `protobuf:"varint,6,opt,name=Mallocs,proto3" json:"Mallocs,omitempty"` Frees uint64 `protobuf:"varint,7,opt,name=Frees,proto3" json:"Frees,omitempty"` LiveObjects uint64 `protobuf:"varint,8,opt,name=LiveObjects,proto3" json:"LiveObjects,omitempty"` PauseTotalNs uint64 `protobuf:"varint,9,opt,name=PauseTotalNs,proto3" json:"PauseTotalNs,omitempty"` Uptime uint32 `protobuf:"varint,10,opt,name=Uptime,proto3" json:"Uptime,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SysStatsResponse) Reset() { *x = SysStatsResponse{} mi := &file_app_stats_command_command_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SysStatsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*SysStatsResponse) ProtoMessage() {} func (x *SysStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SysStatsResponse.ProtoReflect.Descriptor instead. func (*SysStatsResponse) Descriptor() ([]byte, []int) { return file_app_stats_command_command_proto_rawDescGZIP(), []int{6} } func (x *SysStatsResponse) GetNumGoroutine() uint32 { if x != nil { return x.NumGoroutine } return 0 } func (x *SysStatsResponse) GetNumGC() uint32 { if x != nil { return x.NumGC } return 0 } func (x *SysStatsResponse) GetAlloc() uint64 { if x != nil { return x.Alloc } return 0 } func (x *SysStatsResponse) GetTotalAlloc() uint64 { if x != nil { return x.TotalAlloc } return 0 } func (x *SysStatsResponse) GetSys() uint64 { if x != nil { return x.Sys } return 0 } func (x *SysStatsResponse) GetMallocs() uint64 { if x != nil { return x.Mallocs } return 0 } func (x *SysStatsResponse) GetFrees() uint64 { if x != nil { return x.Frees } return 0 } func (x *SysStatsResponse) GetLiveObjects() uint64 { if x != nil { return x.LiveObjects } return 0 } func (x *SysStatsResponse) GetPauseTotalNs() uint64 { if x != nil { return x.PauseTotalNs } return 0 } func (x *SysStatsResponse) GetUptime() uint32 { if x != nil { return x.Uptime } return 0 } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_stats_command_command_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_stats_command_command_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_stats_command_command_proto_rawDescGZIP(), []int{7} } var File_app_stats_command_command_proto protoreflect.FileDescriptor const file_app_stats_command_command_proto_rawDesc = "" + "\n" + "\x1fapp/stats/command/command.proto\x12\x1cv2ray.core.app.stats.command\x1a common/protoext/extensions.proto\";\n" + "\x0fGetStatsRequest\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + "\x05reset\x18\x02 \x01(\bR\x05reset\"0\n" + "\x04Stat\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + "\x05value\x18\x02 \x01(\x03R\x05value\"J\n" + "\x10GetStatsResponse\x126\n" + "\x04stat\x18\x01 \x01(\v2\".v2ray.core.app.stats.command.StatR\x04stat\"w\n" + "\x11QueryStatsRequest\x12\x18\n" + "\apattern\x18\x01 \x01(\tR\apattern\x12\x14\n" + "\x05reset\x18\x02 \x01(\bR\x05reset\x12\x1a\n" + "\bpatterns\x18\x03 \x03(\tR\bpatterns\x12\x16\n" + "\x06regexp\x18\x04 \x01(\bR\x06regexp\"L\n" + "\x12QueryStatsResponse\x126\n" + "\x04stat\x18\x01 \x03(\v2\".v2ray.core.app.stats.command.StatR\x04stat\"\x11\n" + "\x0fSysStatsRequest\"\xa2\x02\n" + "\x10SysStatsResponse\x12\"\n" + "\fNumGoroutine\x18\x01 \x01(\rR\fNumGoroutine\x12\x14\n" + "\x05NumGC\x18\x02 \x01(\rR\x05NumGC\x12\x14\n" + "\x05Alloc\x18\x03 \x01(\x04R\x05Alloc\x12\x1e\n" + "\n" + "TotalAlloc\x18\x04 \x01(\x04R\n" + "TotalAlloc\x12\x10\n" + "\x03Sys\x18\x05 \x01(\x04R\x03Sys\x12\x18\n" + "\aMallocs\x18\x06 \x01(\x04R\aMallocs\x12\x14\n" + "\x05Frees\x18\a \x01(\x04R\x05Frees\x12 \n" + "\vLiveObjects\x18\b \x01(\x04R\vLiveObjects\x12\"\n" + "\fPauseTotalNs\x18\t \x01(\x04R\fPauseTotalNs\x12\x16\n" + "\x06Uptime\x18\n" + " \x01(\rR\x06Uptime\"\"\n" + "\x06Config:\x18\x82\xb5\x18\x14\n" + "\vgrpcservice\x12\x05stats2\xde\x02\n" + "\fStatsService\x12k\n" + "\bGetStats\x12-.v2ray.core.app.stats.command.GetStatsRequest\x1a..v2ray.core.app.stats.command.GetStatsResponse\"\x00\x12q\n" + "\n" + "QueryStats\x12/.v2ray.core.app.stats.command.QueryStatsRequest\x1a0.v2ray.core.app.stats.command.QueryStatsResponse\"\x00\x12n\n" + "\vGetSysStats\x12-.v2ray.core.app.stats.command.SysStatsRequest\x1a..v2ray.core.app.stats.command.SysStatsResponse\"\x00Bu\n" + " com.v2ray.core.app.stats.commandP\x01Z0github.com/v2fly/v2ray-core/v5/app/stats/command\xaa\x02\x1cV2Ray.Core.App.Stats.Commandb\x06proto3" var ( file_app_stats_command_command_proto_rawDescOnce sync.Once file_app_stats_command_command_proto_rawDescData []byte ) func file_app_stats_command_command_proto_rawDescGZIP() []byte { file_app_stats_command_command_proto_rawDescOnce.Do(func() { file_app_stats_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_stats_command_command_proto_rawDesc), len(file_app_stats_command_command_proto_rawDesc))) }) return file_app_stats_command_command_proto_rawDescData } var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_app_stats_command_command_proto_goTypes = []any{ (*GetStatsRequest)(nil), // 0: v2ray.core.app.stats.command.GetStatsRequest (*Stat)(nil), // 1: v2ray.core.app.stats.command.Stat (*GetStatsResponse)(nil), // 2: v2ray.core.app.stats.command.GetStatsResponse (*QueryStatsRequest)(nil), // 3: v2ray.core.app.stats.command.QueryStatsRequest (*QueryStatsResponse)(nil), // 4: v2ray.core.app.stats.command.QueryStatsResponse (*SysStatsRequest)(nil), // 5: v2ray.core.app.stats.command.SysStatsRequest (*SysStatsResponse)(nil), // 6: v2ray.core.app.stats.command.SysStatsResponse (*Config)(nil), // 7: v2ray.core.app.stats.command.Config } var file_app_stats_command_command_proto_depIdxs = []int32{ 1, // 0: v2ray.core.app.stats.command.GetStatsResponse.stat:type_name -> v2ray.core.app.stats.command.Stat 1, // 1: v2ray.core.app.stats.command.QueryStatsResponse.stat:type_name -> v2ray.core.app.stats.command.Stat 0, // 2: v2ray.core.app.stats.command.StatsService.GetStats:input_type -> v2ray.core.app.stats.command.GetStatsRequest 3, // 3: v2ray.core.app.stats.command.StatsService.QueryStats:input_type -> v2ray.core.app.stats.command.QueryStatsRequest 5, // 4: v2ray.core.app.stats.command.StatsService.GetSysStats:input_type -> v2ray.core.app.stats.command.SysStatsRequest 2, // 5: v2ray.core.app.stats.command.StatsService.GetStats:output_type -> v2ray.core.app.stats.command.GetStatsResponse 4, // 6: v2ray.core.app.stats.command.StatsService.QueryStats:output_type -> v2ray.core.app.stats.command.QueryStatsResponse 6, // 7: v2ray.core.app.stats.command.StatsService.GetSysStats:output_type -> v2ray.core.app.stats.command.SysStatsResponse 5, // [5:8] is the sub-list for method output_type 2, // [2:5] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_app_stats_command_command_proto_init() } func file_app_stats_command_command_proto_init() { if File_app_stats_command_command_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_stats_command_command_proto_rawDesc), len(file_app_stats_command_command_proto_rawDesc)), NumEnums: 0, NumMessages: 8, NumExtensions: 0, NumServices: 1, }, GoTypes: file_app_stats_command_command_proto_goTypes, DependencyIndexes: file_app_stats_command_command_proto_depIdxs, MessageInfos: file_app_stats_command_command_proto_msgTypes, }.Build() File_app_stats_command_command_proto = out.File file_app_stats_command_command_proto_goTypes = nil file_app_stats_command_command_proto_depIdxs = nil } ================================================ FILE: app/stats/command/command.proto ================================================ syntax = "proto3"; package v2ray.core.app.stats.command; option csharp_namespace = "V2Ray.Core.App.Stats.Command"; option go_package = "github.com/v2fly/v2ray-core/v5/app/stats/command"; option java_package = "com.v2ray.core.app.stats.command"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message GetStatsRequest { // Name of the stat counter. string name = 1; // Whether or not to reset the counter to fetching its value. bool reset = 2; } message Stat { string name = 1; int64 value = 2; } message GetStatsResponse { Stat stat = 1; } message QueryStatsRequest { // Deprecated, use Patterns instead string pattern = 1; bool reset = 2; repeated string patterns = 3; bool regexp = 4; } message QueryStatsResponse { repeated Stat stat = 1; } message SysStatsRequest {} message SysStatsResponse { uint32 NumGoroutine = 1; uint32 NumGC = 2; uint64 Alloc = 3; uint64 TotalAlloc = 4; uint64 Sys = 5; uint64 Mallocs = 6; uint64 Frees = 7; uint64 LiveObjects = 8; uint64 PauseTotalNs = 9; uint32 Uptime = 10; } service StatsService { rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {} rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {} rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {} } message Config { option (v2ray.core.common.protoext.message_opt).type = "grpcservice"; option (v2ray.core.common.protoext.message_opt).short_name = "stats"; } ================================================ FILE: app/stats/command/command_grpc.pb.go ================================================ package command import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( StatsService_GetStats_FullMethodName = "/v2ray.core.app.stats.command.StatsService/GetStats" StatsService_QueryStats_FullMethodName = "/v2ray.core.app.stats.command.StatsService/QueryStats" StatsService_GetSysStats_FullMethodName = "/v2ray.core.app.stats.command.StatsService/GetSysStats" ) // StatsServiceClient is the client API for StatsService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type StatsServiceClient interface { GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) } type statsServiceClient struct { cc grpc.ClientConnInterface } func NewStatsServiceClient(cc grpc.ClientConnInterface) StatsServiceClient { return &statsServiceClient{cc} } func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetStatsResponse) err := c.cc.Invoke(ctx, StatsService_GetStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(QueryStatsResponse) err := c.cc.Invoke(ctx, StatsService_QueryStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SysStatsResponse) err := c.cc.Invoke(ctx, StatsService_GetSysStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // StatsServiceServer is the server API for StatsService service. // All implementations must embed UnimplementedStatsServiceServer // for forward compatibility. type StatsServiceServer interface { GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) mustEmbedUnimplementedStatsServiceServer() } // UnimplementedStatsServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedStatsServiceServer struct{} func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetStats not implemented") } func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryStats not implemented") } func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented") } func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {} func (UnimplementedStatsServiceServer) testEmbeddedByValue() {} // UnsafeStatsServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to StatsServiceServer will // result in compilation errors. type UnsafeStatsServiceServer interface { mustEmbedUnimplementedStatsServiceServer() } func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) { // If the following call pancis, it indicates UnimplementedStatsServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&StatsService_ServiceDesc, srv) } func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetStatsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(StatsServiceServer).GetStats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: StatsService_GetStats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest)) } return interceptor(ctx, in, info, handler) } func _StatsService_QueryStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryStatsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(StatsServiceServer).QueryStats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: StatsService_QueryStats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).QueryStats(ctx, req.(*QueryStatsRequest)) } return interceptor(ctx, in, info, handler) } func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SysStatsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(StatsServiceServer).GetSysStats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: StatsService_GetSysStats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).GetSysStats(ctx, req.(*SysStatsRequest)) } return interceptor(ctx, in, info, handler) } // StatsService_ServiceDesc is the grpc.ServiceDesc for StatsService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var StatsService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "v2ray.core.app.stats.command.StatsService", HandlerType: (*StatsServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "GetStats", Handler: _StatsService_GetStats_Handler, }, { MethodName: "QueryStats", Handler: _StatsService_QueryStats_Handler, }, { MethodName: "GetSysStats", Handler: _StatsService_GetSysStats_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "app/stats/command/command.proto", } ================================================ FILE: app/stats/command/command_test.go ================================================ package command_test import ( "context" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/v2fly/v2ray-core/v5/app/stats" . "github.com/v2fly/v2ray-core/v5/app/stats/command" "github.com/v2fly/v2ray-core/v5/common" ) func TestGetStats(t *testing.T) { m, err := stats.NewManager(context.Background(), &stats.Config{}) common.Must(err) sc, err := m.RegisterCounter("test_counter") common.Must(err) sc.Set(1) s := NewStatsServer(m) testCases := []struct { name string reset bool value int64 err bool }{ { name: "counterNotExist", err: true, }, { name: "test_counter", reset: true, value: 1, }, { name: "test_counter", value: 0, }, } for _, tc := range testCases { resp, err := s.GetStats(context.Background(), &GetStatsRequest{ Name: tc.name, Reset_: tc.reset, }) if tc.err { if err == nil { t.Error("nil error: ", tc.name) } } else { common.Must(err) if r := cmp.Diff(resp.Stat, &Stat{Name: tc.name, Value: tc.value}, cmpopts.IgnoreUnexported(Stat{})); r != "" { t.Error(r) } } } } func TestQueryStats(t *testing.T) { m, err := stats.NewManager(context.Background(), &stats.Config{}) common.Must(err) sc1, err := m.RegisterCounter("test_counter") common.Must(err) sc1.Set(1) sc2, err := m.RegisterCounter("test_counter_2") common.Must(err) sc2.Set(2) sc3, err := m.RegisterCounter("test_counter_3") common.Must(err) sc3.Set(3) s := NewStatsServer(m) resp, err := s.QueryStats(context.Background(), &QueryStatsRequest{ Pattern: "counter_", }) common.Must(err) if r := cmp.Diff(resp.Stat, []*Stat{ {Name: "test_counter_2", Value: 2}, {Name: "test_counter_3", Value: 3}, }, cmpopts.SortSlices(func(s1, s2 *Stat) bool { return s1.Name < s2.Name }), cmpopts.IgnoreUnexported(Stat{})); r != "" { t.Error(r) } } ================================================ FILE: app/stats/command/errors.generated.go ================================================ package command import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/stats/config.pb.go ================================================ package stats import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_stats_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_stats_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_stats_config_proto_rawDescGZIP(), []int{0} } type ChannelConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Blocking bool `protobuf:"varint,1,opt,name=Blocking,proto3" json:"Blocking,omitempty"` SubscriberLimit int32 `protobuf:"varint,2,opt,name=SubscriberLimit,proto3" json:"SubscriberLimit,omitempty"` BufferSize int32 `protobuf:"varint,3,opt,name=BufferSize,proto3" json:"BufferSize,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ChannelConfig) Reset() { *x = ChannelConfig{} mi := &file_app_stats_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ChannelConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ChannelConfig) ProtoMessage() {} func (x *ChannelConfig) ProtoReflect() protoreflect.Message { mi := &file_app_stats_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ChannelConfig.ProtoReflect.Descriptor instead. func (*ChannelConfig) Descriptor() ([]byte, []int) { return file_app_stats_config_proto_rawDescGZIP(), []int{1} } func (x *ChannelConfig) GetBlocking() bool { if x != nil { return x.Blocking } return false } func (x *ChannelConfig) GetSubscriberLimit() int32 { if x != nil { return x.SubscriberLimit } return 0 } func (x *ChannelConfig) GetBufferSize() int32 { if x != nil { return x.BufferSize } return 0 } var File_app_stats_config_proto protoreflect.FileDescriptor const file_app_stats_config_proto_rawDesc = "" + "\n" + "\x16app/stats/config.proto\x12\x14v2ray.core.app.stats\x1a common/protoext/extensions.proto\"\x1e\n" + "\x06Config:\x14\x82\xb5\x18\x10\n" + "\aservice\x12\x05stats\"u\n" + "\rChannelConfig\x12\x1a\n" + "\bBlocking\x18\x01 \x01(\bR\bBlocking\x12(\n" + "\x0fSubscriberLimit\x18\x02 \x01(\x05R\x0fSubscriberLimit\x12\x1e\n" + "\n" + "BufferSize\x18\x03 \x01(\x05R\n" + "BufferSizeB]\n" + "\x18com.v2ray.core.app.statsP\x01Z(github.com/v2fly/v2ray-core/v5/app/stats\xaa\x02\x14V2Ray.Core.App.Statsb\x06proto3" var ( file_app_stats_config_proto_rawDescOnce sync.Once file_app_stats_config_proto_rawDescData []byte ) func file_app_stats_config_proto_rawDescGZIP() []byte { file_app_stats_config_proto_rawDescOnce.Do(func() { file_app_stats_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_stats_config_proto_rawDesc), len(file_app_stats_config_proto_rawDesc))) }) return file_app_stats_config_proto_rawDescData } var file_app_stats_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_app_stats_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.stats.Config (*ChannelConfig)(nil), // 1: v2ray.core.app.stats.ChannelConfig } var file_app_stats_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_app_stats_config_proto_init() } func file_app_stats_config_proto_init() { if File_app_stats_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_stats_config_proto_rawDesc), len(file_app_stats_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_stats_config_proto_goTypes, DependencyIndexes: file_app_stats_config_proto_depIdxs, MessageInfos: file_app_stats_config_proto_msgTypes, }.Build() File_app_stats_config_proto = out.File file_app_stats_config_proto_goTypes = nil file_app_stats_config_proto_depIdxs = nil } ================================================ FILE: app/stats/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.stats; option csharp_namespace = "V2Ray.Core.App.Stats"; option go_package = "github.com/v2fly/v2ray-core/v5/app/stats"; option java_package = "com.v2ray.core.app.stats"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "stats"; } message ChannelConfig { bool Blocking = 1; int32 SubscriberLimit = 2; int32 BufferSize = 3; } ================================================ FILE: app/stats/counter.go ================================================ package stats import "sync/atomic" // Counter is an implementation of stats.Counter. type Counter struct { value int64 } // Value implements stats.Counter. func (c *Counter) Value() int64 { return atomic.LoadInt64(&c.value) } // Set implements stats.Counter. func (c *Counter) Set(newValue int64) int64 { return atomic.SwapInt64(&c.value, newValue) } // Add implements stats.Counter. func (c *Counter) Add(delta int64) int64 { return atomic.AddInt64(&c.value, delta) } ================================================ FILE: app/stats/counter_test.go ================================================ package stats_test import ( "context" "testing" . "github.com/v2fly/v2ray-core/v5/app/stats" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/stats" ) func TestStatsCounter(t *testing.T) { raw, err := common.CreateObject(context.Background(), &Config{}) common.Must(err) m := raw.(stats.Manager) c, err := m.RegisterCounter("test.counter") common.Must(err) if v := c.Add(1); v != 1 { t.Fatal("unpexcted Add(1) return: ", v, ", wanted ", 1) } if v := c.Set(0); v != 1 { t.Fatal("unexpected Set(0) return: ", v, ", wanted ", 1) } if v := c.Value(); v != 0 { t.Fatal("unexpected Value() return: ", v, ", wanted ", 0) } } ================================================ FILE: app/stats/errors.generated.go ================================================ package stats import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/stats/stats.go ================================================ package stats //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/features/stats" ) // Manager is an implementation of stats.Manager. type Manager struct { access sync.RWMutex counters map[string]*Counter channels map[string]*Channel running bool } // NewManager creates an instance of Statistics Manager. func NewManager(ctx context.Context, config *Config) (*Manager, error) { m := &Manager{ counters: make(map[string]*Counter), channels: make(map[string]*Channel), } return m, nil } // Type implements common.HasType. func (*Manager) Type() interface{} { return stats.ManagerType() } // RegisterCounter implements stats.Manager. func (m *Manager) RegisterCounter(name string) (stats.Counter, error) { m.access.Lock() defer m.access.Unlock() if _, found := m.counters[name]; found { return nil, newError("Counter ", name, " already registered.") } newError("create new counter ", name).AtDebug().WriteToLog() c := new(Counter) m.counters[name] = c return c, nil } // UnregisterCounter implements stats.Manager. func (m *Manager) UnregisterCounter(name string) error { m.access.Lock() defer m.access.Unlock() if _, found := m.counters[name]; found { newError("remove counter ", name).AtDebug().WriteToLog() delete(m.counters, name) } return nil } // GetCounter implements stats.Manager. func (m *Manager) GetCounter(name string) stats.Counter { m.access.RLock() defer m.access.RUnlock() if c, found := m.counters[name]; found { return c } return nil } // VisitCounters calls visitor function on all managed counters. func (m *Manager) VisitCounters(visitor func(string, stats.Counter) bool) { m.access.RLock() defer m.access.RUnlock() for name, c := range m.counters { if !visitor(name, c) { break } } } // RegisterChannel implements stats.Manager. func (m *Manager) RegisterChannel(name string) (stats.Channel, error) { m.access.Lock() defer m.access.Unlock() if _, found := m.channels[name]; found { return nil, newError("Channel ", name, " already registered.") } newError("create new channel ", name).AtDebug().WriteToLog() c := NewChannel(&ChannelConfig{BufferSize: 64, Blocking: false}) m.channels[name] = c if m.running { return c, c.Start() } return c, nil } // UnregisterChannel implements stats.Manager. func (m *Manager) UnregisterChannel(name string) error { m.access.Lock() defer m.access.Unlock() if c, found := m.channels[name]; found { newError("remove channel ", name).AtDebug().WriteToLog() delete(m.channels, name) return c.Close() } return nil } // GetChannel implements stats.Manager. func (m *Manager) GetChannel(name string) stats.Channel { m.access.RLock() defer m.access.RUnlock() if c, found := m.channels[name]; found { return c } return nil } // Start implements common.Runnable. func (m *Manager) Start() error { m.access.Lock() defer m.access.Unlock() m.running = true errs := []error{} for _, channel := range m.channels { if err := channel.Start(); err != nil { errs = append(errs, err) } } if len(errs) != 0 { return errors.Combine(errs...) } return nil } // Close implement common.Closable. func (m *Manager) Close() error { m.access.Lock() defer m.access.Unlock() m.running = false errs := []error{} for name, channel := range m.channels { newError("remove channel ", name).AtDebug().WriteToLog() delete(m.channels, name) if err := channel.Close(); err != nil { errs = append(errs, err) } } if len(errs) != 0 { return errors.Combine(errs...) } return nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewManager(ctx, config.(*Config)) })) } ================================================ FILE: app/stats/stats_test.go ================================================ package stats_test import ( "context" "testing" "time" . "github.com/v2fly/v2ray-core/v5/app/stats" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/stats" ) func TestInterface(t *testing.T) { _ = (stats.Manager)(new(Manager)) } func TestStatsChannelRunnable(t *testing.T) { raw, err := common.CreateObject(context.Background(), &Config{}) common.Must(err) m := raw.(stats.Manager) ch1, err := m.RegisterChannel("test.channel.1") c1 := ch1.(*Channel) common.Must(err) if c1.Running() { t.Fatalf("unexpected running channel: test.channel.%d", 1) } common.Must(m.Start()) if !c1.Running() { t.Fatalf("unexpected non-running channel: test.channel.%d", 1) } ch2, err := m.RegisterChannel("test.channel.2") c2 := ch2.(*Channel) common.Must(err) if !c2.Running() { t.Fatalf("unexpected non-running channel: test.channel.%d", 2) } s1, err := c1.Subscribe() common.Must(err) common.Must(c1.Close()) if c1.Running() { t.Fatalf("unexpected running channel: test.channel.%d", 1) } select { // Check all subscribers in closed channel are closed case _, ok := <-s1: if ok { t.Fatalf("unexpected non-closed subscriber in channel: test.channel.%d", 1) } case <-time.After(500 * time.Millisecond): t.Fatalf("unexpected non-closed subscriber in channel: test.channel.%d", 1) } if len(c1.Subscribers()) != 0 { // Check subscribers in closed channel are emptied t.Fatalf("unexpected non-empty subscribers in channel: test.channel.%d", 1) } common.Must(m.Close()) if c2.Running() { t.Fatalf("unexpected running channel: test.channel.%d", 2) } ch3, err := m.RegisterChannel("test.channel.3") c3 := ch3.(*Channel) common.Must(err) if c3.Running() { t.Fatalf("unexpected running channel: test.channel.%d", 3) } common.Must(c3.Start()) common.Must(m.UnregisterChannel("test.channel.3")) if c3.Running() { // Test that unregistering will close the channel. t.Fatalf("unexpected running channel: test.channel.%d", 3) } } ================================================ FILE: app/subscription/config.pb.go ================================================ package subscription import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ImportSource struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` TagPrefix string `protobuf:"bytes,3,opt,name=tag_prefix,json=tagPrefix,proto3" json:"tag_prefix,omitempty"` ImportUsingTag string `protobuf:"bytes,4,opt,name=import_using_tag,json=importUsingTag,proto3" json:"import_using_tag,omitempty"` DefaultExpireSeconds uint64 `protobuf:"varint,5,opt,name=default_expire_seconds,json=defaultExpireSeconds,proto3" json:"default_expire_seconds,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImportSource) Reset() { *x = ImportSource{} mi := &file_app_subscription_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImportSource) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImportSource) ProtoMessage() {} func (x *ImportSource) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImportSource.ProtoReflect.Descriptor instead. func (*ImportSource) Descriptor() ([]byte, []int) { return file_app_subscription_config_proto_rawDescGZIP(), []int{0} } func (x *ImportSource) GetName() string { if x != nil { return x.Name } return "" } func (x *ImportSource) GetUrl() string { if x != nil { return x.Url } return "" } func (x *ImportSource) GetTagPrefix() string { if x != nil { return x.TagPrefix } return "" } func (x *ImportSource) GetImportUsingTag() string { if x != nil { return x.ImportUsingTag } return "" } func (x *ImportSource) GetDefaultExpireSeconds() uint64 { if x != nil { return x.DefaultExpireSeconds } return 0 } // Config is the settings for Subscription Manager. type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Imports []*ImportSource `protobuf:"bytes,1,rep,name=imports,proto3" json:"imports,omitempty"` NonnativeConverterOverlay []byte `protobuf:"bytes,2,opt,name=nonnative_converter_overlay,json=nonnativeConverterOverlay,proto3" json:"nonnative_converter_overlay,omitempty"` NonnativeConverterOverlayFile string `protobuf:"bytes,96002,opt,name=nonnative_converter_overlay_file,json=nonnativeConverterOverlayFile,proto3" json:"nonnative_converter_overlay_file,omitempty"` Persistence bool `protobuf:"varint,3,opt,name=persistence,proto3" json:"persistence,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_subscription_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_subscription_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetImports() []*ImportSource { if x != nil { return x.Imports } return nil } func (x *Config) GetNonnativeConverterOverlay() []byte { if x != nil { return x.NonnativeConverterOverlay } return nil } func (x *Config) GetNonnativeConverterOverlayFile() string { if x != nil { return x.NonnativeConverterOverlayFile } return "" } func (x *Config) GetPersistence() bool { if x != nil { return x.Persistence } return false } var File_app_subscription_config_proto protoreflect.FileDescriptor const file_app_subscription_config_proto_rawDesc = "" + "\n" + "\x1dapp/subscription/config.proto\x12\x1bv2ray.core.app.subscription\x1a common/protoext/extensions.proto\"\xb3\x01\n" + "\fImportSource\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n" + "\x03url\x18\x02 \x01(\tR\x03url\x12\x1d\n" + "\n" + "tag_prefix\x18\x03 \x01(\tR\ttagPrefix\x12(\n" + "\x10import_using_tag\x18\x04 \x01(\tR\x0eimportUsingTag\x124\n" + "\x16default_expire_seconds\x18\x05 \x01(\x04R\x14defaultExpireSeconds\"\xba\x02\n" + "\x06Config\x12C\n" + "\aimports\x18\x01 \x03(\v2).v2ray.core.app.subscription.ImportSourceR\aimports\x12>\n" + "\x1bnonnative_converter_overlay\x18\x02 \x01(\fR\x19nonnativeConverterOverlay\x12l\n" + " nonnative_converter_overlay_file\x18\x82\xee\x05 \x01(\tB!\x82\xb5\x18\x1d\"\x1bnonnative_converter_overlayR\x1dnonnativeConverterOverlayFile\x12 \n" + "\vpersistence\x18\x03 \x01(\bR\vpersistence:\x1b\x82\xb5\x18\x17\n" + "\aservice\x12\fsubscriptionBr\n" + "\x1fcom.v2ray.core.app.subscriptionP\x01Z/github.com/v2fly/v2ray-core/v5/app/subscription\xaa\x02\x1bV2Ray.Core.App.Subscriptionb\x06proto3" var ( file_app_subscription_config_proto_rawDescOnce sync.Once file_app_subscription_config_proto_rawDescData []byte ) func file_app_subscription_config_proto_rawDescGZIP() []byte { file_app_subscription_config_proto_rawDescOnce.Do(func() { file_app_subscription_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_subscription_config_proto_rawDesc), len(file_app_subscription_config_proto_rawDesc))) }) return file_app_subscription_config_proto_rawDescData } var file_app_subscription_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_app_subscription_config_proto_goTypes = []any{ (*ImportSource)(nil), // 0: v2ray.core.app.subscription.ImportSource (*Config)(nil), // 1: v2ray.core.app.subscription.Config } var file_app_subscription_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.app.subscription.Config.imports:type_name -> v2ray.core.app.subscription.ImportSource 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_app_subscription_config_proto_init() } func file_app_subscription_config_proto_init() { if File_app_subscription_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_subscription_config_proto_rawDesc), len(file_app_subscription_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_subscription_config_proto_goTypes, DependencyIndexes: file_app_subscription_config_proto_depIdxs, MessageInfos: file_app_subscription_config_proto_msgTypes, }.Build() File_app_subscription_config_proto = out.File file_app_subscription_config_proto_goTypes = nil file_app_subscription_config_proto_depIdxs = nil } ================================================ FILE: app/subscription/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.subscription; option csharp_namespace = "V2Ray.Core.App.Subscription"; option go_package = "github.com/v2fly/v2ray-core/v5/app/subscription"; option java_package = "com.v2ray.core.app.subscription"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message ImportSource { string name = 1; string url = 2; string tag_prefix = 3; string import_using_tag = 4; uint64 default_expire_seconds = 5; } // Config is the settings for Subscription Manager. message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "subscription"; repeated ImportSource imports = 1; bytes nonnative_converter_overlay = 2; string nonnative_converter_overlay_file = 96002 [(v2ray.core.common.protoext.field_opt).convert_time_read_file_into = "nonnative_converter_overlay"]; bool persistence = 3; } ================================================ FILE: app/subscription/containers/base64urlline/base64urlline.go ================================================ package base64urlline //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/subscription/containers/base64urlline/errors.generated.go ================================================ package base64urlline import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/containers/base64urlline/parser.go ================================================ package base64urlline import ( "bufio" "bytes" "encoding/base64" "io" "github.com/v2fly/v2ray-core/v5/app/subscription/containers" "github.com/v2fly/v2ray-core/v5/common" ) func newBase64URLLineParser() containers.SubscriptionContainerDocumentParser { return &parser{} } type parser struct{} func (p parser) ParseSubscriptionContainerDocument(rawConfig []byte) (*containers.Container, error) { result := &containers.Container{} result.Kind = "Base64URLLine" result.Metadata = make(map[string]string) bodyDecoder := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(rawConfig)) decoded, err := io.ReadAll(bodyDecoder) if err != nil { return nil, newError("failed to decode base64url body base64").Base(err) } scanner := bufio.NewScanner(bytes.NewReader(decoded)) const maxCapacity int = 1024 * 256 buf := make([]byte, maxCapacity) scanner.Buffer(buf, maxCapacity) for scanner.Scan() { result.ServerSpecs = append(result.ServerSpecs, containers.UnparsedServerConf{ KindHint: "URL", Content: scanner.Bytes(), }) } return result, nil } func init() { common.Must(containers.RegisterParser("Base64URLLine", newBase64URLLineParser())) } ================================================ FILE: app/subscription/containers/containers.go ================================================ package containers //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type UnparsedServerConf struct { KindHint string Content []byte } type Container struct { Kind string Metadata map[string]string ServerSpecs []UnparsedServerConf } type SubscriptionContainerDocumentParser interface { ParseSubscriptionContainerDocument(rawConfig []byte) (*Container, error) } var knownParsers = make(map[string]SubscriptionContainerDocumentParser) func RegisterParser(kind string, parser SubscriptionContainerDocumentParser) error { if _, found := knownParsers[kind]; found { return newError("parser already registered for kind ", kind) } knownParsers[kind] = parser return nil } ================================================ FILE: app/subscription/containers/dataurlsingle/dataurl.go ================================================ package dataurlsingle import ( "bytes" "strings" "github.com/vincent-petithory/dataurl" "github.com/v2fly/v2ray-core/v5/app/subscription/containers" "github.com/v2fly/v2ray-core/v5/common" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func newSingularDataURLParser() containers.SubscriptionContainerDocumentParser { return &parser{} } type parser struct{} func (p parser) ParseSubscriptionContainerDocument(rawConfig []byte) (*containers.Container, error) { dataURL, err := dataurl.Decode(bytes.NewReader(rawConfig)) if err != nil { return nil, newError("unable to decode dataURL").Base(err) } if dataURL.MediaType.Type != "application" { return nil, newError("unsupported media type: ", dataURL.MediaType.Type) } if !strings.HasPrefix(dataURL.MediaType.Subtype, "vnd.v2ray.subscription-singular") { return nil, newError("unsupported media subtype: ", dataURL.MediaType.Subtype) } result := &containers.Container{} result.Kind = "DataURLSingle" result.Metadata = make(map[string]string) result.ServerSpecs = append(result.ServerSpecs, containers.UnparsedServerConf{ KindHint: "", Content: dataURL.Data, }) return result, nil } func init() { common.Must(containers.RegisterParser("DataURLSingle", newSingularDataURLParser())) } ================================================ FILE: app/subscription/containers/dataurlsingle/errors.generated.go ================================================ package dataurlsingle import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/containers/errors.generated.go ================================================ package containers import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/containers/jsonfieldarray/errors.generated.go ================================================ package jsonfieldarray import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/containers/jsonfieldarray/jsonfieldarray.go ================================================ package jsonfieldarray //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/subscription/containers/jsonfieldarray/jsonified/errors.generated.go ================================================ package jsonified import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/containers/jsonfieldarray/jsonified/jsonified.go ================================================ package jsonified //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/subscription/containers/jsonfieldarray/jsonified/parser.go ================================================ package jsonified import ( "github.com/v2fly/v2ray-core/v5/app/subscription/containers" "github.com/v2fly/v2ray-core/v5/app/subscription/containers/jsonfieldarray" "github.com/v2fly/v2ray-core/v5/common" jsonConf "github.com/v2fly/v2ray-core/v5/infra/conf/json" ) func newJsonifiedYamlParser() containers.SubscriptionContainerDocumentParser { return &jsonifiedYAMLParser{} } type jsonifiedYAMLParser struct{} func (j jsonifiedYAMLParser) ParseSubscriptionContainerDocument(rawConfig []byte) (*containers.Container, error) { parser := jsonfieldarray.NewJSONFieldArrayParser() jsonified, err := jsonConf.FromYAML(rawConfig) if err != nil { return nil, newError("failed to parse as yaml").Base(err) } container, err := parser.ParseSubscriptionContainerDocument(jsonified) if err != nil { return nil, newError("failed to parse as jsonfieldarray").Base(err) } container.Kind = "Yaml2Json+" + container.Kind for _, value := range container.ServerSpecs { value.KindHint = "Yaml2Json+" + value.KindHint } return container, nil } func init() { common.Must(containers.RegisterParser("Yaml2Json", newJsonifiedYamlParser())) } ================================================ FILE: app/subscription/containers/jsonfieldarray/parser.go ================================================ package jsonfieldarray import ( "encoding/json" "github.com/v2fly/v2ray-core/v5/app/subscription/containers" "github.com/v2fly/v2ray-core/v5/common" ) // NewJSONFieldArrayParser internal api func NewJSONFieldArrayParser() containers.SubscriptionContainerDocumentParser { return newJSONFieldArrayParser() } func newJSONFieldArrayParser() containers.SubscriptionContainerDocumentParser { return &parser{} } type parser struct{} type jsonDocument map[string]json.RawMessage func (p parser) ParseSubscriptionContainerDocument(rawConfig []byte) (*containers.Container, error) { result := &containers.Container{} result.Kind = "JsonFieldArray" result.Metadata = make(map[string]string) var doc jsonDocument if err := json.Unmarshal(rawConfig, &doc); err != nil { return nil, newError("failed to parse as json").Base(err) } for key, value := range doc { switch value[0] { case '[': parsedArray, err := p.parseArray(value, "JsonFieldArray+"+key) if err != nil { return nil, newError("failed to parse as json array").Base(err) } result.ServerSpecs = append(result.ServerSpecs, parsedArray...) case '{': fallthrough default: result.Metadata[key] = string(value) } } return result, nil } func (p parser) parseArray(rawConfig []byte, kindHint string) ([]containers.UnparsedServerConf, error) { var result []json.RawMessage if err := json.Unmarshal(rawConfig, &result); err != nil { return nil, newError("failed to parse as json array").Base(err) } var ret []containers.UnparsedServerConf for _, value := range result { ret = append(ret, containers.UnparsedServerConf{ KindHint: kindHint, Content: []byte(value), }) } return ret, nil } func init() { common.Must(containers.RegisterParser("JsonFieldArray", newJSONFieldArrayParser())) } ================================================ FILE: app/subscription/containers/tryall.go ================================================ package containers func TryAllParsers(rawConfig []byte, prioritizedParser string) (*Container, error) { if prioritizedParser != "" { if parser, found := knownParsers[prioritizedParser]; found { container, err := parser.ParseSubscriptionContainerDocument(rawConfig) if err == nil { return container, nil } } } for _, parser := range knownParsers { container, err := parser.ParseSubscriptionContainerDocument(rawConfig) if err == nil { return container, nil } } return nil, newError("no parser found for config") } ================================================ FILE: app/subscription/containers/urlline/errors.generated.go ================================================ package urlline import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/containers/urlline/parser.go ================================================ package urlline import ( "bufio" "bytes" "net/url" "strings" "github.com/v2fly/v2ray-core/v5/app/subscription/containers" "github.com/v2fly/v2ray-core/v5/common" ) func newURLLineParser() containers.SubscriptionContainerDocumentParser { return &parser{} } type parser struct{} func (p parser) ParseSubscriptionContainerDocument(rawConfig []byte) (*containers.Container, error) { result := &containers.Container{} result.Kind = "URLLine" result.Metadata = make(map[string]string) scanner := bufio.NewScanner(bytes.NewReader(rawConfig)) const maxCapacity int = 1024 * 256 buf := make([]byte, maxCapacity) scanner.Buffer(buf, maxCapacity) parsedLine := 0 failedLine := 0 for scanner.Scan() { content := scanner.Text() content = strings.TrimSpace(content) if strings.HasPrefix(content, "#") { continue } if strings.HasPrefix(content, "//") { continue } _, err := url.Parse(content) if err != nil { failedLine++ continue } else { parsedLine++ } result.ServerSpecs = append(result.ServerSpecs, containers.UnparsedServerConf{ KindHint: "URL", Content: scanner.Bytes(), }) } if failedLine > parsedLine || parsedLine == 0 { return nil, newError("failed to parse as URLLine").Base(newError("too many failed lines")) } return result, nil } func init() { common.Must(containers.RegisterParser("URLLine", newURLLineParser())) } ================================================ FILE: app/subscription/containers/urlline/urlline.go ================================================ package urlline //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/subscription/documentfetcher/dataurlfetcher/dataurl.go ================================================ package dataurlfetcher import ( "context" "strings" "github.com/vincent-petithory/dataurl" "github.com/v2fly/v2ray-core/v5/app/subscription" "github.com/v2fly/v2ray-core/v5/app/subscription/documentfetcher" "github.com/v2fly/v2ray-core/v5/common" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func newDataURLFetcher() *dataURLFetcher { return &dataURLFetcher{} } func init() { common.Must(documentfetcher.RegisterFetcher("dataurl", newDataURLFetcher())) } type dataURLFetcher struct{} func (d *dataURLFetcher) DownloadDocument(ctx context.Context, source *subscription.ImportSource, opts ...documentfetcher.FetcherOptions) ([]byte, error) { dataURL, err := dataurl.DecodeString(source.Url) if err != nil { return nil, newError("unable to decode dataURL").Base(err) } if dataURL.MediaType.Type != "application" { return nil, newError("unsupported media type: ", dataURL.MediaType.Type) } if !strings.HasPrefix(dataURL.MediaType.Subtype, "vnd.v2ray.subscription") { return nil, newError("unsupported media subtype: ", dataURL.MediaType.Subtype) } return []byte(source.Url), nil } ================================================ FILE: app/subscription/documentfetcher/dataurlfetcher/errors.generated.go ================================================ package dataurlfetcher import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/documentfetcher/errors.generated.go ================================================ package documentfetcher import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/documentfetcher/fetcher.go ================================================ package documentfetcher import ( "context" "github.com/v2fly/v2ray-core/v5/app/subscription" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type FetcherOptions interface{} type Fetcher interface { DownloadDocument(ctx context.Context, source *subscription.ImportSource, opts ...FetcherOptions) ([]byte, error) } var knownFetcher = make(map[string]Fetcher) func RegisterFetcher(name string, fetcher Fetcher) error { if _, found := knownFetcher[name]; found { return newError("fetcher ", name, " already registered") } knownFetcher[name] = fetcher return nil } func GetFetcher(name string) (Fetcher, error) { if fetcher, found := knownFetcher[name]; found { return fetcher, nil } return nil, newError("fetcher ", name, " not found") } ================================================ FILE: app/subscription/documentfetcher/httpfetcher/errors.generated.go ================================================ package httpfetcher import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/documentfetcher/httpfetcher/http.go ================================================ package httpfetcher import ( "context" "io" gonet "net" "net/http" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/app/subscription" "github.com/v2fly/v2ray-core/v5/app/subscription/documentfetcher" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func newHTTPFetcher() *httpFetcher { return &httpFetcher{} } func init() { common.Must(documentfetcher.RegisterFetcher("http", newHTTPFetcher())) } type httpFetcher struct{} func (h *httpFetcher) DownloadDocument(ctx context.Context, source *subscription.ImportSource, opts ...documentfetcher.FetcherOptions) ([]byte, error) { instanceNetwork := envctx.EnvironmentFromContext(ctx).(environment.InstanceNetworkCapabilitySet) outboundDialer := instanceNetwork.OutboundDialer() var httpRoundTripper http.RoundTripper //nolint: gosimple httpRoundTripper = &http.Transport{ DialContext: func(ctx_ context.Context, network string, addr string) (gonet.Conn, error) { dest, err := net.ParseDestination(network + ":" + addr) if err != nil { return nil, newError("unable to parse destination") } return outboundDialer(ctx, dest, source.ImportUsingTag) }, } request, err := http.NewRequest("GET", source.Url, nil) if err != nil { return nil, newError("unable to generate request").Base(err) } resp, err := httpRoundTripper.RoundTrip(request) if err != nil { return nil, newError("unable to send request").Base(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, newError("unexpected http status ", resp.StatusCode, "=", resp.Status) } data, err := io.ReadAll(resp.Body) if err != nil { return nil, newError("unable to read response").Base(err) } return data, nil } ================================================ FILE: app/subscription/entries/entries.go ================================================ package entries import "github.com/v2fly/v2ray-core/v5/app/subscription/specs" //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type Converter interface { ConvertToAbstractServerConfig(rawConfig []byte, kindHint string) (*specs.SubscriptionServerConfig, error) } ================================================ FILE: app/subscription/entries/errors.generated.go ================================================ package entries import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/entries/nonnative/converter.go ================================================ package nonnative import ( "io/fs" "github.com/v2fly/v2ray-core/v5/app/subscription/entries" "github.com/v2fly/v2ray-core/v5/app/subscription/entries/nonnative/nonnativeifce" "github.com/v2fly/v2ray-core/v5/app/subscription/entries/outbound" "github.com/v2fly/v2ray-core/v5/app/subscription/specs" "github.com/v2fly/v2ray-core/v5/common" ) type nonNativeConverter struct { matcher *DefMatcher } func (n *nonNativeConverter) ConvertToAbstractServerConfig(rawConfig []byte, kindHint string) (*specs.SubscriptionServerConfig, error) { nonNativeLink := ExtractAllValuesFromBytes(rawConfig) nonNativeLink.Values["_kind"] = kindHint result, err := n.matcher.ExecuteAll(nonNativeLink) if err != nil { return nil, newError("failed to find working converting template").Base(err) } outboundParser := outbound.NewOutboundEntriesParser() outboundEntries, err := outboundParser.ConvertToAbstractServerConfig(result, "") if err != nil { return nil, newError("failed to parse template output as outbound entries").Base(err) } return outboundEntries, nil } func NewNonNativeConverter(fs fs.FS) (entries.Converter, error) { matcher := NewDefMatcher() if fs == nil { err := matcher.LoadEmbeddedDefinitions() if err != nil { return nil, newError("failed to load embedded definitions").Base(err) } } else { err := matcher.LoadDefinitions(fs) if err != nil { return nil, newError("failed to load provided definitions").Base(err) } } return &nonNativeConverter{matcher: matcher}, nil } func init() { common.Must(entries.RegisterConverter("nonnative", common.Must2(NewNonNativeConverter(nil)).(entries.Converter))) nonnativeifce.NewNonNativeConverterConstructor = NewNonNativeConverter } ================================================ FILE: app/subscription/entries/nonnative/definitions/shadowsocks.jsont ================================================ {{if assertExists . "root_!kind" | not}} Unknown environment {{end}} {{if assertIsOneOf . "root_!kind" "json" "link" | not}} This template only works for json input. {{end}} {{ $type := tryGet . "root_!kind"}} {{ $methodName := tryGet . "root_!json_method_!unquoted" "root_!json_protocol_!unquoted" "root_!json_cipher_!unquoted" "root_!link_userinfo_!value_!rawContent"}} {{ if eq $type "link" }} {{ $methodName = splitAndGetNth ":" 0 $methodName}} {{end}} {{if assertValueIsOneOf $methodName "chacha20-ietf-poly1305" "chacha20-poly1305" "aes-128-gcm" "aes-256-gcm" | not}} This template only works for ss. {{end}} {{ $server_address := tryGet . "root_!json_server" "root_!json_address" "root_!json_endpoint" "root_!link_host"}} {{ $server_port := tryGet . "root_!json_port" "root_!json_server_port" "root_!json_endpoint" "root_!link_host"}} {{if $server_address | splitAndGetAfterNth ":" 0 | len | lt 1}} {{ $server_addressport_unquoted := tryGet . "root_!json_endpoint_!unquoted" "root_!link_host"}} {{ $server_port = $server_addressport_unquoted | splitAndGetNth ":" -1}} {{ $server_portWithSep := printf ":%v" $server_port}} {{ $server_address = $server_addressport_unquoted | stringCutSuffix $server_portWithSep | jsonEncode}} {{end}} {{ $name_annotation := tryGet . "root_!json_name_!unquoted" "root_!json_id_!unquoted" "root_!json_tag_!unquoted" "root_!json_remarks_!unquoted" "root_!link_fragment" ""}} {{$password := tryGet . "root_!json_password" "root_!json_psk" "root_!link_userinfo_!value_!rawContent"}} {{ if eq $type "link" }} {{ $password = splitAndGetNth ":" 1 $password | jsonEncode}} {{end}} { "protocol": "shadowsocks", "settings": { "address": {{$server_address}}, "port": {{$server_port}}, "method": {{$methodName | jsonEncode}}, "password": {{$password}} }, "metadata":{ "TagName": {{print $name_annotation "_" $server_address | jsonEncode}}, "DisplayName": {{print $name_annotation | jsonEncode}} } } ================================================ FILE: app/subscription/entries/nonnative/definitions/shadowsocks2022.jsont ================================================ {{if assertExists . "root_!kind" | not}} Unknown environment {{end}} {{if assertIsOneOf . "root_!kind" "json" | not}} This template only works for json input. {{end}} {{ $methodName := tryGet . "root_!json_method_!unquoted" "root_!json_protocol_!unquoted"}} {{if assertValueIsOneOf $methodName "2022-blake3-aes-128-gcm" "2022-blake3-aes-256-gcm" | not}} This template only works for ss2022. {{end}} {{ $server_address := tryGet . "root_!json_server" "root_!json_address" "root_!json_endpoint"}} {{ $server_port := tryGet . "root_!json_port" "root_!json_server_port" "root_!json_endpoint"}} {{if $server_address | splitAndGetAfterNth ":" 0 | len | gt 1}} {{ $server_addressport_unquoted := tryGet . "root_!json_endpoint_!unquoted"}} {{ $server_port = $server_addressport_unquoted | splitAndGetAfterNth ":" -1}} {{ $server_portWithSep := printf ":%v" $server_port}} {{ $server_address = $server_addressport_unquoted | stringCutSuffix $server_portWithSep | jsonEncode}} {{end}} {{ $name_annotation := tryGet . "root_!json_name_!unquoted" "root_!json_id_!unquoted" "root_!json_tag_!unquoted" "root_!json_remarks_!unquoted" ""}} {{ $psk := tryGet . "root_!json_password_!unquoted" "root_!json_psk_!unquoted"}} {{ $ipsk_encoded := "" }} {{if $psk | splitAndGetAfterNth ":" 0 | len | ne 1}} {{ $origpsk := $psk }} {{ $psk = $psk | splitAndGetNth ":" -1 }} {{ $pskWithSep := printf ":%v" $psk}} {{ $ipsk_encoded = $origpsk | stringCutSuffix $pskWithSep | splitAndGetAfterNth ":" 0 | jsonEncode}} {{else}} {{$ipsk_encoded = tryGet . "root_!json_iPSKs" ""}} {{end}} { "protocol": "shadowsocks2022", "settings": { "address": {{$server_address}}, "port": {{$server_port}}, "method": {{$methodName | jsonEncode}}, "psk": {{$psk | jsonEncode}} {{if $ipsk_encoded|len|ne 0}} , "ipsk": {{$ipsk_encoded}} {{end}} }, "metadata":{ "TagName": {{print $name_annotation "_" $server_address | jsonEncode}}, "DisplayName": {{print $name_annotation | jsonEncode}} } } ================================================ FILE: app/subscription/entries/nonnative/definitions/vmess.jsont ================================================ {{if assertExists . "root_!kind" | not}} Unknown environment {{end}} {{ $protocol_name := tryGet . "root_!link_protocol" "root_!json_type_!unquoted"}} {{if assertValueIsOneOf $protocol_name "vmess" | not}} This template will only handle vmess link {{end}} {{ $server_address := tryGet . "root_!link_host_!base64_!json_add" "root_!json_server"}} {{ $server_uuid := tryGet . "root_!link_host_!base64_!json_id" "root_!json_uuid"}} {{ $server_port := tryGet . "root_!link_host_!base64_!json_port_!unquoted" "root_!link_host_!base64_!json_port" "root_!json_port_!unquoted" "root_!json_port"}} {{ $transport_type := tryGet . "root_!link_host_!base64_!json_net_!unquoted" "root_!json_network_!unquoted" ""}} {{ $transport_type = $transport_type | unalias "tcp" ""}} {{ $name_annotation := tryGet . "root_!link_host_!base64_!json_ps_!unquoted" "root_!json_name_!unquoted" ""}} {{if assertValueIsOneOf $transport_type "tcp" "kcp" "ws" "h2" "quic" "grpc"| not }} unknown transport type {{end}} {{$transport_grpc_service_name := ""}} {{ if $transport_type | eq "grpc"}} {{ $transport_grpc_service_name = tryGet . "root_!link_host_!base64_!json_path" ""}} {{end}} {{$transport_ws_path := ""}} {{ if $transport_type | eq "ws"}} {{ $transport_ws_path = tryGet . "root_!link_host_!base64_!json_path" "root_!json_ws-opts_!json_path" ""}} {{end}} {{ $security_type := tryGet . "root_!link_host_!base64_!json_tls_!unquoted" "root_!json_tls" ""}} {{ $security_type = $security_type | unalias "none" "" "false" "0"}} {{if assertValueIsOneOf $security_type "tls" "utls" "none"| not }} unknown security type {{end}} {{ $security_tlsmmon_sni := tryGet . "root_!link_host_!base64_!json_sni" ""}} {{ $security_tlsmmon_sni = $security_tlsmmon_sni | unalias $server_address ""}} { "protocol": "vmess", "settings":{ "address":{{$server_address}}, "port":{{$server_port}}, "uuid":{{$server_uuid}} }, "streamSettings":{ "transport":{{$transport_type|jsonEncode}}, "security":{{$security_type|jsonEncode}}, "transportSettings":{ {{ if $transport_type | eq "grpc"}} "serviceName":{{$transport_grpc_service_name}} {{end}} {{ if $transport_type | eq "ws"}} "path":{{$transport_ws_path}} {{end}} }, "securitySettings":{ {{ if $security_type | eq "tls"}} "serverName":{{$security_tlsmmon_sni}} {{end}} } }, "metadata":{ "TagName": {{print $name_annotation "_" $server_address | jsonEncode}}, "DisplayName": {{print $name_annotation | jsonEncode}} } } ================================================ FILE: app/subscription/entries/nonnative/errors.generated.go ================================================ package nonnative import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/entries/nonnative/matchdef.go ================================================ package nonnative import ( "bytes" "embed" "encoding/json" "io/fs" "strings" "text/template" ) //go:embed definitions/* var embeddedDefinitions embed.FS func NewDefMatcher() *DefMatcher { d := &DefMatcher{} d.init() return d } type DefMatcher struct { templates *template.Template } type ExecutionEnvironment struct { link AbstractNonNativeLink } func (d *DefMatcher) createFuncMap() template.FuncMap { return map[string]any{ "assertExists": func(env *ExecutionEnvironment, names ...string) (bool, error) { link := env.link for _, v := range names { _, ok := link.Values[v] if !ok { return false, newError("failed assertExists of ", v) } } return true, nil }, "assertIsOneOf": func(env *ExecutionEnvironment, name string, values ...string) (bool, error) { link := env.link actualValue, ok := link.Values[name] if !ok { return false, newError("failed assertIs of non-exist ", name) } found := false for _, currentValue := range values { if currentValue == actualValue { found = true break } } if !found { return false, newError("failed assertIsOneOf name = ", actualValue, "is not one of ", values) } return true, nil }, "assertValueIsOneOf": func(value string, values ...string) (bool, error) { actualValue := value found := false for _, currentValue := range values { if currentValue == actualValue { found = true break } } if !found { return false, newError("failed assertIsOneOf name = ", actualValue, "is not one of ", values) } return true, nil }, "tryGet": func(env *ExecutionEnvironment, names ...string) (string, error) { link := env.link for _, currentName := range names { value, ok := link.Values[currentName] if ok { return value, nil } else if currentName == "" { return "", nil } } return "", newError("failed tryGet exists none of ", names) }, "splitAndGetNth": func(sep string, n int, content string) (string, error) { result := strings.Split(content, sep) if n > len(result)-1 { return "", newError("failed splitAndGetNth exists too short content:", content, "n = ", n, "sep =", sep) } if n < 0 { n = len(result) + n if n < 0 { return "", newError("failed splitAndGetNth exists too short content:", content, "n = ", n, "sep =", sep) } } return result[n], nil }, "splitAndGetAfterNth": func(sep string, n int, content string) ([]string, error) { result := strings.Split(content, sep) if n < 0 { n = len(result) + n } if n > len(result)-1 { return []string{}, newError("failed splitAndGetNth exists too short content:", content) } return result[n:], nil }, "splitAndGetBeforeNth": func(sep string, n int, content string) ([]string, error) { result := strings.Split(content, sep) if n < 0 { n = len(result) + n } if n > len(result)-1 { return []string{}, newError("failed splitAndGetNth exists too short content:", content) } return result[:n], nil }, "jsonEncode": func(content any) (string, error) { buf := bytes.NewBuffer(nil) err := json.NewEncoder(buf).Encode(content) if err != nil { return "", newError("unable to jsonQuote ", content).Base(err) } return buf.String(), nil }, "stringCutSuffix": func(suffix, content string) (string, error) { remaining, found := strings.CutSuffix(content, suffix) if !found { return "", newError("suffix not found in content =", suffix, " suffix =", suffix) } return remaining, nil }, "unalias": func(standardName string, names ...string) (string, error) { if len(names) == 0 { return "", newError("no input value specified") } actualInput := names[len(names)-1] alias := names[:len(names)-1] for _, v := range alias { if v == actualInput { return standardName, nil } } return actualInput, nil }, } } func (d *DefMatcher) init() { d.templates = template.New("root").Funcs(d.createFuncMap()) } func (d *DefMatcher) LoadEmbeddedDefinitions() error { return d.LoadDefinitions(embeddedDefinitions) } func (d *DefMatcher) LoadDefinitions(fs fs.FS) error { var err error d.templates, err = d.templates.ParseFS(fs, "definitions/*.jsont") if err != nil { return err } return nil } func (d *DefMatcher) ExecuteNamed(link AbstractNonNativeLink, name string) ([]byte, error) { outputBuffer := bytes.NewBuffer(nil) env := &ExecutionEnvironment{link: link} err := d.templates.ExecuteTemplate(outputBuffer, name, env) if err != nil { return nil, newError("failed to execute template").Base(err) } return outputBuffer.Bytes(), nil } func (d *DefMatcher) ExecuteAll(link AbstractNonNativeLink) ([]byte, error) { outputBuffer := bytes.NewBuffer(nil) for _, loadedTemplates := range d.templates.Templates() { env := &ExecutionEnvironment{link: link} err := loadedTemplates.Execute(outputBuffer, env) if err != nil { outputBuffer.Reset() } else { break } } if outputBuffer.Len() == 0 { return nil, newError("failed to find a working template") } return outputBuffer.Bytes(), nil } ================================================ FILE: app/subscription/entries/nonnative/nonnative.go ================================================ package nonnative import ( "encoding/base64" "encoding/json" "net/url" "regexp" "strings" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func ExtractAllValuesFromBytes(bytes []byte) AbstractNonNativeLink { link := AbstractNonNativeLink{} link.fromBytes(bytes) return link } type jsonDocument map[string]json.RawMessage type AbstractNonNativeLink struct { Values map[string]string } func (a *AbstractNonNativeLink) fromBytes(bytes []byte) { a.Values = make(map[string]string) content := string(bytes) content = strings.Trim(content, " \n\t\r") a.extractValue(content, "root") } func (a *AbstractNonNativeLink) extractValue(content, prefix string) { if content == "" { return } { // check if the content is a link match, err := regexp.Match("[a-zA-Z0-9]+:((\\/\\/)|\\?)", []byte(content)) if err != nil { panic(err) } if match { // if so, parse as link parsedURL, err := url.Parse(content) // if process is successful, then continue to parse every element of the link if err == nil { a.Values[prefix+"_!kind"] = "link" a.extractLink(parsedURL, prefix) return } } } { // check if it is base64 content = strings.Trim(content, "=") decoded, err := base64.RawStdEncoding.DecodeString(content) if err == nil { a.Values[prefix+"_!kind"] = "base64" a.Values[prefix+"_!rawContent"] = string(decoded) a.extractValue(string(decoded), prefix+"_!base64") return } } { // check if it is base64url content = strings.Trim(content, "=") decoded, err := base64.RawURLEncoding.DecodeString(content) if err == nil { a.Values[prefix+"_!kind"] = "base64url" a.Values[prefix+"_!rawContent"] = string(decoded) a.extractValue(string(decoded), prefix+"_!base64") return } } { // check if it is json var doc jsonDocument if err := json.Unmarshal([]byte(content), &doc); err == nil { a.Values[prefix+"_!kind"] = "json" a.extractJSON(&doc, prefix) return } } } func (a *AbstractNonNativeLink) extractLink(content *url.URL, prefix string) { a.Values[prefix+"_!link"] = content.String() a.Values[prefix+"_!link_protocol"] = content.Scheme a.Values[prefix+"_!link_host"] = content.Host a.extractValue(content.Host, prefix+"_!link_host") a.Values[prefix+"_!link_path"] = content.Path a.Values[prefix+"_!link_query"] = content.RawQuery a.Values[prefix+"_!link_fragment"] = content.Fragment a.Values[prefix+"_!link_userinfo"] = content.User.String() a.extractValue(content.User.String(), prefix+"_!link_userinfo_!value") a.Values[prefix+"_!link_opaque"] = content.Opaque } func (a *AbstractNonNativeLink) extractJSON(content *jsonDocument, prefix string) { for key, value := range *content { switch value[0] { case '{': a.extractValue(string(value), prefix+"_!json_"+key) case '"': var unquoted string if err := json.Unmarshal(value, &unquoted); err == nil { a.Values[prefix+"_!json_"+key+"_!unquoted"] = unquoted } fallthrough default: a.Values[prefix+"_!json_"+key] = string(value) } } } ================================================ FILE: app/subscription/entries/nonnative/nonnativeifce/nonnativeifce.go ================================================ package nonnativeifce import ( "io/fs" "github.com/v2fly/v2ray-core/v5/app/subscription/entries" ) type NonNativeConverterConstructorT func(fs fs.FS) (entries.Converter, error) var NewNonNativeConverterConstructor NonNativeConverterConstructorT ================================================ FILE: app/subscription/entries/outbound/errors.generated.go ================================================ package outbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/entries/outbound/outbound.go ================================================ package outbound import ( "github.com/v2fly/v2ray-core/v5/app/subscription/entries" "github.com/v2fly/v2ray-core/v5/app/subscription/specs" "github.com/v2fly/v2ray-core/v5/common" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen // NewOutboundEntriesParser internal api func NewOutboundEntriesParser() entries.Converter { return newOutboundEntriesParser() } func newOutboundEntriesParser() entries.Converter { return &outboundEntriesParser{} } type outboundEntriesParser struct{} func (o *outboundEntriesParser) ConvertToAbstractServerConfig(rawConfig []byte, kindHint string) (*specs.SubscriptionServerConfig, error) { parser := specs.NewOutboundParser() outbound, err := parser.ParseOutboundConfig(rawConfig) if err != nil { return nil, newError("failed to parse outbound config").Base(err).AtWarning() } return parser.ToSubscriptionServerConfig(outbound) } func init() { common.Must(entries.RegisterConverter("outbound", newOutboundEntriesParser())) } ================================================ FILE: app/subscription/entries/register.go ================================================ package entries import "github.com/v2fly/v2ray-core/v5/app/subscription/specs" type ConverterRegistry struct { knownConverters map[string]Converter parent *ConverterRegistry } var globalConverterRegistry = &ConverterRegistry{knownConverters: map[string]Converter{}} func RegisterConverter(kind string, converter Converter) error { return globalConverterRegistry.RegisterConverter(kind, converter) } func GetOverlayConverterRegistry() *ConverterRegistry { return globalConverterRegistry.GetOverlayConverterRegistry() } func (c *ConverterRegistry) RegisterConverter(kind string, converter Converter) error { if _, found := c.knownConverters[kind]; found { return newError("converter already registered for kind ", kind) } c.knownConverters[kind] = converter return nil } func (c *ConverterRegistry) TryAllConverters(rawConfig []byte, prioritizedConverter, kindHint string) (*specs.SubscriptionServerConfig, error) { if prioritizedConverter != "" { if converter, found := c.knownConverters[prioritizedConverter]; found { serverConfig, err := converter.ConvertToAbstractServerConfig(rawConfig, kindHint) if err == nil { return serverConfig, nil } } } for _, converter := range c.knownConverters { serverConfig, err := converter.ConvertToAbstractServerConfig(rawConfig, kindHint) if err == nil { return serverConfig, nil } } if c.parent != nil { if serverConfig, err := c.parent.TryAllConverters(rawConfig, prioritizedConverter, kindHint); err == nil { return serverConfig, nil } } return nil, newError("no converter found for config") } func (c *ConverterRegistry) GetOverlayConverterRegistry() *ConverterRegistry { return &ConverterRegistry{knownConverters: map[string]Converter{}, parent: c} } ================================================ FILE: app/subscription/errors.generated.go ================================================ package subscription import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/specs/abstract_spec.pb.go ================================================ package specs import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ServerConfiguration struct { state protoimpl.MessageState `protogen:"open.v1"` Protocol string `protobuf:"bytes,1,opt,name=protocol,proto3" json:"protocol,omitempty"` ProtocolSettings *anypb.Any `protobuf:"bytes,2,opt,name=protocol_settings,json=protocolSettings,proto3" json:"protocol_settings,omitempty"` Transport string `protobuf:"bytes,3,opt,name=transport,proto3" json:"transport,omitempty"` TransportSettings *anypb.Any `protobuf:"bytes,4,opt,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"` Security string `protobuf:"bytes,5,opt,name=security,proto3" json:"security,omitempty"` SecuritySettings *anypb.Any `protobuf:"bytes,6,opt,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfiguration) Reset() { *x = ServerConfiguration{} mi := &file_app_subscription_specs_abstract_spec_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfiguration) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfiguration) ProtoMessage() {} func (x *ServerConfiguration) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_specs_abstract_spec_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfiguration.ProtoReflect.Descriptor instead. func (*ServerConfiguration) Descriptor() ([]byte, []int) { return file_app_subscription_specs_abstract_spec_proto_rawDescGZIP(), []int{0} } func (x *ServerConfiguration) GetProtocol() string { if x != nil { return x.Protocol } return "" } func (x *ServerConfiguration) GetProtocolSettings() *anypb.Any { if x != nil { return x.ProtocolSettings } return nil } func (x *ServerConfiguration) GetTransport() string { if x != nil { return x.Transport } return "" } func (x *ServerConfiguration) GetTransportSettings() *anypb.Any { if x != nil { return x.TransportSettings } return nil } func (x *ServerConfiguration) GetSecurity() string { if x != nil { return x.Security } return "" } func (x *ServerConfiguration) GetSecuritySettings() *anypb.Any { if x != nil { return x.SecuritySettings } return nil } type SubscriptionServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Metadata map[string]string `protobuf:"bytes,2,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Configuration *ServerConfiguration `protobuf:"bytes,3,opt,name=configuration,proto3" json:"configuration,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SubscriptionServerConfig) Reset() { *x = SubscriptionServerConfig{} mi := &file_app_subscription_specs_abstract_spec_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SubscriptionServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SubscriptionServerConfig) ProtoMessage() {} func (x *SubscriptionServerConfig) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_specs_abstract_spec_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SubscriptionServerConfig.ProtoReflect.Descriptor instead. func (*SubscriptionServerConfig) Descriptor() ([]byte, []int) { return file_app_subscription_specs_abstract_spec_proto_rawDescGZIP(), []int{1} } func (x *SubscriptionServerConfig) GetId() string { if x != nil { return x.Id } return "" } func (x *SubscriptionServerConfig) GetMetadata() map[string]string { if x != nil { return x.Metadata } return nil } func (x *SubscriptionServerConfig) GetConfiguration() *ServerConfiguration { if x != nil { return x.Configuration } return nil } type SubscriptionDocument struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata map[string]string `protobuf:"bytes,2,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Server []*SubscriptionServerConfig `protobuf:"bytes,3,rep,name=server,proto3" json:"server,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SubscriptionDocument) Reset() { *x = SubscriptionDocument{} mi := &file_app_subscription_specs_abstract_spec_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SubscriptionDocument) String() string { return protoimpl.X.MessageStringOf(x) } func (*SubscriptionDocument) ProtoMessage() {} func (x *SubscriptionDocument) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_specs_abstract_spec_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SubscriptionDocument.ProtoReflect.Descriptor instead. func (*SubscriptionDocument) Descriptor() ([]byte, []int) { return file_app_subscription_specs_abstract_spec_proto_rawDescGZIP(), []int{2} } func (x *SubscriptionDocument) GetMetadata() map[string]string { if x != nil { return x.Metadata } return nil } func (x *SubscriptionDocument) GetServer() []*SubscriptionServerConfig { if x != nil { return x.Server } return nil } var File_app_subscription_specs_abstract_spec_proto protoreflect.FileDescriptor const file_app_subscription_specs_abstract_spec_proto_rawDesc = "" + "\n" + "*app/subscription/specs/abstract_spec.proto\x12!v2ray.core.app.subscription.specs\x1a\x19google/protobuf/any.proto\"\xb6\x02\n" + "\x13ServerConfiguration\x12\x1a\n" + "\bprotocol\x18\x01 \x01(\tR\bprotocol\x12A\n" + "\x11protocol_settings\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x10protocolSettings\x12\x1c\n" + "\ttransport\x18\x03 \x01(\tR\ttransport\x12C\n" + "\x12transport_settings\x18\x04 \x01(\v2\x14.google.protobuf.AnyR\x11transportSettings\x12\x1a\n" + "\bsecurity\x18\x05 \x01(\tR\bsecurity\x12A\n" + "\x11security_settings\x18\x06 \x01(\v2\x14.google.protobuf.AnyR\x10securitySettings\"\xac\x02\n" + "\x18SubscriptionServerConfig\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12e\n" + "\bmetadata\x18\x02 \x03(\v2I.v2ray.core.app.subscription.specs.SubscriptionServerConfig.MetadataEntryR\bmetadata\x12\\\n" + "\rconfiguration\x18\x03 \x01(\v26.v2ray.core.app.subscription.specs.ServerConfigurationR\rconfiguration\x1a;\n" + "\rMetadataEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x8b\x02\n" + "\x14SubscriptionDocument\x12a\n" + "\bmetadata\x18\x02 \x03(\v2E.v2ray.core.app.subscription.specs.SubscriptionDocument.MetadataEntryR\bmetadata\x12S\n" + "\x06server\x18\x03 \x03(\v2;.v2ray.core.app.subscription.specs.SubscriptionServerConfigR\x06server\x1a;\n" + "\rMetadataEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x84\x01\n" + "%com.v2ray.core.app.subscription.specsP\x01Z5github.com/v2fly/v2ray-core/v5/app/subscription/specs\xaa\x02!V2Ray.Core.App.Subscription.Specsb\x06proto3" var ( file_app_subscription_specs_abstract_spec_proto_rawDescOnce sync.Once file_app_subscription_specs_abstract_spec_proto_rawDescData []byte ) func file_app_subscription_specs_abstract_spec_proto_rawDescGZIP() []byte { file_app_subscription_specs_abstract_spec_proto_rawDescOnce.Do(func() { file_app_subscription_specs_abstract_spec_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_subscription_specs_abstract_spec_proto_rawDesc), len(file_app_subscription_specs_abstract_spec_proto_rawDesc))) }) return file_app_subscription_specs_abstract_spec_proto_rawDescData } var file_app_subscription_specs_abstract_spec_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_app_subscription_specs_abstract_spec_proto_goTypes = []any{ (*ServerConfiguration)(nil), // 0: v2ray.core.app.subscription.specs.ServerConfiguration (*SubscriptionServerConfig)(nil), // 1: v2ray.core.app.subscription.specs.SubscriptionServerConfig (*SubscriptionDocument)(nil), // 2: v2ray.core.app.subscription.specs.SubscriptionDocument nil, // 3: v2ray.core.app.subscription.specs.SubscriptionServerConfig.MetadataEntry nil, // 4: v2ray.core.app.subscription.specs.SubscriptionDocument.MetadataEntry (*anypb.Any)(nil), // 5: google.protobuf.Any } var file_app_subscription_specs_abstract_spec_proto_depIdxs = []int32{ 5, // 0: v2ray.core.app.subscription.specs.ServerConfiguration.protocol_settings:type_name -> google.protobuf.Any 5, // 1: v2ray.core.app.subscription.specs.ServerConfiguration.transport_settings:type_name -> google.protobuf.Any 5, // 2: v2ray.core.app.subscription.specs.ServerConfiguration.security_settings:type_name -> google.protobuf.Any 3, // 3: v2ray.core.app.subscription.specs.SubscriptionServerConfig.metadata:type_name -> v2ray.core.app.subscription.specs.SubscriptionServerConfig.MetadataEntry 0, // 4: v2ray.core.app.subscription.specs.SubscriptionServerConfig.configuration:type_name -> v2ray.core.app.subscription.specs.ServerConfiguration 4, // 5: v2ray.core.app.subscription.specs.SubscriptionDocument.metadata:type_name -> v2ray.core.app.subscription.specs.SubscriptionDocument.MetadataEntry 1, // 6: v2ray.core.app.subscription.specs.SubscriptionDocument.server:type_name -> v2ray.core.app.subscription.specs.SubscriptionServerConfig 7, // [7:7] is the sub-list for method output_type 7, // [7:7] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name } func init() { file_app_subscription_specs_abstract_spec_proto_init() } func file_app_subscription_specs_abstract_spec_proto_init() { if File_app_subscription_specs_abstract_spec_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_subscription_specs_abstract_spec_proto_rawDesc), len(file_app_subscription_specs_abstract_spec_proto_rawDesc)), NumEnums: 0, NumMessages: 5, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_subscription_specs_abstract_spec_proto_goTypes, DependencyIndexes: file_app_subscription_specs_abstract_spec_proto_depIdxs, MessageInfos: file_app_subscription_specs_abstract_spec_proto_msgTypes, }.Build() File_app_subscription_specs_abstract_spec_proto = out.File file_app_subscription_specs_abstract_spec_proto_goTypes = nil file_app_subscription_specs_abstract_spec_proto_depIdxs = nil } ================================================ FILE: app/subscription/specs/abstract_spec.proto ================================================ syntax = "proto3"; package v2ray.core.app.subscription.specs; option csharp_namespace = "V2Ray.Core.App.Subscription.Specs"; option go_package = "github.com/v2fly/v2ray-core/v5/app/subscription/specs"; option java_package = "com.v2ray.core.app.subscription.specs"; option java_multiple_files = true; import "google/protobuf/any.proto"; message ServerConfiguration{ string protocol = 1; google.protobuf.Any protocol_settings = 2; string transport = 3; google.protobuf.Any transport_settings = 4; string security = 5; google.protobuf.Any security_settings = 6; } message SubscriptionServerConfig{ string id = 1; map metadata = 2; ServerConfiguration configuration = 3; } message SubscriptionDocument { map metadata = 2; repeated SubscriptionServerConfig server = 3; } ================================================ FILE: app/subscription/specs/errors.generated.go ================================================ package specs import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/specs/outbound_parser.go ================================================ package specs import ( "bytes" "context" "encoding/json" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/registry" "github.com/v2fly/v2ray-core/v5/common/serial" ) func NewOutboundParser() *OutboundParser { return &OutboundParser{} } type OutboundParser struct{} func (p *OutboundParser) ParseOutboundConfig(rawConfig []byte) (*OutboundConfig, error) { skeleton := &OutboundConfig{} decoder := json.NewDecoder(bytes.NewReader(rawConfig)) decoder.DisallowUnknownFields() err := decoder.Decode(skeleton) if err != nil { return nil, newError("failed to parse outbound config skeleton").Base(err) } return skeleton, nil } func (p *OutboundParser) toAbstractServerSpec(config *OutboundConfig) (*ServerConfiguration, error) { serverConfig := &ServerConfiguration{} serverConfig.Protocol = config.Protocol { protocolSettings, err := loadHeterogeneousConfigFromRawJSONRestricted("outbound", config.Protocol, config.Settings) if err != nil { return nil, newError("failed to parse protocol settings").Base(err) } serverConfig.ProtocolSettings = serial.ToTypedMessage(protocolSettings) } if config.StreamSetting != nil { if config.StreamSetting.Transport == "" { config.StreamSetting.Transport = "tcp" } if config.StreamSetting.Security == "" { config.StreamSetting.Security = "none" } { serverConfig.Transport = config.StreamSetting.Transport transportSettings, err := loadHeterogeneousConfigFromRawJSONRestricted( "transport", config.StreamSetting.Transport, config.StreamSetting.TransportSettings) if err != nil { return nil, newError("failed to parse transport settings").Base(err) } serverConfig.TransportSettings = serial.ToTypedMessage(transportSettings) } if config.StreamSetting.Security != "none" { securitySettings, err := loadHeterogeneousConfigFromRawJSONRestricted( "security", config.StreamSetting.Security, config.StreamSetting.SecuritySettings) if err != nil { return nil, newError("failed to parse security settings").Base(err) } serverConfig.SecuritySettings = serial.ToTypedMessage(securitySettings) serverConfig.Security = serial.V2Type(serverConfig.SecuritySettings) } } return serverConfig, nil } func (p *OutboundParser) ToSubscriptionServerConfig(config *OutboundConfig) (*SubscriptionServerConfig, error) { serverSpec, err := p.toAbstractServerSpec(config) if err != nil { return nil, newError("unable to parse server specification").Base(err) } return &SubscriptionServerConfig{ Configuration: serverSpec, Metadata: config.Metadata, }, nil } func loadHeterogeneousConfigFromRawJSONRestricted(interfaceType, name string, rawJSON json.RawMessage) (proto.Message, error) { ctx := context.TODO() ctx = registry.CreateRestrictedModeContext(ctx) if len(rawJSON) == 0 { rawJSON = []byte("{}") } return registry.LoadImplementationByAlias(ctx, interfaceType, name, []byte(rawJSON)) } ================================================ FILE: app/subscription/specs/skeleton.go ================================================ package specs import ( "encoding/json" ) type OutboundConfig struct { Protocol string `json:"protocol"` Settings json.RawMessage `json:"settings"` StreamSetting *StreamConfig `json:"streamSettings"` Metadata map[string]string `json:"metadata"` } type StreamConfig struct { Transport string `json:"transport"` TransportSettings json.RawMessage `json:"transportSettings"` Security string `json:"security"` SecuritySettings json.RawMessage `json:"securitySettings"` } ================================================ FILE: app/subscription/specs/specs.go ================================================ package specs //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/subscription/subscription.go ================================================ package subscription import "github.com/v2fly/v2ray-core/v5/features" //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type SubscriptionManager interface { features.Feature AddTrackedSubscriptionFromImportSource(importSource *ImportSource) error RemoveTrackedSubscription(name string) error ListTrackedSubscriptions() []string GetTrackedSubscriptionStatus(name string) (*TrackedSubscriptionStatus, error) UpdateTrackedSubscription(name string) error } func SubscriptionManagerType() interface{} { return (*SubscriptionManager)(nil) } ================================================ FILE: app/subscription/subscription_rpc.pb.go ================================================ package subscription import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type SubscriptionServer struct { state protoimpl.MessageState `protogen:"open.v1"` ServerMetadata map[string]string `protobuf:"bytes,2,rep,name=serverMetadata,proto3" json:"serverMetadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Tag string `protobuf:"bytes,3,opt,name=tag,proto3" json:"tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SubscriptionServer) Reset() { *x = SubscriptionServer{} mi := &file_app_subscription_subscription_rpc_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SubscriptionServer) String() string { return protoimpl.X.MessageStringOf(x) } func (*SubscriptionServer) ProtoMessage() {} func (x *SubscriptionServer) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscription_rpc_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SubscriptionServer.ProtoReflect.Descriptor instead. func (*SubscriptionServer) Descriptor() ([]byte, []int) { return file_app_subscription_subscription_rpc_proto_rawDescGZIP(), []int{0} } func (x *SubscriptionServer) GetServerMetadata() map[string]string { if x != nil { return x.ServerMetadata } return nil } func (x *SubscriptionServer) GetTag() string { if x != nil { return x.Tag } return "" } type TrackedSubscriptionStatus struct { state protoimpl.MessageState `protogen:"open.v1"` Servers map[string]*SubscriptionServer `protobuf:"bytes,1,rep,name=servers,proto3" json:"servers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` DocumentMetadata map[string]string `protobuf:"bytes,2,rep,name=documentMetadata,proto3" json:"documentMetadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` ImportSource *ImportSource `protobuf:"bytes,3,opt,name=importSource,proto3" json:"importSource,omitempty"` AddedByApi bool `protobuf:"varint,4,opt,name=added_by_api,json=addedByApi,proto3" json:"added_by_api,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TrackedSubscriptionStatus) Reset() { *x = TrackedSubscriptionStatus{} mi := &file_app_subscription_subscription_rpc_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TrackedSubscriptionStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*TrackedSubscriptionStatus) ProtoMessage() {} func (x *TrackedSubscriptionStatus) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscription_rpc_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TrackedSubscriptionStatus.ProtoReflect.Descriptor instead. func (*TrackedSubscriptionStatus) Descriptor() ([]byte, []int) { return file_app_subscription_subscription_rpc_proto_rawDescGZIP(), []int{1} } func (x *TrackedSubscriptionStatus) GetServers() map[string]*SubscriptionServer { if x != nil { return x.Servers } return nil } func (x *TrackedSubscriptionStatus) GetDocumentMetadata() map[string]string { if x != nil { return x.DocumentMetadata } return nil } func (x *TrackedSubscriptionStatus) GetImportSource() *ImportSource { if x != nil { return x.ImportSource } return nil } func (x *TrackedSubscriptionStatus) GetAddedByApi() bool { if x != nil { return x.AddedByApi } return false } var File_app_subscription_subscription_rpc_proto protoreflect.FileDescriptor const file_app_subscription_subscription_rpc_proto_rawDesc = "" + "\n" + "'app/subscription/subscription_rpc.proto\x12\x1bv2ray.core.app.subscription\x1a\x1dapp/subscription/config.proto\"\xd6\x01\n" + "\x12SubscriptionServer\x12k\n" + "\x0eserverMetadata\x18\x02 \x03(\v2C.v2ray.core.app.subscription.SubscriptionServer.ServerMetadataEntryR\x0eserverMetadata\x12\x10\n" + "\x03tag\x18\x03 \x01(\tR\x03tag\x1aA\n" + "\x13ServerMetadataEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x97\x04\n" + "\x19TrackedSubscriptionStatus\x12]\n" + "\aservers\x18\x01 \x03(\v2C.v2ray.core.app.subscription.TrackedSubscriptionStatus.ServersEntryR\aservers\x12x\n" + "\x10documentMetadata\x18\x02 \x03(\v2L.v2ray.core.app.subscription.TrackedSubscriptionStatus.DocumentMetadataEntryR\x10documentMetadata\x12M\n" + "\fimportSource\x18\x03 \x01(\v2).v2ray.core.app.subscription.ImportSourceR\fimportSource\x12 \n" + "\fadded_by_api\x18\x04 \x01(\bR\n" + "addedByApi\x1ak\n" + "\fServersEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12E\n" + "\x05value\x18\x02 \x01(\v2/.v2ray.core.app.subscription.SubscriptionServerR\x05value:\x028\x01\x1aC\n" + "\x15DocumentMetadataEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01Br\n" + "\x1fcom.v2ray.core.app.subscriptionP\x01Z/github.com/v2fly/v2ray-core/v5/app/subscription\xaa\x02\x1bV2Ray.Core.App.Subscriptionb\x06proto3" var ( file_app_subscription_subscription_rpc_proto_rawDescOnce sync.Once file_app_subscription_subscription_rpc_proto_rawDescData []byte ) func file_app_subscription_subscription_rpc_proto_rawDescGZIP() []byte { file_app_subscription_subscription_rpc_proto_rawDescOnce.Do(func() { file_app_subscription_subscription_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_subscription_subscription_rpc_proto_rawDesc), len(file_app_subscription_subscription_rpc_proto_rawDesc))) }) return file_app_subscription_subscription_rpc_proto_rawDescData } var file_app_subscription_subscription_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_app_subscription_subscription_rpc_proto_goTypes = []any{ (*SubscriptionServer)(nil), // 0: v2ray.core.app.subscription.SubscriptionServer (*TrackedSubscriptionStatus)(nil), // 1: v2ray.core.app.subscription.TrackedSubscriptionStatus nil, // 2: v2ray.core.app.subscription.SubscriptionServer.ServerMetadataEntry nil, // 3: v2ray.core.app.subscription.TrackedSubscriptionStatus.ServersEntry nil, // 4: v2ray.core.app.subscription.TrackedSubscriptionStatus.DocumentMetadataEntry (*ImportSource)(nil), // 5: v2ray.core.app.subscription.ImportSource } var file_app_subscription_subscription_rpc_proto_depIdxs = []int32{ 2, // 0: v2ray.core.app.subscription.SubscriptionServer.serverMetadata:type_name -> v2ray.core.app.subscription.SubscriptionServer.ServerMetadataEntry 3, // 1: v2ray.core.app.subscription.TrackedSubscriptionStatus.servers:type_name -> v2ray.core.app.subscription.TrackedSubscriptionStatus.ServersEntry 4, // 2: v2ray.core.app.subscription.TrackedSubscriptionStatus.documentMetadata:type_name -> v2ray.core.app.subscription.TrackedSubscriptionStatus.DocumentMetadataEntry 5, // 3: v2ray.core.app.subscription.TrackedSubscriptionStatus.importSource:type_name -> v2ray.core.app.subscription.ImportSource 0, // 4: v2ray.core.app.subscription.TrackedSubscriptionStatus.ServersEntry.value:type_name -> v2ray.core.app.subscription.SubscriptionServer 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_app_subscription_subscription_rpc_proto_init() } func file_app_subscription_subscription_rpc_proto_init() { if File_app_subscription_subscription_rpc_proto != nil { return } file_app_subscription_config_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_subscription_subscription_rpc_proto_rawDesc), len(file_app_subscription_subscription_rpc_proto_rawDesc)), NumEnums: 0, NumMessages: 5, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_subscription_subscription_rpc_proto_goTypes, DependencyIndexes: file_app_subscription_subscription_rpc_proto_depIdxs, MessageInfos: file_app_subscription_subscription_rpc_proto_msgTypes, }.Build() File_app_subscription_subscription_rpc_proto = out.File file_app_subscription_subscription_rpc_proto_goTypes = nil file_app_subscription_subscription_rpc_proto_depIdxs = nil } ================================================ FILE: app/subscription/subscription_rpc.proto ================================================ syntax = "proto3"; package v2ray.core.app.subscription; option csharp_namespace = "V2Ray.Core.App.Subscription"; option go_package = "github.com/v2fly/v2ray-core/v5/app/subscription"; option java_package = "com.v2ray.core.app.subscription"; option java_multiple_files = true; import "app/subscription/config.proto"; message SubscriptionServer { map serverMetadata = 2; string tag = 3; } message TrackedSubscriptionStatus { map servers = 1; map documentMetadata = 2; v2ray.core.app.subscription.ImportSource importSource = 3; bool added_by_api = 4; } ================================================ FILE: app/subscription/subscriptionmanager/command/command.go ================================================ package command import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/subscription" "github.com/v2fly/v2ray-core/v5/common" "google.golang.org/grpc" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type SubscriptionManagerService struct { UnimplementedSubscriptionManagerServiceServer manager subscription.SubscriptionManager } func (s *SubscriptionManagerService) UpdateTrackedSubscription(ctx context.Context, request *UpdateTrackedSubscriptionRequest) (*UpdateTrackedSubscriptionResponse, error) { if s.manager == nil { return nil, newError("subscription manager is not available") } err := s.manager.UpdateTrackedSubscription(request.Name) if err != nil { return nil, err } return &UpdateTrackedSubscriptionResponse{}, nil } func NewSubscriptionManagerService(manager subscription.SubscriptionManager) *SubscriptionManagerService { return &SubscriptionManagerService{manager: manager} } func (s *SubscriptionManagerService) ListTrackedSubscription(ctx context.Context, req *ListTrackedSubscriptionRequest) (*ListTrackedSubscriptionResponse, error) { if s.manager == nil { return nil, newError("subscription manager is not available") } names := s.manager.ListTrackedSubscriptions() return &ListTrackedSubscriptionResponse{Names: names}, nil } func (s *SubscriptionManagerService) AddTrackedSubscription(ctx context.Context, req *AddTrackedSubscriptionRequest) (*AddTrackedSubscriptionResponse, error) { if s.manager == nil { return nil, newError("subscription manager is not available") } err := s.manager.AddTrackedSubscriptionFromImportSource(req.Source) if err != nil { return nil, err } return &AddTrackedSubscriptionResponse{}, nil } func (s *SubscriptionManagerService) RemoveTrackedSubscription(ctx context.Context, req *RemoveTrackedSubscriptionRequest) (*RemoveTrackedSubscriptionResponse, error) { if s.manager == nil { return nil, newError("subscription manager is not available") } err := s.manager.RemoveTrackedSubscription(req.Name) if err != nil { return nil, err } return &RemoveTrackedSubscriptionResponse{}, nil } func (s *SubscriptionManagerService) GetTrackedSubscriptionStatus(ctx context.Context, req *GetTrackedSubscriptionStatusRequest) (*GetTrackedSubscriptionStatusResponse, error) { if s.manager == nil { return nil, newError("subscription manager is not available") } status, err := s.manager.GetTrackedSubscriptionStatus(req.Name) if err != nil { return nil, err } return &GetTrackedSubscriptionStatusResponse{Status: status}, nil } func (s *SubscriptionManagerService) Register(server *grpc.Server) { RegisterSubscriptionManagerServiceServer(server, s) } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { var manager subscription.SubscriptionManager common.Must(core.RequireFeatures(ctx, func(m subscription.SubscriptionManager) { manager = m })) service := NewSubscriptionManagerService(manager) return service, nil })) } ================================================ FILE: app/subscription/subscriptionmanager/command/command.pb.go ================================================ package command import ( subscription "github.com/v2fly/v2ray-core/v5/app/subscription" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ListTrackedSubscriptionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListTrackedSubscriptionRequest) Reset() { *x = ListTrackedSubscriptionRequest{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListTrackedSubscriptionRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListTrackedSubscriptionRequest) ProtoMessage() {} func (x *ListTrackedSubscriptionRequest) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListTrackedSubscriptionRequest.ProtoReflect.Descriptor instead. func (*ListTrackedSubscriptionRequest) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{0} } type ListTrackedSubscriptionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListTrackedSubscriptionResponse) Reset() { *x = ListTrackedSubscriptionResponse{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListTrackedSubscriptionResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListTrackedSubscriptionResponse) ProtoMessage() {} func (x *ListTrackedSubscriptionResponse) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListTrackedSubscriptionResponse.ProtoReflect.Descriptor instead. func (*ListTrackedSubscriptionResponse) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{1} } func (x *ListTrackedSubscriptionResponse) GetNames() []string { if x != nil { return x.Names } return nil } type AddTrackedSubscriptionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Source *subscription.ImportSource `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddTrackedSubscriptionRequest) Reset() { *x = AddTrackedSubscriptionRequest{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddTrackedSubscriptionRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddTrackedSubscriptionRequest) ProtoMessage() {} func (x *AddTrackedSubscriptionRequest) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddTrackedSubscriptionRequest.ProtoReflect.Descriptor instead. func (*AddTrackedSubscriptionRequest) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{2} } func (x *AddTrackedSubscriptionRequest) GetSource() *subscription.ImportSource { if x != nil { return x.Source } return nil } type AddTrackedSubscriptionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddTrackedSubscriptionResponse) Reset() { *x = AddTrackedSubscriptionResponse{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddTrackedSubscriptionResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddTrackedSubscriptionResponse) ProtoMessage() {} func (x *AddTrackedSubscriptionResponse) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddTrackedSubscriptionResponse.ProtoReflect.Descriptor instead. func (*AddTrackedSubscriptionResponse) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{3} } type RemoveTrackedSubscriptionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RemoveTrackedSubscriptionRequest) Reset() { *x = RemoveTrackedSubscriptionRequest{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RemoveTrackedSubscriptionRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RemoveTrackedSubscriptionRequest) ProtoMessage() {} func (x *RemoveTrackedSubscriptionRequest) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RemoveTrackedSubscriptionRequest.ProtoReflect.Descriptor instead. func (*RemoveTrackedSubscriptionRequest) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{4} } func (x *RemoveTrackedSubscriptionRequest) GetName() string { if x != nil { return x.Name } return "" } type RemoveTrackedSubscriptionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RemoveTrackedSubscriptionResponse) Reset() { *x = RemoveTrackedSubscriptionResponse{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RemoveTrackedSubscriptionResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*RemoveTrackedSubscriptionResponse) ProtoMessage() {} func (x *RemoveTrackedSubscriptionResponse) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RemoveTrackedSubscriptionResponse.ProtoReflect.Descriptor instead. func (*RemoveTrackedSubscriptionResponse) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{5} } type UpdateTrackedSubscriptionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateTrackedSubscriptionRequest) Reset() { *x = UpdateTrackedSubscriptionRequest{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UpdateTrackedSubscriptionRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpdateTrackedSubscriptionRequest) ProtoMessage() {} func (x *UpdateTrackedSubscriptionRequest) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpdateTrackedSubscriptionRequest.ProtoReflect.Descriptor instead. func (*UpdateTrackedSubscriptionRequest) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{6} } func (x *UpdateTrackedSubscriptionRequest) GetName() string { if x != nil { return x.Name } return "" } type UpdateTrackedSubscriptionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateTrackedSubscriptionResponse) Reset() { *x = UpdateTrackedSubscriptionResponse{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UpdateTrackedSubscriptionResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpdateTrackedSubscriptionResponse) ProtoMessage() {} func (x *UpdateTrackedSubscriptionResponse) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpdateTrackedSubscriptionResponse.ProtoReflect.Descriptor instead. func (*UpdateTrackedSubscriptionResponse) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{7} } type GetTrackedSubscriptionStatusRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetTrackedSubscriptionStatusRequest) Reset() { *x = GetTrackedSubscriptionStatusRequest{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetTrackedSubscriptionStatusRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetTrackedSubscriptionStatusRequest) ProtoMessage() {} func (x *GetTrackedSubscriptionStatusRequest) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetTrackedSubscriptionStatusRequest.ProtoReflect.Descriptor instead. func (*GetTrackedSubscriptionStatusRequest) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{8} } func (x *GetTrackedSubscriptionStatusRequest) GetName() string { if x != nil { return x.Name } return "" } type GetTrackedSubscriptionStatusResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Status *subscription.TrackedSubscriptionStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetTrackedSubscriptionStatusResponse) Reset() { *x = GetTrackedSubscriptionStatusResponse{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetTrackedSubscriptionStatusResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetTrackedSubscriptionStatusResponse) ProtoMessage() {} func (x *GetTrackedSubscriptionStatusResponse) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetTrackedSubscriptionStatusResponse.ProtoReflect.Descriptor instead. func (*GetTrackedSubscriptionStatusResponse) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{9} } func (x *GetTrackedSubscriptionStatusResponse) GetStatus() *subscription.TrackedSubscriptionStatus { if x != nil { return x.Status } return nil } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_subscription_subscriptionmanager_command_command_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP(), []int{10} } var File_app_subscription_subscriptionmanager_command_command_proto protoreflect.FileDescriptor const file_app_subscription_subscriptionmanager_command_command_proto_rawDesc = "" + "\n" + ":app/subscription/subscriptionmanager/command/command.proto\x127v2ray.core.app.subscription.subscriptionmanager.command\x1a common/protoext/extensions.proto\x1a\x1dapp/subscription/config.proto\x1a'app/subscription/subscription_rpc.proto\" \n" + "\x1eListTrackedSubscriptionRequest\"7\n" + "\x1fListTrackedSubscriptionResponse\x12\x14\n" + "\x05names\x18\x01 \x03(\tR\x05names\"b\n" + "\x1dAddTrackedSubscriptionRequest\x12A\n" + "\x06source\x18\x01 \x01(\v2).v2ray.core.app.subscription.ImportSourceR\x06source\" \n" + "\x1eAddTrackedSubscriptionResponse\"6\n" + " RemoveTrackedSubscriptionRequest\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\"#\n" + "!RemoveTrackedSubscriptionResponse\"6\n" + " UpdateTrackedSubscriptionRequest\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\"#\n" + "!UpdateTrackedSubscriptionResponse\"9\n" + "#GetTrackedSubscriptionStatusRequest\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\"v\n" + "$GetTrackedSubscriptionStatusResponse\x12N\n" + "\x06status\x18\x01 \x01(\v26.v2ray.core.app.subscription.TrackedSubscriptionStatusR\x06status\"0\n" + "\x06Config:&\x82\xb5\x18\"\n" + "\vgrpcservice\x12\x13subscriptionmanager2\xc9\b\n" + "\x1aSubscriptionManagerService\x12\xce\x01\n" + "\x17ListTrackedSubscription\x12W.v2ray.core.app.subscription.subscriptionmanager.command.ListTrackedSubscriptionRequest\x1aX.v2ray.core.app.subscription.subscriptionmanager.command.ListTrackedSubscriptionResponse\"\x00\x12\xcb\x01\n" + "\x16AddTrackedSubscription\x12V.v2ray.core.app.subscription.subscriptionmanager.command.AddTrackedSubscriptionRequest\x1aW.v2ray.core.app.subscription.subscriptionmanager.command.AddTrackedSubscriptionResponse\"\x00\x12\xd4\x01\n" + "\x19RemoveTrackedSubscription\x12Y.v2ray.core.app.subscription.subscriptionmanager.command.RemoveTrackedSubscriptionRequest\x1aZ.v2ray.core.app.subscription.subscriptionmanager.command.RemoveTrackedSubscriptionResponse\"\x00\x12\xdd\x01\n" + "\x1cGetTrackedSubscriptionStatus\x12\\.v2ray.core.app.subscription.subscriptionmanager.command.GetTrackedSubscriptionStatusRequest\x1a].v2ray.core.app.subscription.subscriptionmanager.command.GetTrackedSubscriptionStatusResponse\"\x00\x12\xd4\x01\n" + "\x19UpdateTrackedSubscription\x12Y.v2ray.core.app.subscription.subscriptionmanager.command.UpdateTrackedSubscriptionRequest\x1aZ.v2ray.core.app.subscription.subscriptionmanager.command.UpdateTrackedSubscriptionResponse\"\x00B\xc2\x01\n" + "7com.v2ray.core.subscription.subscriptionmanager.commandP\x01ZKgithub.com/v2fly/v2ray-core/v5/app/subscription/subscriptionmanager/command\xaa\x027V2Ray.Core.App.Subscription.Subscriptionmanager.Commandb\x06proto3" var ( file_app_subscription_subscriptionmanager_command_command_proto_rawDescOnce sync.Once file_app_subscription_subscriptionmanager_command_command_proto_rawDescData []byte ) func file_app_subscription_subscriptionmanager_command_command_proto_rawDescGZIP() []byte { file_app_subscription_subscriptionmanager_command_command_proto_rawDescOnce.Do(func() { file_app_subscription_subscriptionmanager_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_subscription_subscriptionmanager_command_command_proto_rawDesc), len(file_app_subscription_subscriptionmanager_command_command_proto_rawDesc))) }) return file_app_subscription_subscriptionmanager_command_command_proto_rawDescData } var file_app_subscription_subscriptionmanager_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_app_subscription_subscriptionmanager_command_command_proto_goTypes = []any{ (*ListTrackedSubscriptionRequest)(nil), // 0: v2ray.core.app.subscription.subscriptionmanager.command.ListTrackedSubscriptionRequest (*ListTrackedSubscriptionResponse)(nil), // 1: v2ray.core.app.subscription.subscriptionmanager.command.ListTrackedSubscriptionResponse (*AddTrackedSubscriptionRequest)(nil), // 2: v2ray.core.app.subscription.subscriptionmanager.command.AddTrackedSubscriptionRequest (*AddTrackedSubscriptionResponse)(nil), // 3: v2ray.core.app.subscription.subscriptionmanager.command.AddTrackedSubscriptionResponse (*RemoveTrackedSubscriptionRequest)(nil), // 4: v2ray.core.app.subscription.subscriptionmanager.command.RemoveTrackedSubscriptionRequest (*RemoveTrackedSubscriptionResponse)(nil), // 5: v2ray.core.app.subscription.subscriptionmanager.command.RemoveTrackedSubscriptionResponse (*UpdateTrackedSubscriptionRequest)(nil), // 6: v2ray.core.app.subscription.subscriptionmanager.command.UpdateTrackedSubscriptionRequest (*UpdateTrackedSubscriptionResponse)(nil), // 7: v2ray.core.app.subscription.subscriptionmanager.command.UpdateTrackedSubscriptionResponse (*GetTrackedSubscriptionStatusRequest)(nil), // 8: v2ray.core.app.subscription.subscriptionmanager.command.GetTrackedSubscriptionStatusRequest (*GetTrackedSubscriptionStatusResponse)(nil), // 9: v2ray.core.app.subscription.subscriptionmanager.command.GetTrackedSubscriptionStatusResponse (*Config)(nil), // 10: v2ray.core.app.subscription.subscriptionmanager.command.Config (*subscription.ImportSource)(nil), // 11: v2ray.core.app.subscription.ImportSource (*subscription.TrackedSubscriptionStatus)(nil), // 12: v2ray.core.app.subscription.TrackedSubscriptionStatus } var file_app_subscription_subscriptionmanager_command_command_proto_depIdxs = []int32{ 11, // 0: v2ray.core.app.subscription.subscriptionmanager.command.AddTrackedSubscriptionRequest.source:type_name -> v2ray.core.app.subscription.ImportSource 12, // 1: v2ray.core.app.subscription.subscriptionmanager.command.GetTrackedSubscriptionStatusResponse.status:type_name -> v2ray.core.app.subscription.TrackedSubscriptionStatus 0, // 2: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.ListTrackedSubscription:input_type -> v2ray.core.app.subscription.subscriptionmanager.command.ListTrackedSubscriptionRequest 2, // 3: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.AddTrackedSubscription:input_type -> v2ray.core.app.subscription.subscriptionmanager.command.AddTrackedSubscriptionRequest 4, // 4: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.RemoveTrackedSubscription:input_type -> v2ray.core.app.subscription.subscriptionmanager.command.RemoveTrackedSubscriptionRequest 8, // 5: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.GetTrackedSubscriptionStatus:input_type -> v2ray.core.app.subscription.subscriptionmanager.command.GetTrackedSubscriptionStatusRequest 6, // 6: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.UpdateTrackedSubscription:input_type -> v2ray.core.app.subscription.subscriptionmanager.command.UpdateTrackedSubscriptionRequest 1, // 7: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.ListTrackedSubscription:output_type -> v2ray.core.app.subscription.subscriptionmanager.command.ListTrackedSubscriptionResponse 3, // 8: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.AddTrackedSubscription:output_type -> v2ray.core.app.subscription.subscriptionmanager.command.AddTrackedSubscriptionResponse 5, // 9: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.RemoveTrackedSubscription:output_type -> v2ray.core.app.subscription.subscriptionmanager.command.RemoveTrackedSubscriptionResponse 9, // 10: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.GetTrackedSubscriptionStatus:output_type -> v2ray.core.app.subscription.subscriptionmanager.command.GetTrackedSubscriptionStatusResponse 7, // 11: v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService.UpdateTrackedSubscription:output_type -> v2ray.core.app.subscription.subscriptionmanager.command.UpdateTrackedSubscriptionResponse 7, // [7:12] is the sub-list for method output_type 2, // [2:7] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_app_subscription_subscriptionmanager_command_command_proto_init() } func file_app_subscription_subscriptionmanager_command_command_proto_init() { if File_app_subscription_subscriptionmanager_command_command_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_subscription_subscriptionmanager_command_command_proto_rawDesc), len(file_app_subscription_subscriptionmanager_command_command_proto_rawDesc)), NumEnums: 0, NumMessages: 11, NumExtensions: 0, NumServices: 1, }, GoTypes: file_app_subscription_subscriptionmanager_command_command_proto_goTypes, DependencyIndexes: file_app_subscription_subscriptionmanager_command_command_proto_depIdxs, MessageInfos: file_app_subscription_subscriptionmanager_command_command_proto_msgTypes, }.Build() File_app_subscription_subscriptionmanager_command_command_proto = out.File file_app_subscription_subscriptionmanager_command_command_proto_goTypes = nil file_app_subscription_subscriptionmanager_command_command_proto_depIdxs = nil } ================================================ FILE: app/subscription/subscriptionmanager/command/command.proto ================================================ syntax = "proto3"; package v2ray.core.app.subscription.subscriptionmanager.command; option csharp_namespace = "V2Ray.Core.App.Subscription.Subscriptionmanager.Command"; option go_package = "github.com/v2fly/v2ray-core/v5/app/subscription/subscriptionmanager/command"; option java_package = "com.v2ray.core.subscription.subscriptionmanager.command"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "app/subscription/config.proto"; import "app/subscription/subscription_rpc.proto"; message ListTrackedSubscriptionRequest { } message ListTrackedSubscriptionResponse { repeated string names = 1; } message AddTrackedSubscriptionRequest{ v2ray.core.app.subscription.ImportSource source = 1; } message AddTrackedSubscriptionResponse{ } message RemoveTrackedSubscriptionRequest{ string name = 1; } message RemoveTrackedSubscriptionResponse{ } message UpdateTrackedSubscriptionRequest{ string name = 1; } message UpdateTrackedSubscriptionResponse{ } message GetTrackedSubscriptionStatusRequest { string name = 1; } message GetTrackedSubscriptionStatusResponse { v2ray.core.app.subscription.TrackedSubscriptionStatus status = 1; } service SubscriptionManagerService { rpc ListTrackedSubscription(ListTrackedSubscriptionRequest) returns (ListTrackedSubscriptionResponse) {} rpc AddTrackedSubscription(AddTrackedSubscriptionRequest) returns (AddTrackedSubscriptionResponse) {} rpc RemoveTrackedSubscription(RemoveTrackedSubscriptionRequest) returns (RemoveTrackedSubscriptionResponse) {} rpc GetTrackedSubscriptionStatus(GetTrackedSubscriptionStatusRequest) returns (GetTrackedSubscriptionStatusResponse) {} rpc UpdateTrackedSubscription(UpdateTrackedSubscriptionRequest) returns (UpdateTrackedSubscriptionResponse) {} } message Config { option (v2ray.core.common.protoext.message_opt).type = "grpcservice"; option (v2ray.core.common.protoext.message_opt).short_name = "subscriptionmanager"; } ================================================ FILE: app/subscription/subscriptionmanager/command/command_grpc.pb.go ================================================ package command import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( SubscriptionManagerService_ListTrackedSubscription_FullMethodName = "/v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService/ListTrackedSubscription" SubscriptionManagerService_AddTrackedSubscription_FullMethodName = "/v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService/AddTrackedSubscription" SubscriptionManagerService_RemoveTrackedSubscription_FullMethodName = "/v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService/RemoveTrackedSubscription" SubscriptionManagerService_GetTrackedSubscriptionStatus_FullMethodName = "/v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService/GetTrackedSubscriptionStatus" SubscriptionManagerService_UpdateTrackedSubscription_FullMethodName = "/v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService/UpdateTrackedSubscription" ) // SubscriptionManagerServiceClient is the client API for SubscriptionManagerService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type SubscriptionManagerServiceClient interface { ListTrackedSubscription(ctx context.Context, in *ListTrackedSubscriptionRequest, opts ...grpc.CallOption) (*ListTrackedSubscriptionResponse, error) AddTrackedSubscription(ctx context.Context, in *AddTrackedSubscriptionRequest, opts ...grpc.CallOption) (*AddTrackedSubscriptionResponse, error) RemoveTrackedSubscription(ctx context.Context, in *RemoveTrackedSubscriptionRequest, opts ...grpc.CallOption) (*RemoveTrackedSubscriptionResponse, error) GetTrackedSubscriptionStatus(ctx context.Context, in *GetTrackedSubscriptionStatusRequest, opts ...grpc.CallOption) (*GetTrackedSubscriptionStatusResponse, error) UpdateTrackedSubscription(ctx context.Context, in *UpdateTrackedSubscriptionRequest, opts ...grpc.CallOption) (*UpdateTrackedSubscriptionResponse, error) } type subscriptionManagerServiceClient struct { cc grpc.ClientConnInterface } func NewSubscriptionManagerServiceClient(cc grpc.ClientConnInterface) SubscriptionManagerServiceClient { return &subscriptionManagerServiceClient{cc} } func (c *subscriptionManagerServiceClient) ListTrackedSubscription(ctx context.Context, in *ListTrackedSubscriptionRequest, opts ...grpc.CallOption) (*ListTrackedSubscriptionResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListTrackedSubscriptionResponse) err := c.cc.Invoke(ctx, SubscriptionManagerService_ListTrackedSubscription_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *subscriptionManagerServiceClient) AddTrackedSubscription(ctx context.Context, in *AddTrackedSubscriptionRequest, opts ...grpc.CallOption) (*AddTrackedSubscriptionResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AddTrackedSubscriptionResponse) err := c.cc.Invoke(ctx, SubscriptionManagerService_AddTrackedSubscription_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *subscriptionManagerServiceClient) RemoveTrackedSubscription(ctx context.Context, in *RemoveTrackedSubscriptionRequest, opts ...grpc.CallOption) (*RemoveTrackedSubscriptionResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RemoveTrackedSubscriptionResponse) err := c.cc.Invoke(ctx, SubscriptionManagerService_RemoveTrackedSubscription_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *subscriptionManagerServiceClient) GetTrackedSubscriptionStatus(ctx context.Context, in *GetTrackedSubscriptionStatusRequest, opts ...grpc.CallOption) (*GetTrackedSubscriptionStatusResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetTrackedSubscriptionStatusResponse) err := c.cc.Invoke(ctx, SubscriptionManagerService_GetTrackedSubscriptionStatus_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *subscriptionManagerServiceClient) UpdateTrackedSubscription(ctx context.Context, in *UpdateTrackedSubscriptionRequest, opts ...grpc.CallOption) (*UpdateTrackedSubscriptionResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpdateTrackedSubscriptionResponse) err := c.cc.Invoke(ctx, SubscriptionManagerService_UpdateTrackedSubscription_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // SubscriptionManagerServiceServer is the server API for SubscriptionManagerService service. // All implementations must embed UnimplementedSubscriptionManagerServiceServer // for forward compatibility. type SubscriptionManagerServiceServer interface { ListTrackedSubscription(context.Context, *ListTrackedSubscriptionRequest) (*ListTrackedSubscriptionResponse, error) AddTrackedSubscription(context.Context, *AddTrackedSubscriptionRequest) (*AddTrackedSubscriptionResponse, error) RemoveTrackedSubscription(context.Context, *RemoveTrackedSubscriptionRequest) (*RemoveTrackedSubscriptionResponse, error) GetTrackedSubscriptionStatus(context.Context, *GetTrackedSubscriptionStatusRequest) (*GetTrackedSubscriptionStatusResponse, error) UpdateTrackedSubscription(context.Context, *UpdateTrackedSubscriptionRequest) (*UpdateTrackedSubscriptionResponse, error) mustEmbedUnimplementedSubscriptionManagerServiceServer() } // UnimplementedSubscriptionManagerServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedSubscriptionManagerServiceServer struct{} func (UnimplementedSubscriptionManagerServiceServer) ListTrackedSubscription(context.Context, *ListTrackedSubscriptionRequest) (*ListTrackedSubscriptionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListTrackedSubscription not implemented") } func (UnimplementedSubscriptionManagerServiceServer) AddTrackedSubscription(context.Context, *AddTrackedSubscriptionRequest) (*AddTrackedSubscriptionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddTrackedSubscription not implemented") } func (UnimplementedSubscriptionManagerServiceServer) RemoveTrackedSubscription(context.Context, *RemoveTrackedSubscriptionRequest) (*RemoveTrackedSubscriptionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveTrackedSubscription not implemented") } func (UnimplementedSubscriptionManagerServiceServer) GetTrackedSubscriptionStatus(context.Context, *GetTrackedSubscriptionStatusRequest) (*GetTrackedSubscriptionStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTrackedSubscriptionStatus not implemented") } func (UnimplementedSubscriptionManagerServiceServer) UpdateTrackedSubscription(context.Context, *UpdateTrackedSubscriptionRequest) (*UpdateTrackedSubscriptionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateTrackedSubscription not implemented") } func (UnimplementedSubscriptionManagerServiceServer) mustEmbedUnimplementedSubscriptionManagerServiceServer() { } func (UnimplementedSubscriptionManagerServiceServer) testEmbeddedByValue() {} // UnsafeSubscriptionManagerServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to SubscriptionManagerServiceServer will // result in compilation errors. type UnsafeSubscriptionManagerServiceServer interface { mustEmbedUnimplementedSubscriptionManagerServiceServer() } func RegisterSubscriptionManagerServiceServer(s grpc.ServiceRegistrar, srv SubscriptionManagerServiceServer) { // If the following call pancis, it indicates UnimplementedSubscriptionManagerServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&SubscriptionManagerService_ServiceDesc, srv) } func _SubscriptionManagerService_ListTrackedSubscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListTrackedSubscriptionRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(SubscriptionManagerServiceServer).ListTrackedSubscription(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: SubscriptionManagerService_ListTrackedSubscription_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SubscriptionManagerServiceServer).ListTrackedSubscription(ctx, req.(*ListTrackedSubscriptionRequest)) } return interceptor(ctx, in, info, handler) } func _SubscriptionManagerService_AddTrackedSubscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AddTrackedSubscriptionRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(SubscriptionManagerServiceServer).AddTrackedSubscription(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: SubscriptionManagerService_AddTrackedSubscription_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SubscriptionManagerServiceServer).AddTrackedSubscription(ctx, req.(*AddTrackedSubscriptionRequest)) } return interceptor(ctx, in, info, handler) } func _SubscriptionManagerService_RemoveTrackedSubscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RemoveTrackedSubscriptionRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(SubscriptionManagerServiceServer).RemoveTrackedSubscription(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: SubscriptionManagerService_RemoveTrackedSubscription_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SubscriptionManagerServiceServer).RemoveTrackedSubscription(ctx, req.(*RemoveTrackedSubscriptionRequest)) } return interceptor(ctx, in, info, handler) } func _SubscriptionManagerService_GetTrackedSubscriptionStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetTrackedSubscriptionStatusRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(SubscriptionManagerServiceServer).GetTrackedSubscriptionStatus(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: SubscriptionManagerService_GetTrackedSubscriptionStatus_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SubscriptionManagerServiceServer).GetTrackedSubscriptionStatus(ctx, req.(*GetTrackedSubscriptionStatusRequest)) } return interceptor(ctx, in, info, handler) } func _SubscriptionManagerService_UpdateTrackedSubscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UpdateTrackedSubscriptionRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(SubscriptionManagerServiceServer).UpdateTrackedSubscription(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: SubscriptionManagerService_UpdateTrackedSubscription_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SubscriptionManagerServiceServer).UpdateTrackedSubscription(ctx, req.(*UpdateTrackedSubscriptionRequest)) } return interceptor(ctx, in, info, handler) } // SubscriptionManagerService_ServiceDesc is the grpc.ServiceDesc for SubscriptionManagerService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var SubscriptionManagerService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "v2ray.core.app.subscription.subscriptionmanager.command.SubscriptionManagerService", HandlerType: (*SubscriptionManagerServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "ListTrackedSubscription", Handler: _SubscriptionManagerService_ListTrackedSubscription_Handler, }, { MethodName: "AddTrackedSubscription", Handler: _SubscriptionManagerService_AddTrackedSubscription_Handler, }, { MethodName: "RemoveTrackedSubscription", Handler: _SubscriptionManagerService_RemoveTrackedSubscription_Handler, }, { MethodName: "GetTrackedSubscriptionStatus", Handler: _SubscriptionManagerService_GetTrackedSubscriptionStatus_Handler, }, { MethodName: "UpdateTrackedSubscription", Handler: _SubscriptionManagerService_UpdateTrackedSubscription_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "app/subscription/subscriptionmanager/command/command.proto", } ================================================ FILE: app/subscription/subscriptionmanager/command/errors.generated.go ================================================ package command import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/subscriptionmanager/delta.go ================================================ package subscriptionmanager type changedDocument struct { removed []string added []string modified []string unchanged []string } ================================================ FILE: app/subscription/subscriptionmanager/errors.generated.go ================================================ package subscriptionmanager import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/subscription/subscriptionmanager/known_metadata.go ================================================ package subscriptionmanager const ( ServerMetadataID = "ID" ServerMetadataTagName = "TagName" ServerMetadataFullyQualifiedName = "FullyQualifiedName" ) ================================================ FILE: app/subscription/subscriptionmanager/manager.go ================================================ package subscriptionmanager import ( "archive/zip" "bytes" "context" "sync" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/persistentstorage" "github.com/v2fly/v2ray-core/v5/app/persistentstorage/protostorage" "github.com/v2fly/v2ray-core/v5/app/subscription" "github.com/v2fly/v2ray-core/v5/app/subscription/entries" "github.com/v2fly/v2ray-core/v5/app/subscription/entries/nonnative/nonnativeifce" "github.com/v2fly/v2ray-core/v5/app/subscription/specs" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/task" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type SubscriptionManagerImpl struct { sync.Mutex config *subscription.Config ctx context.Context s *core.Instance converter *entries.ConverterRegistry trackedSubscriptions map[string]*trackedSubscription refreshTask *task.Periodic persistentStorage persistentstorage.ScopedPersistentStorage persistImportStorage persistentstorage.ScopedPersistentStorage persistImportSourceProtoStorage protostorage.ProtoPersistentStorage } func (s *SubscriptionManagerImpl) Type() interface{} { return subscription.SubscriptionManagerType() } func (s *SubscriptionManagerImpl) housekeeping() error { for subscriptionName := range s.trackedSubscriptions { s.Lock() if err := s.checkupSubscription(subscriptionName); err != nil { newError("failed to checkup subscription: ", err).AtWarning().WriteToLog() } s.Unlock() } return nil } func (s *SubscriptionManagerImpl) Start() error { if s.config.Persistence { appEnvironment := envctx.EnvironmentFromContext(s.ctx).(environment.AppEnvironment) s.persistentStorage = appEnvironment.PersistentStorage() importsStorage, err := s.persistentStorage.NarrowScope(s.ctx, []byte("imports")) if err != nil { return newError("failed to get persistent storage for imports").Base(err) } s.persistImportStorage = importsStorage s.persistImportSourceProtoStorage = importsStorage.(protostorage.ProtoPersistentStorage) if err = s.loadAllFromPersistentStorage(); err != nil { newError("failed to load all from persistent storage: ", err).WriteToLog() } } go func() { if err := s.refreshTask.Start(); err != nil { return } }() return nil } func (s *SubscriptionManagerImpl) Close() error { if err := s.refreshTask.Close(); err != nil { return err } return nil } func (s *SubscriptionManagerImpl) addTrackedSubscriptionFromImportSource(importSource *subscription.ImportSource, addedByAPI bool, ) error { if s.config.Persistence && addedByAPI { err := s.persistImportSourceProtoStorage.PutProto(s.ctx, importSource.Name, importSource) if err != nil { return newError("failed to persist import source: ", err) } } tracked, err := newTrackedSubscription(importSource) if err != nil { return newError("failed to init subscription ", importSource.Name, ": ", err) } tracked.addedByAPI = addedByAPI s.trackedSubscriptions[importSource.Name] = tracked return nil } func (s *SubscriptionManagerImpl) removeTrackedSubscription(subscriptionName string) error { if s.config.Persistence { err := s.persistImportStorage.Put(s.ctx, []byte(subscriptionName), nil) if err != nil { return newError("failed to delete import source: ", err) } } if _, ok := s.trackedSubscriptions[subscriptionName]; ok { err := s.applySubscriptionTo(subscriptionName, &specs.SubscriptionDocument{Server: make([]*specs.SubscriptionServerConfig, 0)}) if err != nil { return newError("failed to apply empty subscription: ", err) } delete(s.trackedSubscriptions, subscriptionName) } return nil } func (s *SubscriptionManagerImpl) init() error { s.refreshTask = &task.Periodic{ Interval: time.Duration(60) * time.Second, Execute: s.housekeeping, } s.trackedSubscriptions = make(map[string]*trackedSubscription) s.converter = entries.GetOverlayConverterRegistry() if s.config.NonnativeConverterOverlay != nil { zipReader, err := zip.NewReader(bytes.NewReader(s.config.NonnativeConverterOverlay), int64(len(s.config.NonnativeConverterOverlay))) if err != nil { return newError("failed to read nonnative converter overlay: ", err) } converter, err := nonnativeifce.NewNonNativeConverterConstructor(zipReader) if err != nil { return newError("failed to construct nonnative converter: ", err) } if err := s.converter.RegisterConverter("user_nonnative", converter); err != nil { return newError("failed to register user nonnative converter: ", err) } } for _, v := range s.config.Imports { if err := s.addTrackedSubscriptionFromImportSource(v, false); err != nil { return newError("failed to add tracked subscription: ", err) } } return nil } func (s *SubscriptionManagerImpl) loadAllFromPersistentStorage() error { if !s.config.Persistence { return nil } protoImportSources, err := s.persistImportStorage.List(s.ctx, []byte("")) if err != nil { return newError("failed to list import sources: ", err) } for _, protoImportSource := range protoImportSources { var importSource subscription.ImportSource err := s.persistImportSourceProtoStorage.GetProto(s.ctx, string(protoImportSource), &importSource) if err != nil { return newError("failed to get import source: ", err) } if err := s.addTrackedSubscriptionFromImportSource(&importSource, false); err != nil { return newError("failed to add tracked subscription: ", err) } } return nil } func NewSubscriptionManager(ctx context.Context, config *subscription.Config) (*SubscriptionManagerImpl, error) { instance := core.MustFromContext(ctx) impl := &SubscriptionManagerImpl{ctx: ctx, s: instance, config: config} if err := impl.init(); err != nil { return nil, newError("failed to init subscription manager: ", err) } return impl, nil } func init() { common.Must(common.RegisterConfig((*subscription.Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewSubscriptionManager(ctx, config.(*subscription.Config)) })) } ================================================ FILE: app/subscription/subscriptionmanager/manager_rpc.go ================================================ package subscriptionmanager import "github.com/v2fly/v2ray-core/v5/app/subscription" func (s *SubscriptionManagerImpl) AddTrackedSubscriptionFromImportSource(importSource *subscription.ImportSource) error { s.Lock() defer s.Unlock() return s.addTrackedSubscriptionFromImportSource(importSource, true) } func (s *SubscriptionManagerImpl) RemoveTrackedSubscription(name string) error { s.Lock() defer s.Unlock() return s.removeTrackedSubscription(name) } func (s *SubscriptionManagerImpl) UpdateTrackedSubscription(name string) error { s.Lock() defer s.Unlock() return s.updateSubscription(name) } func (s *SubscriptionManagerImpl) ListTrackedSubscriptions() []string { s.Lock() defer s.Unlock() var names []string for name := range s.trackedSubscriptions { names = append(names, name) } return names } func (s *SubscriptionManagerImpl) GetTrackedSubscriptionStatus(name string) (*subscription.TrackedSubscriptionStatus, error) { s.Lock() defer s.Unlock() if trackedSubscriptionItem, ok := s.trackedSubscriptions[name]; ok { result := &subscription.TrackedSubscriptionStatus{} if err := trackedSubscriptionItem.fillStatus(result); err != nil { return nil, newError("failed to fill status").Base(err) } result.ImportSource = trackedSubscriptionItem.importSource return result, nil } else { return nil, newError("unable to locate") } } ================================================ FILE: app/subscription/subscriptionmanager/serverspec_materialize.go ================================================ package subscriptionmanager import ( core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/subscription/specs" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func (s *SubscriptionManagerImpl) materialize(subscriptionName, tagName string, serverSpec *specs.SubscriptionServerConfig) (*core.OutboundHandlerConfig, error) { outboundConf, err := s.getOutboundTemplateForSubscriptionName(subscriptionName) if err != nil { return nil, newError("failed to get outbound template for subscription name: ", err) } senderSettingsIfcd, err := serial.GetInstanceOf(outboundConf.SenderSettings) if err != nil { return nil, newError("failed to get sender settings: ", err) } senderSettings := senderSettingsIfcd.(*proxyman.SenderConfig) if serverSpec.Configuration.Transport != "" { senderSettings.StreamSettings.ProtocolName = serverSpec.Configuration.Transport senderSettings.StreamSettings.TransportSettings = append(senderSettings.StreamSettings.TransportSettings, &internet.TransportConfig{ProtocolName: serverSpec.Configuration.Transport, Settings: serverSpec.Configuration.TransportSettings}) } if serverSpec.Configuration.Security != "" { senderSettings.StreamSettings.SecurityType = serverSpec.Configuration.Security senderSettings.StreamSettings.SecuritySettings = append(senderSettings.StreamSettings.SecuritySettings, serverSpec.Configuration.SecuritySettings) } outboundConf.SenderSettings = serial.ToTypedMessage(senderSettings) outboundConf.ProxySettings = serverSpec.Configuration.ProtocolSettings outboundConf.Tag = tagName return outboundConf, nil } func (s *SubscriptionManagerImpl) getOutboundTemplateForSubscriptionName(subscriptionName string) (*core.OutboundHandlerConfig, error) { //nolint: unparam senderSetting := &proxyman.SenderConfig{ DomainStrategy: proxyman.SenderConfig_AS_IS, StreamSettings: &internet.StreamConfig{}, } return &core.OutboundHandlerConfig{SenderSettings: serial.ToTypedMessage(senderSetting)}, nil } ================================================ FILE: app/subscription/subscriptionmanager/subdocapplier.go ================================================ package subscriptionmanager import ( "fmt" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/subscription/specs" ) func (s *SubscriptionManagerImpl) applySubscriptionTo(name string, document *specs.SubscriptionDocument) error { var trackedSub *trackedSubscription if trackedSubFound, found := s.trackedSubscriptions[name]; !found { return newError("not found") } else { trackedSub = trackedSubFound } delta, err := trackedSub.diff(document) if err != nil { return err } nameToServerConfig := make(map[string]*specs.SubscriptionServerConfig) for _, server := range document.Server { nameToServerConfig[server.Id] = server } for _, serverName := range delta.removed { if err := s.removeManagedServer(name, serverName); err != nil { newError("failed to remove managed server: ", err).AtWarning().WriteToLog() continue } trackedSub.recordRemovedServer(serverName) } for _, serverName := range delta.modified { serverConfig := nameToServerConfig[serverName] if err := s.updateManagedServer(name, serverName, serverConfig); err != nil { newError("failed to update managed server: ", err).AtWarning().WriteToLog() continue } trackedSub.recordUpdatedServer(serverName, serverConfig.Metadata[ServerMetadataTagName], serverConfig) } for _, serverName := range delta.added { serverConfig := nameToServerConfig[serverName] if err := s.addManagedServer(name, serverName, serverConfig); err != nil { newError("failed to add managed server: ", err).AtWarning().WriteToLog() continue } trackedSub.recordUpdatedServer(serverName, serverConfig.Metadata[ServerMetadataTagName], serverConfig) } newError("finished applying subscription, ", name, "; ", fmt.Sprintf( "%v updated, %v added, %v removed, %v unchanged", len(delta.modified), len(delta.added), len(delta.removed), len(delta.unchanged))).AtInfo().WriteToLog() return nil } func (s *SubscriptionManagerImpl) removeManagedServer(subscriptionName, serverName string) error { var trackedSub *trackedSubscription if trackedSubFound, found := s.trackedSubscriptions[subscriptionName]; !found { return newError("not found") } else { trackedSub = trackedSubFound } var trackedServer *materializedServer if trackedServerFound, err := trackedSub.getCurrentServer(serverName); err != nil { return err } else { trackedServer = trackedServerFound } tagName := fmt.Sprintf("%s_%s", trackedSub.importSource.TagPrefix, trackedServer.tagPostfix) if err := core.RemoveOutboundHandler(s.s, tagName); err != nil { return newError("failed to remove handler: ", err) } trackedSub.recordRemovedServer(serverName) return nil } func (s *SubscriptionManagerImpl) addManagedServer(subscriptionName, serverName string, serverSpec *specs.SubscriptionServerConfig, ) error { var trackedSub *trackedSubscription if trackedSubFound, found := s.trackedSubscriptions[subscriptionName]; !found { return newError("not found") } else { trackedSub = trackedSubFound } tagPostfix := serverSpec.Metadata[ServerMetadataTagName] tagName := fmt.Sprintf("%s_%s", trackedSub.importSource.TagPrefix, tagPostfix) materialized, err := s.materialize(subscriptionName, tagName, serverSpec) if err != nil { return newError("failed to materialize server: ", err) } if err := core.AddOutboundHandler(s.s, materialized); err != nil { return newError("failed to add handler: ", err) } trackedSub.recordUpdatedServer(serverName, tagPostfix, serverSpec) return nil } func (s *SubscriptionManagerImpl) updateManagedServer(subscriptionName, serverName string, serverSpec *specs.SubscriptionServerConfig, ) error { if err := s.removeManagedServer(subscriptionName, serverName); err != nil { return newError("failed to update managed server: ", err).AtWarning() } if err := s.addManagedServer(subscriptionName, serverName, serverSpec); err != nil { return newError("failed to update managed server : ", err).AtWarning() } return nil } ================================================ FILE: app/subscription/subscriptionmanager/subdocchecker.go ================================================ package subscriptionmanager import "time" func (s *SubscriptionManagerImpl) checkupSubscription(subscriptionName string) error { var trackedSub *trackedSubscription if trackedSubFound, found := s.trackedSubscriptions[subscriptionName]; !found { return newError("not found") } else { trackedSub = trackedSubFound } shouldUpdate := false if trackedSub.currentDocumentExpireTime.Before(time.Now()) { shouldUpdate = true } if shouldUpdate { if err := s.updateSubscription(subscriptionName); err != nil { return newError("failed to update subscription: ", err) } } return nil } ================================================ FILE: app/subscription/subscriptionmanager/subdocupdater.go ================================================ package subscriptionmanager import ( "fmt" "strings" "time" "unicode" "golang.org/x/crypto/sha3" "github.com/v2fly/v2ray-core/v5/app/subscription/containers" "github.com/v2fly/v2ray-core/v5/app/subscription/documentfetcher" "github.com/v2fly/v2ray-core/v5/app/subscription/specs" ) func (s *SubscriptionManagerImpl) updateSubscription(subscriptionName string) error { var trackedSub *trackedSubscription if trackedSubFound, found := s.trackedSubscriptions[subscriptionName]; !found { return newError("not found") } else { trackedSub = trackedSubFound } importSource := trackedSub.importSource docFetcher, err := documentfetcher.GetFetcher("http") if err != nil { return newError("failed to get fetcher: ", err) } if strings.HasPrefix(importSource.Url, "data:") { docFetcher, err = documentfetcher.GetFetcher("dataurl") if err != nil { return newError("failed to get fetcher: ", err) } } downloadedDocument, err := docFetcher.DownloadDocument(s.ctx, importSource) if err != nil { return newError("failed to download document: ", err) } trackedSub.originalDocument = downloadedDocument container, err := containers.TryAllParsers(trackedSub.originalDocument, "") if err != nil { return newError("failed to parse document: ", err) } trackedSub.originalContainer = container parsedDocument := &specs.SubscriptionDocument{} parsedDocument.Metadata = container.Metadata trackedSub.originalServerConfig = make(map[string]*originalServerConfig) for _, server := range trackedSub.originalContainer.ServerSpecs { documentHash := sha3.Sum256(server.Content) serverConfigHashName := fmt.Sprintf("%x", documentHash) parsed, err := s.converter.TryAllConverters(server.Content, "outbound", server.KindHint) if err != nil { trackedSub.originalServerConfig["!!!"+serverConfigHashName] = &originalServerConfig{data: server.Content} continue } s.polyfillServerConfig(parsed, serverConfigHashName) parsedDocument.Server = append(parsedDocument.Server, parsed) trackedSub.originalServerConfig[parsed.Id] = &originalServerConfig{data: server.Content} } newError("new subscription document fetched and parsed from ", subscriptionName).AtInfo().WriteToLog() if err := s.applySubscriptionTo(subscriptionName, parsedDocument); err != nil { return newError("failed to apply subscription: ", err) } trackedSub.currentDocument = parsedDocument trackedSub.currentDocumentExpireTime = time.Now().Add(time.Second * time.Duration(importSource.DefaultExpireSeconds)) return nil } func (s *SubscriptionManagerImpl) polyfillServerConfig(document *specs.SubscriptionServerConfig, hash string) { document.Id = hash if document.Metadata == nil { document.Metadata = make(map[string]string) } if id, ok := document.Metadata[ServerMetadataID]; !ok || id == "" { document.Metadata[ServerMetadataID] = document.Id } else { document.Id = document.Metadata[ServerMetadataID] } if fqn, ok := document.Metadata[ServerMetadataFullyQualifiedName]; !ok || fqn == "" { document.Metadata[ServerMetadataFullyQualifiedName] = hash } if tagName, ok := document.Metadata[ServerMetadataTagName]; !ok || tagName == "" { document.Metadata[ServerMetadataTagName] = document.Metadata[ServerMetadataID] } document.Metadata[ServerMetadataTagName] = s.restrictTagName(document.Metadata[ServerMetadataTagName]) } func (s *SubscriptionManagerImpl) restrictTagName(tagName string) string { newTagName := &strings.Builder{} somethingRemoved := false for _, c := range tagName { if (unicode.IsLetter(c) || unicode.IsNumber(c)) && c < 128 { newTagName.WriteRune(c) } else { somethingRemoved = true } } newTagNameString := newTagName.String() if len(newTagNameString) > 24 { newTagNameString = newTagNameString[:15] somethingRemoved = true } if somethingRemoved { hashedTagName := sha3.Sum256([]byte(tagName)) hashedTagNameString := fmt.Sprintf("%x", hashedTagName) newTagNameString = newTagNameString + "_" + hashedTagNameString[:8] } return newTagNameString } ================================================ FILE: app/subscription/subscriptionmanager/tracked_subscription.go ================================================ package subscriptionmanager import ( "time" "github.com/v2fly/v2ray-core/v5/app/subscription" "github.com/v2fly/v2ray-core/v5/app/subscription/containers" "github.com/v2fly/v2ray-core/v5/app/subscription/specs" ) func newTrackedSubscription(importSource *subscription.ImportSource) (*trackedSubscription, error) { //nolint: unparam return &trackedSubscription{importSource: importSource, materialized: map[string]*materializedServer{}}, nil } type trackedSubscription struct { importSource *subscription.ImportSource currentDocumentExpireTime time.Time currentDocument *specs.SubscriptionDocument materialized map[string]*materializedServer originalDocument []byte originalContainer *containers.Container originalServerConfig map[string]*originalServerConfig addedByAPI bool } type originalServerConfig struct { data []byte } func (s *trackedSubscription) diff(newDocument *specs.SubscriptionDocument) (changedDocument, error) { //nolint: unparam delta := changedDocument{} seen := make(map[string]bool) for _, server := range newDocument.Server { if currentMaterialized, found := s.materialized[server.Id]; found { if currentMaterialized.serverConfig.Metadata[ServerMetadataFullyQualifiedName] == server.Metadata[ServerMetadataFullyQualifiedName] { delta.unchanged = append(delta.unchanged, server.Id) } else { delta.modified = append(delta.modified, server.Id) } seen[server.Id] = true } else { delta.added = append(delta.added, server.Id) } } for name := range s.materialized { if _, ok := seen[name]; !ok { delta.removed = append(delta.removed, name) } } return delta, nil } func (s *trackedSubscription) recordRemovedServer(name string) { delete(s.materialized, name) } func (s *trackedSubscription) recordUpdatedServer(name, tagPostfix string, serverConfig *specs.SubscriptionServerConfig) { s.materialized[name] = &materializedServer{tagPostfix: tagPostfix, serverConfig: serverConfig} } func (s *trackedSubscription) getCurrentServer(name string) (*materializedServer, error) { if materialized, found := s.materialized[name]; found { return materialized, nil } else { return nil, newError("not found") } } type materializedServer struct { tagPostfix string serverConfig *specs.SubscriptionServerConfig } func (s *trackedSubscription) fillStatus(status *subscription.TrackedSubscriptionStatus) error { //nolint: unparam status.ImportSource = s.importSource if s.currentDocument == nil { return nil } status.DocumentMetadata = s.currentDocument.Metadata status.Servers = make(map[string]*subscription.SubscriptionServer) for _, v := range s.currentDocument.Server { status.Servers[v.Id] = &subscription.SubscriptionServer{ ServerMetadata: v.Metadata, } if materializedInstance, ok := s.materialized[v.Id]; ok { status.Servers[v.Id].Tag = materializedInstance.tagPostfix } } status.AddedByApi = s.addedByAPI return nil } ================================================ FILE: app/tun/config.pb.go ================================================ package tun import ( proxyman "github.com/v2fly/v2ray-core/v5/app/proxyman" routercommon "github.com/v2fly/v2ray-core/v5/app/router/routercommon" packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" _ "github.com/v2fly/v2ray-core/v5/common/protoext" internet "github.com/v2fly/v2ray-core/v5/transport/internet" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Mtu uint32 `protobuf:"varint,2,opt,name=mtu,proto3" json:"mtu,omitempty"` UserLevel uint32 `protobuf:"varint,3,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,4,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"` Tag string `protobuf:"bytes,5,opt,name=tag,proto3" json:"tag,omitempty"` Ips []*routercommon.CIDR `protobuf:"bytes,6,rep,name=ips,proto3" json:"ips,omitempty"` Routes []*routercommon.CIDR `protobuf:"bytes,7,rep,name=routes,proto3" json:"routes,omitempty"` EnablePromiscuousMode bool `protobuf:"varint,8,opt,name=enable_promiscuous_mode,json=enablePromiscuousMode,proto3" json:"enable_promiscuous_mode,omitempty"` EnableSpoofing bool `protobuf:"varint,9,opt,name=enable_spoofing,json=enableSpoofing,proto3" json:"enable_spoofing,omitempty"` SocketSettings *internet.SocketConfig `protobuf:"bytes,10,opt,name=socket_settings,json=socketSettings,proto3" json:"socket_settings,omitempty"` SniffingSettings *proxyman.SniffingConfig `protobuf:"bytes,11,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_app_tun_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_app_tun_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_app_tun_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetName() string { if x != nil { return x.Name } return "" } func (x *Config) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } func (x *Config) GetUserLevel() uint32 { if x != nil { return x.UserLevel } return 0 } func (x *Config) GetPacketEncoding() packetaddr.PacketAddrType { if x != nil { return x.PacketEncoding } return packetaddr.PacketAddrType(0) } func (x *Config) GetTag() string { if x != nil { return x.Tag } return "" } func (x *Config) GetIps() []*routercommon.CIDR { if x != nil { return x.Ips } return nil } func (x *Config) GetRoutes() []*routercommon.CIDR { if x != nil { return x.Routes } return nil } func (x *Config) GetEnablePromiscuousMode() bool { if x != nil { return x.EnablePromiscuousMode } return false } func (x *Config) GetEnableSpoofing() bool { if x != nil { return x.EnableSpoofing } return false } func (x *Config) GetSocketSettings() *internet.SocketConfig { if x != nil { return x.SocketSettings } return nil } func (x *Config) GetSniffingSettings() *proxyman.SniffingConfig { if x != nil { return x.SniffingSettings } return nil } var File_app_tun_config_proto protoreflect.FileDescriptor const file_app_tun_config_proto_rawDesc = "" + "\n" + "\x14app/tun/config.proto\x12\x12v2ray.core.app.tun\x1a\x19app/proxyman/config.proto\x1a$app/router/routercommon/common.proto\x1a common/protoext/extensions.proto\x1a\"common/net/packetaddr/config.proto\x1a\x1ftransport/internet/config.proto\"\xd2\x04\n" + "\x06Config\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n" + "\x03mtu\x18\x02 \x01(\rR\x03mtu\x12\x1d\n" + "\n" + "user_level\x18\x03 \x01(\rR\tuserLevel\x12R\n" + "\x0fpacket_encoding\x18\x04 \x01(\x0e2).v2ray.core.net.packetaddr.PacketAddrTypeR\x0epacketEncoding\x12\x10\n" + "\x03tag\x18\x05 \x01(\tR\x03tag\x12:\n" + "\x03ips\x18\x06 \x03(\v2(.v2ray.core.app.router.routercommon.CIDRR\x03ips\x12@\n" + "\x06routes\x18\a \x03(\v2(.v2ray.core.app.router.routercommon.CIDRR\x06routes\x126\n" + "\x17enable_promiscuous_mode\x18\b \x01(\bR\x15enablePromiscuousMode\x12'\n" + "\x0fenable_spoofing\x18\t \x01(\bR\x0eenableSpoofing\x12T\n" + "\x0fsocket_settings\x18\n" + " \x01(\v2+.v2ray.core.transport.internet.SocketConfigR\x0esocketSettings\x12T\n" + "\x11sniffing_settings\x18\v \x01(\v2'.v2ray.core.app.proxyman.SniffingConfigR\x10sniffingSettings:\x12\x82\xb5\x18\x0e\n" + "\aservice\x12\x03tunBW\n" + "\x16com.v2ray.core.app.tunP\x01Z&github.com/v2fly/v2ray-core/v5/app/tun\xaa\x02\x12V2Ray.Core.App.Tunb\x06proto3" var ( file_app_tun_config_proto_rawDescOnce sync.Once file_app_tun_config_proto_rawDescData []byte ) func file_app_tun_config_proto_rawDescGZIP() []byte { file_app_tun_config_proto_rawDescOnce.Do(func() { file_app_tun_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_tun_config_proto_rawDesc), len(file_app_tun_config_proto_rawDesc))) }) return file_app_tun_config_proto_rawDescData } var file_app_tun_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_app_tun_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.app.tun.Config (packetaddr.PacketAddrType)(0), // 1: v2ray.core.net.packetaddr.PacketAddrType (*routercommon.CIDR)(nil), // 2: v2ray.core.app.router.routercommon.CIDR (*internet.SocketConfig)(nil), // 3: v2ray.core.transport.internet.SocketConfig (*proxyman.SniffingConfig)(nil), // 4: v2ray.core.app.proxyman.SniffingConfig } var file_app_tun_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.app.tun.Config.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType 2, // 1: v2ray.core.app.tun.Config.ips:type_name -> v2ray.core.app.router.routercommon.CIDR 2, // 2: v2ray.core.app.tun.Config.routes:type_name -> v2ray.core.app.router.routercommon.CIDR 3, // 3: v2ray.core.app.tun.Config.socket_settings:type_name -> v2ray.core.transport.internet.SocketConfig 4, // 4: v2ray.core.app.tun.Config.sniffing_settings:type_name -> v2ray.core.app.proxyman.SniffingConfig 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_app_tun_config_proto_init() } func file_app_tun_config_proto_init() { if File_app_tun_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_tun_config_proto_rawDesc), len(file_app_tun_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_app_tun_config_proto_goTypes, DependencyIndexes: file_app_tun_config_proto_depIdxs, MessageInfos: file_app_tun_config_proto_msgTypes, }.Build() File_app_tun_config_proto = out.File file_app_tun_config_proto_goTypes = nil file_app_tun_config_proto_depIdxs = nil } ================================================ FILE: app/tun/config.proto ================================================ syntax = "proto3"; package v2ray.core.app.tun; option csharp_namespace = "V2Ray.Core.App.Tun"; option go_package = "github.com/v2fly/v2ray-core/v5/app/tun"; option java_package = "com.v2ray.core.app.tun"; option java_multiple_files = true; import "app/proxyman/config.proto"; import "app/router/routercommon/common.proto"; import "common/protoext/extensions.proto"; import "common/net/packetaddr/config.proto"; import "transport/internet/config.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "service"; option (v2ray.core.common.protoext.message_opt).short_name = "tun"; string name = 1; uint32 mtu = 2; uint32 user_level = 3; v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 4; string tag = 5; repeated v2ray.core.app.router.routercommon.CIDR ips = 6; repeated v2ray.core.app.router.routercommon.CIDR routes = 7; bool enable_promiscuous_mode = 8; bool enable_spoofing = 9; v2ray.core.transport.internet.SocketConfig socket_settings = 10; v2ray.core.app.proxyman.SniffingConfig sniffing_settings = 11; } ================================================ FILE: app/tun/device/device.go ================================================ package device import "gvisor.dev/gvisor/pkg/tcpip/stack" //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type Device interface { stack.LinkEndpoint } type Options struct { Name string MTU uint32 } type DeviceConstructor func(Options) (Device, error) ================================================ FILE: app/tun/device/errors.generated.go ================================================ package device import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/tun/device/gvisor/errors.generated.go ================================================ package gvisor import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/tun/device/gvisor/gvisor.go ================================================ package gvisor //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/tun/device/gvisor/gvisor_linux.go ================================================ //go:build linux && ((linux && amd64) || (linux && arm64)) // +build linux // +build linux,amd64 linux,arm64 package gvisor import ( "fmt" "unsafe" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/rawfile" "gvisor.dev/gvisor/pkg/tcpip/stack" "github.com/v2fly/v2ray-core/v5/app/tun/device" "gvisor.dev/gvisor/pkg/tcpip/link/fdbased" "gvisor.dev/gvisor/pkg/tcpip/link/tun" ) const ( ifReqSize = unix.IFNAMSIZ + 64 ) type GvisorTUN struct { stack.LinkEndpoint options device.Options fd int mtu uint32 // real MTU } func New(options device.Options) (device.Device, error) { t := &GvisorTUN{options: options} if len(options.Name) > unix.IFNAMSIZ { return nil, newError("name too long").AtError() } fd, err := tun.Open(options.Name) if err != nil { return nil, newError("failed to open tun device").Base(err).AtError() } t.fd = fd if options.MTU > 0 { _ = setMTU(options.Name, int(options.MTU)) } mtu, err := rawfile.GetMTU(options.Name) if err != nil { return nil, newError("failed to get mtu").Base(err).AtError() } t.mtu = mtu linkEndpoint, err := fdbased.New(&fdbased.Options{ FDs: []int{fd}, MTU: mtu, // TUN is not need to process ethernet header. EthernetHeader: false, // Readv is the default dispatch mode and is the least performant of the // dispatch options but the one that is supported by all underlying FD // types. PacketDispatchMode: fdbased.Readv, MaxSyscallHeaderBytes: 0x00, }) if err != nil { return nil, newError("failed to create link endpoint").Base(err).AtError() } t.LinkEndpoint = linkEndpoint return t, nil } func (t *GvisorTUN) Close() { _ = unix.Close(t.fd) } // Modified from golang.zx2c4.com/wireguard/tun/tun_linux.go func setMTU(name string, n int) error { // open datagram socket fd, err := unix.Socket( unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0, ) if err != nil { return err } defer func() { _ = unix.Close(fd) }() // do ioctl call var ifr [ifReqSize]byte copy(ifr[:], name) *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) _, _, errno := unix.Syscall( unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ifr[0])), ) if errno != 0 { return fmt.Errorf("failed to set MTU of TUN device: %w", errno) } return nil } ================================================ FILE: app/tun/device/gvisor/gvisor_others.go ================================================ //go:build !linux || (linux && !(amd64 || arm64)) // +build !linux linux,!amd64,!arm64 package gvisor import "github.com/v2fly/v2ray-core/v5/app/tun/device" func New(options device.Options) (device.Device, error) { return nil, newError("not supported").AtError() } ================================================ FILE: app/tun/device/linkWriterToWriter.go ================================================ package device import ( "io" "github.com/v2fly/v2ray-core/v5/common/errors" "gvisor.dev/gvisor/pkg/buffer" "gvisor.dev/gvisor/pkg/tcpip/stack" ) func NewLinkWriterToWriter(writer stack.LinkWriter) io.Writer { return &linkWriterToWriter{writer: writer} } type linkWriterToWriter struct { writer stack.LinkWriter } func (l linkWriterToWriter) Write(p []byte) (n int, err error) { buffer := buffer.MakeWithData(p) packetBufferPtr := stack.NewPacketBuffer(stack.PacketBufferOptions{ Payload: buffer, OnRelease: func() { buffer.Release() }, }) packetList := stack.PacketBufferList{} packetList.PushBack(packetBufferPtr) _, terr := l.writer.WritePackets(packetList) if terr != nil { return 0, newError("failed to write packet").Base(errors.New(terr.String())).AtError() } return len(p), nil } ================================================ FILE: app/tun/errors.generated.go ================================================ package tun import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/tun/handler.go ================================================ package tun ================================================ FILE: app/tun/handler_tcp.go ================================================ package tun import ( "context" "time" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/waiter" tun_net "github.com/v2fly/v2ray-core/v5/app/tun/net" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" internet "github.com/v2fly/v2ray-core/v5/transport/internet" ) const ( rcvWnd = 0 // default settings maxInFlight = 2 << 10 ) type tcpConn struct { *gonet.TCPConn id stack.TransportEndpointID } func (c *tcpConn) ID() *stack.TransportEndpointID { return &c.id } type TCPHandler struct { ctx context.Context dispatcher routing.Dispatcher policyManager policy.Manager config *Config } func SetTCPHandler(ctx context.Context, dispatcher routing.Dispatcher, policyManager policy.Manager, config *Config) StackOption { return func(s *stack.Stack) error { tcpForwarder := tcp.NewForwarder(s, rcvWnd, maxInFlight, func(r *tcp.ForwarderRequest) { wg := new(waiter.Queue) linkedEndpoint, err := r.CreateEndpoint(wg) if err != nil { r.Complete(true) return } defer r.Complete(false) if config.SocketSettings != nil { if err := applySocketOptions(s, linkedEndpoint, config.SocketSettings); err != nil { newError("failed to apply socket options: ", err).WriteToLog(session.ExportIDToError(ctx)) } } conn := &tcpConn{ TCPConn: gonet.NewTCPConn(wg, linkedEndpoint), id: r.ID(), } handler := &TCPHandler{ ctx: ctx, dispatcher: dispatcher, policyManager: policyManager, config: config, } go handler.Handle(conn) }) s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket) return nil } } func (h *TCPHandler) Handle(conn tun_net.TCPConn) error { defer conn.Close() id := conn.ID() ctx := session.ContextWithInbound(h.ctx, &session.Inbound{Tag: h.config.Tag}) sessionPolicy := h.policyManager.ForLevel(h.config.UserLevel) dest := net.TCPDestination(tun_net.AddressFromTCPIPAddr(id.LocalAddress), net.Port(id.LocalPort)) src := net.TCPDestination(tun_net.AddressFromTCPIPAddr(id.RemoteAddress), net.Port(id.RemotePort)) ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: src, To: dest, Status: log.AccessAccepted, Reason: "", }) content := new(session.Content) if h.config.SniffingSettings != nil { content.SniffingRequest.Enabled = h.config.SniffingSettings.Enabled content.SniffingRequest.OverrideDestinationForProtocol = h.config.SniffingSettings.DestinationOverride content.SniffingRequest.MetadataOnly = h.config.SniffingSettings.MetadataOnly } ctx = session.ContextWithContent(ctx, content) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) link, err := h.dispatcher.Dispatch(ctx, dest) if err != nil { return newError("failed to dispatch").Base(err) } responseDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP response").Base(err) } return nil } requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if err := buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP request").Base(err) } return nil } requestDoneAndCloseWriter := task.OnSuccess(requestDone, task.Close(link.Writer)) if err := task.Run(h.ctx, requestDoneAndCloseWriter, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) return newError("connection ends").Base(err) } return nil } func applySocketOptions(s *stack.Stack, endpoint tcpip.Endpoint, config *internet.SocketConfig) tcpip.Error { if config.TcpKeepAliveInterval > 0 { interval := tcpip.KeepaliveIntervalOption(time.Duration(config.TcpKeepAliveInterval) * time.Second) if err := endpoint.SetSockOpt(&interval); err != nil { return err } } if config.TcpKeepAliveIdle > 0 { idle := tcpip.KeepaliveIdleOption(time.Duration(config.TcpKeepAliveIdle) * time.Second) if err := endpoint.SetSockOpt(&idle); err != nil { return err } } if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { endpoint.SocketOptions().SetKeepAlive(true) } { var sendBufferSizeRangeOption tcpip.TCPSendBufferSizeRangeOption if err := s.TransportProtocolOption(header.TCPProtocolNumber, &sendBufferSizeRangeOption); err == nil { endpoint.SocketOptions().SetReceiveBufferSize(int64(sendBufferSizeRangeOption.Default), false) } var receiveBufferSizeRangeOption tcpip.TCPReceiveBufferSizeRangeOption if err := s.TransportProtocolOption(header.TCPProtocolNumber, &receiveBufferSizeRangeOption); err == nil { endpoint.SocketOptions().SetSendBufferSize(int64(receiveBufferSizeRangeOption.Default), false) } } return nil } ================================================ FILE: app/tun/handler_udp.go ================================================ package tun import ( "context" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/stack" gvisor_udp "gvisor.dev/gvisor/pkg/tcpip/transport/udp" "gvisor.dev/gvisor/pkg/waiter" tun_net "github.com/v2fly/v2ray-core/v5/app/tun/net" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" udp_proto "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) type UDPHandler struct { ctx context.Context dispatcher routing.Dispatcher policyManager policy.Manager config *Config } type udpConn struct { *gonet.UDPConn id stack.TransportEndpointID } func (c *udpConn) ID() *stack.TransportEndpointID { return &c.id } func SetUDPHandler(ctx context.Context, dispatcher routing.Dispatcher, policyManager policy.Manager, config *Config) StackOption { return func(s *stack.Stack) error { udpForwarder := gvisor_udp.NewForwarder(s, func(r *gvisor_udp.ForwarderRequest) bool { wg := new(waiter.Queue) linkedEndpoint, err := r.CreateEndpoint(wg) if err != nil { newError("failed to create endpoint: ", err).WriteToLog(session.ExportIDToError(ctx)) return false } conn := &udpConn{ UDPConn: gonet.NewUDPConn(wg, linkedEndpoint), id: r.ID(), } handler := &UDPHandler{ ctx: ctx, dispatcher: dispatcher, policyManager: policyManager, config: config, } go handler.Handle(conn) return true }) s.SetTransportProtocolHandler(gvisor_udp.ProtocolNumber, udpForwarder.HandlePacket) return nil } } func (h *UDPHandler) Handle(conn tun_net.UDPConn) error { defer conn.Close() id := conn.ID() ctx := session.ContextWithInbound(h.ctx, &session.Inbound{Tag: h.config.Tag}) content := new(session.Content) if h.config.SniffingSettings != nil { content.SniffingRequest.Enabled = h.config.SniffingSettings.Enabled content.SniffingRequest.OverrideDestinationForProtocol = h.config.SniffingSettings.DestinationOverride content.SniffingRequest.MetadataOnly = h.config.SniffingSettings.MetadataOnly } ctx = session.ContextWithContent(ctx, content) udpDispatcherConstructor := udp.NewSplitDispatcher dest := net.UDPDestination(tun_net.AddressFromTCPIPAddr(id.LocalAddress), net.Port(id.LocalPort)) src := net.UDPDestination(tun_net.AddressFromTCPIPAddr(id.RemoteAddress), net.Port(id.RemotePort)) udpServer := udpDispatcherConstructor(h.dispatcher, func(ctx context.Context, packet *udp_proto.Packet) { if _, err := conn.WriteTo(packet.Payload.Bytes(), &net.UDPAddr{ IP: src.Address.IP(), Port: int(src.Port), }); err != nil { newError("failed to write UDP packet").Base(err).WriteToLog() } }) for { select { case <-ctx.Done(): return nil default: var buffer [2048]byte n, _, err := conn.ReadFrom(buffer[:]) if err != nil { return newError("failed to read UDP packet").Base(err) } currentPacketCtx := ctx udpServer.Dispatch(currentPacketCtx, dest, buf.FromBytes(buffer[:n])) } } } ================================================ FILE: app/tun/net/net.go ================================================ package net import ( "github.com/v2fly/v2ray-core/v5/common/net" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/stack" ) type TCPConn interface { net.Conn ID() *stack.TransportEndpointID } type UDPConn interface { net.Conn net.PacketConn ID() *stack.TransportEndpointID } func AddressFromTCPIPAddr(addr tcpip.Address) net.Address { return net.IPAddress(addr.AsSlice()) } ================================================ FILE: app/tun/option.go ================================================ package tun import ( "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" ) func CreateNIC(id tcpip.NICID, linkEndpoint stack.LinkEndpoint) StackOption { return func(s *stack.Stack) error { if err := s.CreateNICWithOptions(id, linkEndpoint, stack.NICOptions{ Disabled: false, QDisc: nil, }); err != nil { return newError("failed to create NIC:", err) } return nil } } func SetPromiscuousMode(id tcpip.NICID, enable bool) StackOption { return func(s *stack.Stack) error { if err := s.SetPromiscuousMode(id, enable); err != nil { return newError("failed to set promiscuous mode:", err) } return nil } } func SetSpoofing(id tcpip.NICID, enable bool) StackOption { return func(s *stack.Stack) error { if err := s.SetSpoofing(id, enable); err != nil { return newError("failed to set spoofing:", err) } return nil } } func AddProtocolAddress(id tcpip.NICID, ips []*routercommon.CIDR) StackOption { return func(s *stack.Stack) error { for _, ip := range ips { tcpIPAddr := tcpip.AddrFromSlice(ip.Ip) protocolAddress := tcpip.ProtocolAddress{ AddressWithPrefix: tcpip.AddressWithPrefix{ Address: tcpIPAddr, PrefixLen: int(ip.Prefix), }, } switch tcpIPAddr.Len() { case 4: protocolAddress.Protocol = ipv4.ProtocolNumber case 16: protocolAddress.Protocol = ipv6.ProtocolNumber default: return newError("invalid IP address length:", tcpIPAddr.Len()) } if err := s.AddProtocolAddress(id, protocolAddress, stack.AddressProperties{}); err != nil { return newError("failed to add protocol address:", err) } } return nil } } func SetRouteTable(id tcpip.NICID, routes []*routercommon.CIDR) StackOption { return func(s *stack.Stack) error { s.SetRouteTable(func() (table []tcpip.Route) { for _, cidrs := range routes { subnet := tcpip.AddressWithPrefix{ Address: tcpip.AddrFromSlice(cidrs.Ip), PrefixLen: int(cidrs.Prefix), }.Subnet() route := tcpip.Route{ Destination: subnet, NIC: id, } table = append(table, route) } return }()) return nil } } func SetTCPSendBufferSize(size int) StackOption { return func(s *stack.Stack) error { sendBufferSizeRangeOption := tcpip.TCPSendBufferSizeRangeOption{Min: tcp.MinBufferSize, Default: size, Max: tcp.MaxBufferSize} if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sendBufferSizeRangeOption); err != nil { return newError("failed to set tcp send buffer size:", err) } return nil } } func SetTCPReceiveBufferSize(size int) StackOption { return func(s *stack.Stack) error { receiveBufferSizeRangeOption := tcpip.TCPReceiveBufferSizeRangeOption{Min: tcp.MinBufferSize, Default: size, Max: tcp.MaxBufferSize} if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &receiveBufferSizeRangeOption); err != nil { return newError("failed to set tcp receive buffer size:", err) } return nil } } ================================================ FILE: app/tun/packetaddradaptar.go ================================================ package tun import ( "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/stack" "github.com/v2fly/v2ray-core/v5/app/tun/device" "github.com/v2fly/v2ray-core/v5/app/tun/tunsorter" ) func NewDeviceWithSorter(overlay device.Device, sorter *tunsorter.TunSorter) device.Device { return &packetAddrDevice{ Device: overlay, sorter: sorter, } } type packetAddrDevice struct { device.Device sorter *tunsorter.TunSorter secondaryDispatcher stack.NetworkDispatcher } func (p *packetAddrDevice) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { buf := pkt.ToBuffer() _, err := p.sorter.OnPacketReceived(buf.Flatten()) if err != nil { p.secondaryDispatcher.DeliverNetworkPacket(protocol, pkt) } } func (p *packetAddrDevice) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { // TODO implement me panic("implement me") } func (p *packetAddrDevice) Attach(dispatcher stack.NetworkDispatcher) { p.secondaryDispatcher = dispatcher p.Device.Attach(p) } ================================================ FILE: app/tun/packetparse/errors.generated.go ================================================ package packetparse import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/tun/packetparse/packetParse.go ================================================ package packetparse //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: app/tun/packetparse/udp.go ================================================ package packetparse import ( "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/v2fly/v2ray-core/v5/common/net" ) var ( errNotIPPacket = newError("not an IP packet") errNotUDPPacket = newError("not a UDP packet") ) var nullDestination = net.UnixDestination(net.DomainAddress("null")) func TryParseAsUDPPacket(packet []byte) (src, dst net.Destination, data []byte, err error) { parsedPacket := gopacket.NewPacket(packet, layers.LayerTypeIPv4, gopacket.DecodeOptions{ Lazy: true, NoCopy: false, SkipDecodeRecovery: false, DecodeStreamsAsDatagrams: false, }) var srcIP net.Address var dstIP net.Address ipv4Layer := parsedPacket.Layer(layers.LayerTypeIPv4) if ipv4Layer == nil { parsedPacketAsIPv6 := gopacket.NewPacket(packet, layers.LayerTypeIPv6, gopacket.DecodeOptions{ Lazy: true, NoCopy: false, SkipDecodeRecovery: false, DecodeStreamsAsDatagrams: false, }) ipv6Layer := parsedPacketAsIPv6.Layer(layers.LayerTypeIPv6) if ipv6Layer == nil { return nullDestination, nullDestination, nil, errNotIPPacket } ipv6 := ipv6Layer.(*layers.IPv6) srcIP = net.IPAddress(ipv6.SrcIP) dstIP = net.IPAddress(ipv6.DstIP) parsedPacket = parsedPacketAsIPv6 } else { ipv4 := ipv4Layer.(*layers.IPv4) srcIP = net.IPAddress(ipv4.SrcIP) dstIP = net.IPAddress(ipv4.DstIP) } udpLayer := parsedPacket.Layer(layers.LayerTypeUDP) if udpLayer == nil { return nullDestination, nullDestination, nil, errNotUDPPacket } udp := udpLayer.(*layers.UDP) srcPort := net.Port(udp.SrcPort) dstPort := net.Port(udp.DstPort) src = net.UDPDestination(srcIP, srcPort) dst = net.UDPDestination(dstIP, dstPort) data = udp.Payload return // nolint: nakedret } func TryConstructUDPPacket(src, dst net.Destination, data []byte) ([]byte, error) { if src.Address.Family().IsIPv4() && dst.Address.Family().IsIPv4() { return constructIPv4UDPPacket(src, dst, data) } if src.Address.Family().IsIPv6() && dst.Address.Family().IsIPv6() { return constructIPv6UDPPacket(src, dst, data) } return nil, newError("not supported") } func constructIPv4UDPPacket(src, dst net.Destination, data []byte) ([]byte, error) { ipv4 := &layers.IPv4{ Version: 4, Protocol: layers.IPProtocolUDP, SrcIP: src.Address.IP(), DstIP: dst.Address.IP(), TTL: 64, // set TTL to a reasonable non-zero value to allow non-local routing } udp := &layers.UDP{ SrcPort: layers.UDPPort(src.Port), DstPort: layers.UDPPort(dst.Port), } err := udp.SetNetworkLayerForChecksum(ipv4) if err != nil { return nil, err } buffer := gopacket.NewSerializeBuffer() if err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{ FixLengths: true, ComputeChecksums: true, }, ipv4, udp, gopacket.Payload(data)); err != nil { return nil, err } return buffer.Bytes(), nil } func constructIPv6UDPPacket(src, dst net.Destination, data []byte) ([]byte, error) { ipv6 := &layers.IPv6{ Version: 6, NextHeader: layers.IPProtocolUDP, SrcIP: src.Address.IP(), DstIP: dst.Address.IP(), HopLimit: 64, } udp := &layers.UDP{ SrcPort: layers.UDPPort(src.Port), DstPort: layers.UDPPort(dst.Port), } err := udp.SetNetworkLayerForChecksum(ipv6) if err != nil { return nil, err } buffer := gopacket.NewSerializeBuffer() if err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{ FixLengths: true, ComputeChecksums: true, }, ipv6, udp, gopacket.Payload(data)); err != nil { return nil, err } return buffer.Bytes(), nil } ================================================ FILE: app/tun/stack.go ================================================ package tun import ( "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/udp" ) type StackOption func(*stack.Stack) error func (t *TUN) CreateStack(linkedEndpoint stack.LinkEndpoint) (*stack.Stack, error) { s := stack.New(stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{ ipv4.NewProtocol, ipv6.NewProtocol, }, TransportProtocols: []stack.TransportProtocolFactory{ tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6, }, }) nicID := s.NextNICID() opts := []StackOption{ SetTCPHandler(t.ctx, t.dispatcher, t.policyManager, t.config), SetUDPHandler(t.ctx, t.dispatcher, t.policyManager, t.config), CreateNIC(nicID, linkedEndpoint), AddProtocolAddress(nicID, t.config.Ips), SetRouteTable(nicID, t.config.Routes), SetPromiscuousMode(nicID, t.config.EnablePromiscuousMode), SetSpoofing(nicID, t.config.EnableSpoofing), } if t.config.SocketSettings != nil { if size := t.config.SocketSettings.TxBufSize; size != 0 { opts = append(opts, SetTCPSendBufferSize(int(size))) } if size := t.config.SocketSettings.RxBufSize; size != 0 { opts = append(opts, SetTCPReceiveBufferSize(int(size))) } } for _, opt := range opts { if err := opt(s); err != nil { return nil, err } } return s, nil } ================================================ FILE: app/tun/tun.go ================================================ //go:build !confonly // +build !confonly package tun import ( "context" "gvisor.dev/gvisor/pkg/tcpip/stack" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/tun/device" "github.com/v2fly/v2ray-core/v5/app/tun/device/gvisor" "github.com/v2fly/v2ray-core/v5/app/tun/tunsorter" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type TUN struct { ctx context.Context dispatcher routing.Dispatcher policyManager policy.Manager config *Config stack *stack.Stack } func (t *TUN) Type() interface{} { return (*TUN)(nil) } func (t *TUN) Start() error { DeviceConstructor := gvisor.New tunDevice, err := DeviceConstructor(device.Options{ Name: t.config.Name, MTU: t.config.Mtu, }) if err != nil { return newError("failed to create device").Base(err).AtError() } if t.config.PacketEncoding != packetaddr.PacketAddrType_None { writer := device.NewLinkWriterToWriter(tunDevice) sorter := tunsorter.NewTunSorter(writer, t.dispatcher, t.config.PacketEncoding, t.ctx) tunDeviceLayered := NewDeviceWithSorter(tunDevice, sorter) tunDevice = tunDeviceLayered } stack, err := t.CreateStack(tunDevice) if err != nil { return newError("failed to create stack").Base(err).AtError() } t.stack = stack return nil } func (t *TUN) Close() error { if t.stack != nil { t.stack.Close() t.stack.Wait() } return nil } func (t *TUN) Init(ctx context.Context, config *Config, dispatcher routing.Dispatcher, policyManager policy.Manager) error { t.ctx = ctx t.config = config t.dispatcher = dispatcher t.policyManager = policyManager return nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { tun := new(TUN) err := core.RequireFeatures(ctx, func(d routing.Dispatcher, p policy.Manager) error { return tun.Init(ctx, config.(*Config), d, p) }) return tun, err })) } ================================================ FILE: app/tun/tunsorter/errors.generated.go ================================================ package tunsorter import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: app/tun/tunsorter/tunsorter.go ================================================ package tunsorter import ( "context" "io" "sync" "github.com/v2fly/v2ray-core/v5/app/tun/packetparse" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" vudp "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func NewTunSorter(tunWriter io.Writer, dispatcher routing.Dispatcher, packetAddrType packetaddr.PacketAddrType, ctx context.Context) *TunSorter { return &TunSorter{ tunWriter: tunWriter, dispatcher: dispatcher, packetAddrType: packetAddrType, ctx: ctx, } } type TunSorter struct { tunWriter io.Writer dispatcher routing.Dispatcher packetAddrType packetaddr.PacketAddrType trackedConnections sync.Map ctx context.Context } func (t *TunSorter) OnPacketReceived(b []byte) (n int, err error) { src, dst, data, err := packetparse.TryParseAsUDPPacket(b) if err != nil { return 0, err } conn := newTrackedUDPConnection(src, t) trackedConnection, loaded := t.trackedConnections.LoadOrStore(src.String(), conn) conn = trackedConnection.(*trackedUDPConnection) if !loaded { t.onNewConnection(conn) } conn.onNewPacket(dst, data) return len(b), nil } func (t *TunSorter) onNewConnection(connection *trackedUDPConnection) { udpDispatcherConstructor := udp.NewSplitDispatcher switch t.packetAddrType { // nolint: gocritic case packetaddr.PacketAddrType_Packet: ctx := context.WithValue(t.ctx, udp.DispatcherConnectionTerminationSignalReceiverMark, connection) // nolint:staticcheck packetAddrDispatcherFactory := udp.NewPacketAddrDispatcherCreator(ctx) udpDispatcherConstructor = packetAddrDispatcherFactory.NewPacketAddrDispatcher } udpDispatcher := udpDispatcherConstructor(t.dispatcher, func(ctx context.Context, packet *vudp.Packet) { connection.onWritePacket(packet.Source, packet.Payload.Bytes()) }) connection.packetDispatcher = udpDispatcher } func (t *TunSorter) onWritePacket(src net.Destination, dest net.Destination, data []byte) { data, err := packetparse.TryConstructUDPPacket(src, dest, data) if err != nil { newError("failed to construct udp packet").Base(err).WriteToLog() return } _, err = t.tunWriter.Write(data) if err != nil { newError("failed to write udp packet to tun").Base(err).WriteToLog() return } } func newTrackedUDPConnection(src net.Destination, tunSorter *TunSorter) *trackedUDPConnection { return &trackedUDPConnection{ tunSorter: tunSorter, src: src, } } type trackedUDPConnection struct { packetDispatcher udp.DispatcherI tunSorter *TunSorter src net.Destination } func (t *trackedUDPConnection) onNewPacket(dst net.Destination, data []byte) { t.packetDispatcher.Dispatch(context.Background(), dst, buf.FromBytes(data)) } func (t *trackedUDPConnection) onWritePacket(src net.Destination, data []byte) { t.tunSorter.onWritePacket(src, t.src, data) } func (t *trackedUDPConnection) Close() error { t.tunSorter.trackedConnections.Delete(t.src.String()) return nil } ================================================ FILE: common/antireplay/antireplay.go ================================================ package antireplay type GeneralizedReplayFilter interface { Interval() int64 Check(sum []byte) bool } ================================================ FILE: common/antireplay/bloomring.go ================================================ package antireplay import ( "sync" ss_bloomring "github.com/v2fly/ss-bloomring" ) type BloomRing struct { *ss_bloomring.BloomRing lock *sync.Mutex } func (b BloomRing) Interval() int64 { return 9999999 } func (b BloomRing) Check(sum []byte) bool { b.lock.Lock() defer b.lock.Unlock() if b.Test(sum) { return false } b.Add(sum) return true } func NewBloomRing() BloomRing { const ( DefaultSFCapacity = 1e6 // FalsePositiveRate DefaultSFFPR = 1e-6 DefaultSFSlot = 10 ) return BloomRing{ss_bloomring.NewBloomRing(DefaultSFSlot, DefaultSFCapacity, DefaultSFFPR), &sync.Mutex{}} } ================================================ FILE: common/antireplay/replayfilter.go ================================================ package antireplay import ( "sync" "time" cuckoo "github.com/seiflotfy/cuckoofilter" ) const replayFilterCapacity = 100000 // ReplayFilter checks for replay attacks. type ReplayFilter struct { lock sync.Mutex poolA *cuckoo.Filter poolB *cuckoo.Filter poolSwap bool lastSwap int64 interval int64 } // NewReplayFilter creates a new filter with specifying the expiration time interval in seconds. func NewReplayFilter(interval int64) *ReplayFilter { filter := &ReplayFilter{} filter.interval = interval return filter } // Interval in second for expiration time for duplicate records. func (filter *ReplayFilter) Interval() int64 { return filter.interval } // Check determines if there are duplicate records. func (filter *ReplayFilter) Check(sum []byte) bool { filter.lock.Lock() defer filter.lock.Unlock() now := time.Now().Unix() if filter.lastSwap == 0 { filter.lastSwap = now filter.poolA = cuckoo.NewFilter(replayFilterCapacity) filter.poolB = cuckoo.NewFilter(replayFilterCapacity) } elapsed := now - filter.lastSwap if elapsed >= filter.Interval() { if filter.poolSwap { filter.poolA.Reset() } else { filter.poolB.Reset() } filter.poolSwap = !filter.poolSwap filter.lastSwap = now } return filter.poolA.InsertUnique(sum) && filter.poolB.InsertUnique(sum) } ================================================ FILE: common/bitmask/byte.go ================================================ package bitmask // Byte is a bitmask in byte. type Byte byte // Has returns true if this bitmask contains another bitmask. func (b Byte) Has(bb Byte) bool { return (b & bb) != 0 } func (b *Byte) Set(bb Byte) { *b |= bb } func (b *Byte) Clear(bb Byte) { *b &= ^bb } func (b *Byte) Toggle(bb Byte) { *b ^= bb } ================================================ FILE: common/bitmask/byte_test.go ================================================ package bitmask_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/bitmask" ) func TestBitmaskByte(t *testing.T) { b := Byte(0) b.Set(Byte(1)) if !b.Has(1) { t.Fatal("expected ", b, " to contain 1, but actually not") } b.Set(Byte(2)) if !b.Has(2) { t.Fatal("expected ", b, " to contain 2, but actually not") } if !b.Has(1) { t.Fatal("expected ", b, " to contain 1, but actually not") } b.Clear(Byte(1)) if !b.Has(2) { t.Fatal("expected ", b, " to contain 2, but actually not") } if b.Has(1) { t.Fatal("expected ", b, " to not contain 1, but actually did") } b.Toggle(Byte(2)) if b.Has(2) { t.Fatal("expected ", b, " to not contain 2, but actually did") } } ================================================ FILE: common/buf/buf.go ================================================ // Package buf provides a light-weight memory allocation mechanism. package buf //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: common/buf/buffer.go ================================================ package buf import ( "io" "github.com/v2fly/v2ray-core/v5/common/bytespool" ) const ( // Size of a regular buffer. Size = 2048 ) var pool = bytespool.GetPool(Size) // ownership represents the data owner of the buffer. type ownership uint8 const ( managed ownership = 0 unmanaged ownership = 1 bytespools ownership = 2 ) // Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles // the buffer into an internal buffer pool, in order to recreate a buffer more // quickly. type Buffer struct { v []byte start int32 end int32 ownership ownership } // New creates a Buffer with 0 length and 2K capacity. func New() *Buffer { return &Buffer{ v: pool.Get().([]byte), } } // NewWithSize creates a Buffer with 0 length and capacity with at least the given size. func NewWithSize(size int32) *Buffer { return &Buffer{ v: bytespool.Alloc(size), ownership: bytespools, } } // FromBytes creates a Buffer with an existed bytearray func FromBytes(data []byte) *Buffer { return &Buffer{ v: data, end: int32(len(data)), ownership: unmanaged, } } // StackNew creates a new Buffer object on stack. // This method is for buffers that is released in the same function. func StackNew() Buffer { return Buffer{ v: pool.Get().([]byte), } } // Release recycles the buffer into an internal buffer pool. func (b *Buffer) Release() { if b == nil || b.v == nil || b.ownership == unmanaged { return } p := b.v b.v = nil b.Clear() switch b.ownership { case managed: pool.Put(p) // nolint: staticcheck case bytespools: bytespool.Free(p) // nolint: staticcheck } } // Clear clears the content of the buffer, results an empty buffer with // Len() = 0. func (b *Buffer) Clear() { b.start = 0 b.end = 0 } // Byte returns the bytes at index. func (b *Buffer) Byte(index int32) byte { return b.v[b.start+index] } // SetByte sets the byte value at index. func (b *Buffer) SetByte(index int32, value byte) { b.v[b.start+index] = value } // Bytes returns the content bytes of this Buffer. func (b *Buffer) Bytes() []byte { return b.v[b.start:b.end] } // Extend increases the buffer size by n bytes, and returns the extended part. // It panics if result size is larger than buf.Size. func (b *Buffer) Extend(n int32) []byte { end := b.end + n if end > int32(len(b.v)) { panic("extending out of bound") } ext := b.v[b.end:end] b.end = end return ext } // BytesRange returns a slice of this buffer with given from and to boundary. func (b *Buffer) BytesRange(from, to int32) []byte { if from < 0 { from += b.Len() } if to < 0 { to += b.Len() } return b.v[b.start+from : b.start+to] } // BytesFrom returns a slice of this Buffer starting from the given position. func (b *Buffer) BytesFrom(from int32) []byte { if from < 0 { from += b.Len() } return b.v[b.start+from : b.end] } // BytesTo returns a slice of this Buffer from start to the given position. func (b *Buffer) BytesTo(to int32) []byte { if to < 0 { to += b.Len() } return b.v[b.start : b.start+to] } // Resize cuts the buffer at the given position. func (b *Buffer) Resize(from, to int32) { if from < 0 { from += b.Len() } if to < 0 { to += b.Len() } if to < from { panic("Invalid slice") } b.end = b.start + to b.start += from } // Advance cuts the buffer at the given position. func (b *Buffer) Advance(from int32) { if from < 0 { from += b.Len() } b.start += from } // Len returns the length of the buffer content. func (b *Buffer) Len() int32 { if b == nil { return 0 } return b.end - b.start } // Cap returns the capacity of the buffer content. func (b *Buffer) Cap() int32 { if b == nil { return 0 } return int32(len(b.v)) } // IsEmpty returns true if the buffer is empty. func (b *Buffer) IsEmpty() bool { return b.Len() == 0 } // IsFull returns true if the buffer has no more room to grow. func (b *Buffer) IsFull() bool { return b != nil && b.end == int32(len(b.v)) } // Write implements Write method in io.Writer. func (b *Buffer) Write(data []byte) (int, error) { nBytes := copy(b.v[b.end:], data) b.end += int32(nBytes) return nBytes, nil } // WriteByte writes a single byte into the buffer. func (b *Buffer) WriteByte(v byte) error { if b.IsFull() { return newError("buffer full") } b.v[b.end] = v b.end++ return nil } // WriteString implements io.StringWriter. func (b *Buffer) WriteString(s string) (int, error) { return b.Write([]byte(s)) } // ReadByte implements io.ByteReader func (b *Buffer) ReadByte() (byte, error) { if b.start == b.end { return 0, io.EOF } nb := b.v[b.start] b.start++ return nb, nil } // ReadBytes implements bufio.Reader.ReadBytes func (b *Buffer) ReadBytes(length int32) ([]byte, error) { if b.end-b.start < length { return nil, io.EOF } nb := b.v[b.start : b.start+length] b.start += length return nb, nil } // Read implements io.Reader.Read(). func (b *Buffer) Read(data []byte) (int, error) { if b.Len() == 0 { return 0, io.EOF } nBytes := copy(data, b.v[b.start:b.end]) if int32(nBytes) == b.Len() { b.Clear() } else { b.start += int32(nBytes) } return nBytes, nil } // ReadFrom implements io.ReaderFrom. func (b *Buffer) ReadFrom(reader io.Reader) (int64, error) { n, err := reader.Read(b.v[b.end:]) b.end += int32(n) return int64(n), err } // ReadFullFrom reads exact size of bytes from given reader, or until error occurs. func (b *Buffer) ReadFullFrom(reader io.Reader, size int32) (int64, error) { end := b.end + size if end > int32(len(b.v)) { v := end return 0, newError("out of bound: ", v) } n, err := io.ReadFull(reader, b.v[b.end:end]) b.end += int32(n) return int64(n), err } // String returns the string form of this Buffer. func (b *Buffer) String() string { return string(b.Bytes()) } ================================================ FILE: common/buf/buffer_test.go ================================================ package buf_test import ( "bytes" "crypto/rand" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/buf" ) func TestBufferClear(t *testing.T) { buffer := New() defer buffer.Release() payload := "Bytes" buffer.Write([]byte(payload)) if diff := cmp.Diff(buffer.Bytes(), []byte(payload)); diff != "" { t.Error(diff) } buffer.Clear() if buffer.Len() != 0 { t.Error("expect 0 length, but got ", buffer.Len()) } } func TestBufferIsEmpty(t *testing.T) { buffer := New() defer buffer.Release() if buffer.IsEmpty() != true { t.Error("expect empty buffer, but not") } } func TestBufferString(t *testing.T) { buffer := New() defer buffer.Release() const payload = "Test String" common.Must2(buffer.WriteString(payload)) if buffer.String() != payload { t.Error("expect buffer content as ", payload, " but actually ", buffer.String()) } } func TestBufferByte(t *testing.T) { { buffer := New() common.Must(buffer.WriteByte('m')) if buffer.String() != "m" { t.Error("expect buffer content as ", "m", " but actually ", buffer.String()) } buffer.Release() } { buffer := StackNew() common.Must(buffer.WriteByte('n')) if buffer.String() != "n" { t.Error("expect buffer content as ", "n", " but actually ", buffer.String()) } buffer.Release() } { buffer := StackNew() common.Must2(buffer.WriteString("HELLOWORLD")) if b := buffer.Byte(5); b != 'W' { t.Error("unexpected byte ", b) } buffer.SetByte(5, 'M') if buffer.String() != "HELLOMORLD" { t.Error("expect buffer content as ", "n", " but actually ", buffer.String()) } buffer.Release() } } func TestBufferResize(t *testing.T) { buffer := New() defer buffer.Release() const payload = "Test String" common.Must2(buffer.WriteString(payload)) if buffer.String() != payload { t.Error("expect buffer content as ", payload, " but actually ", buffer.String()) } buffer.Resize(-6, -3) if l := buffer.Len(); int(l) != 3 { t.Error("len error ", l) } if s := buffer.String(); s != "Str" { t.Error("unexpect buffer ", s) } buffer.Resize(int32(len(payload)), 200) if l := buffer.Len(); int(l) != 200-len(payload) { t.Error("len error ", l) } } func TestBufferSlice(t *testing.T) { { b := New() common.Must2(b.Write([]byte("abcd"))) bytes := b.BytesFrom(-2) if diff := cmp.Diff(bytes, []byte{'c', 'd'}); diff != "" { t.Error(diff) } } { b := New() common.Must2(b.Write([]byte("abcd"))) bytes := b.BytesTo(-2) if diff := cmp.Diff(bytes, []byte{'a', 'b'}); diff != "" { t.Error(diff) } } { b := New() common.Must2(b.Write([]byte("abcd"))) bytes := b.BytesRange(-3, -1) if diff := cmp.Diff(bytes, []byte{'b', 'c'}); diff != "" { t.Error(diff) } } } func TestBufferReadFullFrom(t *testing.T) { payload := make([]byte, 1024) common.Must2(rand.Read(payload)) reader := bytes.NewReader(payload) b := New() n, err := b.ReadFullFrom(reader, 1024) common.Must(err) if n != 1024 { t.Error("expect reading 1024 bytes, but actually ", n) } if diff := cmp.Diff(payload, b.Bytes()); diff != "" { t.Error(diff) } } func BenchmarkNewBuffer(b *testing.B) { for i := 0; i < b.N; i++ { buffer := New() buffer.Release() } } func BenchmarkNewBufferStack(b *testing.B) { for i := 0; i < b.N; i++ { buffer := StackNew() buffer.Release() } } func BenchmarkWrite2(b *testing.B) { buffer := New() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = buffer.Write([]byte{'a', 'b'}) buffer.Clear() } } func BenchmarkWrite8(b *testing.B) { buffer := New() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = buffer.Write([]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}) buffer.Clear() } } func BenchmarkWrite32(b *testing.B) { buffer := New() payload := make([]byte, 32) rand.Read(payload) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = buffer.Write(payload) buffer.Clear() } } func BenchmarkWriteByte2(b *testing.B) { buffer := New() b.ResetTimer() for i := 0; i < b.N; i++ { _ = buffer.WriteByte('a') _ = buffer.WriteByte('b') buffer.Clear() } } func BenchmarkWriteByte8(b *testing.B) { buffer := New() b.ResetTimer() for i := 0; i < b.N; i++ { _ = buffer.WriteByte('a') _ = buffer.WriteByte('b') _ = buffer.WriteByte('c') _ = buffer.WriteByte('d') _ = buffer.WriteByte('e') _ = buffer.WriteByte('f') _ = buffer.WriteByte('g') _ = buffer.WriteByte('h') buffer.Clear() } } ================================================ FILE: common/buf/copy.go ================================================ package buf import ( "io" "time" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/signal" ) type dataHandler func(MultiBuffer) type copyHandler struct { onData []dataHandler } // SizeCounter is for counting bytes copied by Copy(). type SizeCounter struct { Size int64 } // CopyOption is an option for copying data. type CopyOption func(*copyHandler) // UpdateActivity is a CopyOption to update activity on each data copy operation. func UpdateActivity(timer signal.ActivityUpdater) CopyOption { return func(handler *copyHandler) { handler.onData = append(handler.onData, func(MultiBuffer) { timer.Update() }) } } // CountSize is a CopyOption that sums the total size of data copied into the given SizeCounter. func CountSize(sc *SizeCounter) CopyOption { return func(handler *copyHandler) { handler.onData = append(handler.onData, func(b MultiBuffer) { sc.Size += int64(b.Len()) }) } } type readError struct { error } func (e readError) Error() string { return e.error.Error() } func (e readError) Inner() error { return e.error } // IsReadError returns true if the error in Copy() comes from reading. func IsReadError(err error) bool { _, ok := err.(readError) return ok } type writeError struct { error } func (e writeError) Error() string { return e.error.Error() } func (e writeError) Inner() error { return e.error } // IsWriteError returns true if the error in Copy() comes from writing. func IsWriteError(err error) bool { _, ok := err.(writeError) return ok } func copyInternal(reader Reader, writer Writer, handler *copyHandler) error { for { buffer, err := reader.ReadMultiBuffer() if !buffer.IsEmpty() { for _, handler := range handler.onData { handler(buffer) } if werr := writer.WriteMultiBuffer(buffer); werr != nil { return writeError{werr} } } if err != nil { return readError{err} } } } // Copy dumps all payload from reader to writer or stops when an error occurs. It returns nil when EOF. func Copy(reader Reader, writer Writer, options ...CopyOption) error { var handler copyHandler for _, option := range options { option(&handler) } err := copyInternal(reader, writer, &handler) if err != nil && errors.Cause(err) != io.EOF { return err } return nil } var ErrNotTimeoutReader = newError("not a TimeoutReader") func CopyOnceTimeout(reader Reader, writer Writer, timeout time.Duration) error { timeoutReader, ok := reader.(TimeoutReader) if !ok { return ErrNotTimeoutReader } mb, err := timeoutReader.ReadMultiBufferTimeout(timeout) if err != nil { return err } return writer.WriteMultiBuffer(mb) } ================================================ FILE: common/buf/copy_test.go ================================================ package buf_test import ( "crypto/rand" "io" "testing" "github.com/golang/mock/gomock" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/testing/mocks" ) func TestReadError(t *testing.T) { mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockReader := mocks.NewReader(mockCtl) mockReader.EXPECT().Read(gomock.Any()).Return(0, errors.New("error")) err := buf.Copy(buf.NewReader(mockReader), buf.Discard) if err == nil { t.Fatal("expected error, but nil") } if !buf.IsReadError(err) { t.Error("expected to be ReadError, but not") } if err.Error() != "error" { t.Fatal("unexpected error message: ", err.Error()) } } func TestWriteError(t *testing.T) { mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockWriter := mocks.NewWriter(mockCtl) mockWriter.EXPECT().Write(gomock.Any()).Return(0, errors.New("error")) err := buf.Copy(buf.NewReader(rand.Reader), buf.NewWriter(mockWriter)) if err == nil { t.Fatal("expected error, but nil") } if !buf.IsWriteError(err) { t.Error("expected to be WriteError, but not") } if err.Error() != "error" { t.Fatal("unexpected error message: ", err.Error()) } } type TestReader struct{} func (TestReader) Read(b []byte) (int, error) { return len(b), nil } func BenchmarkCopy(b *testing.B) { reader := buf.NewReader(io.LimitReader(TestReader{}, 10240)) writer := buf.Discard b.ResetTimer() for i := 0; i < b.N; i++ { _ = buf.Copy(reader, writer) } } ================================================ FILE: common/buf/errors.generated.go ================================================ package buf import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/buf/io.go ================================================ package buf import ( "io" "net" "os" "syscall" "time" ) // Reader extends io.Reader with MultiBuffer. type Reader interface { // ReadMultiBuffer reads content from underlying reader, and put it into a MultiBuffer. ReadMultiBuffer() (MultiBuffer, error) } // ErrReadTimeout is an error that happens with IO timeout. var ErrReadTimeout = newError("IO timeout") // TimeoutReader is a reader that returns error if Read() operation takes longer than the given timeout. type TimeoutReader interface { ReadMultiBufferTimeout(time.Duration) (MultiBuffer, error) } // Writer extends io.Writer with MultiBuffer. type Writer interface { // WriteMultiBuffer writes a MultiBuffer into underlying writer. // Caller relinquish the ownership of MultiBuffer after calling this method. WriteMultiBuffer(MultiBuffer) error } // WriteAllBytes ensures all bytes are written into the given writer. func WriteAllBytes(writer io.Writer, payload []byte) error { for len(payload) > 0 { n, err := writer.Write(payload) if err != nil { return err } payload = payload[n:] } return nil } func isPacketReader(reader io.Reader) bool { _, ok := reader.(net.PacketConn) return ok } // NewReader creates a new Reader. // The Reader instance doesn't take the ownership of reader. func NewReader(reader io.Reader) Reader { if mr, ok := reader.(Reader); ok { return mr } if isPacketReader(reader) { return &PacketReader{ Reader: reader, } } _, isFile := reader.(*os.File) if !isFile && useReadv { if sc, ok := reader.(syscall.Conn); ok { rawConn, err := sc.SyscallConn() if err != nil { newError("failed to get sysconn").Base(err).WriteToLog() } else { return NewReadVReader(reader, rawConn) } } } return &SingleReader{ Reader: reader, } } // NewPacketReader creates a new PacketReader based on the given reader. func NewPacketReader(reader io.Reader) Reader { if mr, ok := reader.(Reader); ok { return mr } return &PacketReader{ Reader: reader, } } func isPacketWriter(writer io.Writer) bool { if _, ok := writer.(net.PacketConn); ok { return true } // If the writer doesn't implement syscall.Conn, it is probably not a TCP connection. if _, ok := writer.(syscall.Conn); !ok { return true } return false } // NewWriter creates a new Writer. func NewWriter(writer io.Writer) Writer { if mw, ok := writer.(Writer); ok { return mw } if isPacketWriter(writer) { return &SequentialWriter{ Writer: writer, } } return &BufferToBytesWriter{ Writer: writer, } } ================================================ FILE: common/buf/io_test.go ================================================ package buf_test import ( "crypto/tls" "io" "testing" . "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" ) func TestWriterCreation(t *testing.T) { tcpServer := tcp.Server{} dest, err := tcpServer.Start() if err != nil { t.Fatal("failed to start tcp server: ", err) } defer tcpServer.Close() conn, err := net.Dial("tcp", dest.NetAddr()) if err != nil { t.Fatal("failed to dial a TCP connection: ", err) } defer conn.Close() { writer := NewWriter(conn) if _, ok := writer.(*BufferToBytesWriter); !ok { t.Fatal("writer is not a BufferToBytesWriter") } writer2 := NewWriter(writer.(io.Writer)) if writer2 != writer { t.Fatal("writer is not reused") } } tlsConn := tls.Client(conn, &tls.Config{ InsecureSkipVerify: true, }) defer tlsConn.Close() { writer := NewWriter(tlsConn) if _, ok := writer.(*SequentialWriter); !ok { t.Fatal("writer is not a SequentialWriter") } } } ================================================ FILE: common/buf/multi_buffer.go ================================================ package buf import ( "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/serial" ) // ReadAllToBytes reads all content from the reader into a byte array, until EOF. func ReadAllToBytes(reader io.Reader) ([]byte, error) { mb, err := ReadFrom(reader) if err != nil { return nil, err } if mb.Len() == 0 { return nil, nil } b := make([]byte, mb.Len()) mb, _ = SplitBytes(mb, b) ReleaseMulti(mb) return b, nil } // MultiBuffer is a list of Buffers. The order of Buffer matters. type MultiBuffer []*Buffer // MergeMulti merges content from src to dest, and returns the new address of dest and src func MergeMulti(dest MultiBuffer, src MultiBuffer) (MultiBuffer, MultiBuffer) { dest = append(dest, src...) for idx := range src { src[idx] = nil } return dest, src[:0] } // MergeBytes merges the given bytes into MultiBuffer and return the new address of the merged MultiBuffer. func MergeBytes(dest MultiBuffer, src []byte) MultiBuffer { n := len(dest) if n > 0 && !(dest)[n-1].IsFull() { nBytes, _ := (dest)[n-1].Write(src) src = src[nBytes:] } for len(src) > 0 { b := New() nBytes, _ := b.Write(src) src = src[nBytes:] dest = append(dest, b) } return dest } // ReleaseMulti releases all content of the MultiBuffer, and returns an empty MultiBuffer. func ReleaseMulti(mb MultiBuffer) MultiBuffer { for i := range mb { mb[i].Release() mb[i] = nil } return mb[:0] } // Copy copied the beginning part of the MultiBuffer into the given byte array. func (mb MultiBuffer) Copy(b []byte) int { total := 0 for _, bb := range mb { nBytes := copy(b[total:], bb.Bytes()) total += nBytes if int32(nBytes) < bb.Len() { break } } return total } // ReadFrom reads all content from reader until EOF. func ReadFrom(reader io.Reader) (MultiBuffer, error) { mb := make(MultiBuffer, 0, 16) for { b := New() _, err := b.ReadFullFrom(reader, Size) if b.IsEmpty() { b.Release() } else { mb = append(mb, b) } if err != nil { if errors.Cause(err) == io.EOF || errors.Cause(err) == io.ErrUnexpectedEOF { return mb, nil } return mb, err } } } // SplitBytes splits the given amount of bytes from the beginning of the MultiBuffer. // It returns the new address of MultiBuffer leftover, and number of bytes written into the input byte slice. func SplitBytes(mb MultiBuffer, b []byte) (MultiBuffer, int) { totalBytes := 0 endIndex := -1 for i := range mb { pBuffer := mb[i] nBytes, _ := pBuffer.Read(b) totalBytes += nBytes b = b[nBytes:] if !pBuffer.IsEmpty() { endIndex = i break } pBuffer.Release() mb[i] = nil } if endIndex == -1 { mb = mb[:0] } else { mb = mb[endIndex:] } return mb, totalBytes } // SplitFirstBytes splits the first buffer from MultiBuffer, and then copy its content into the given slice. func SplitFirstBytes(mb MultiBuffer, p []byte) (MultiBuffer, int) { mb, b := SplitFirst(mb) if b == nil { return mb, 0 } n := copy(p, b.Bytes()) b.Release() return mb, n } // Compact returns another MultiBuffer by merging all content of the given one together. func Compact(mb MultiBuffer) MultiBuffer { if len(mb) == 0 { return mb } mb2 := make(MultiBuffer, 0, len(mb)) last := mb[0] for i := 1; i < len(mb); i++ { curr := mb[i] if last.Len()+curr.Len() > Size { mb2 = append(mb2, last) last = curr } else { common.Must2(last.ReadFrom(curr)) curr.Release() } } mb2 = append(mb2, last) return mb2 } // SplitFirst splits the first Buffer from the beginning of the MultiBuffer. func SplitFirst(mb MultiBuffer) (MultiBuffer, *Buffer) { if len(mb) == 0 { return mb, nil } b := mb[0] mb[0] = nil mb = mb[1:] return mb, b } // SplitSize splits the beginning of the MultiBuffer into another one, for at most size bytes. func SplitSize(mb MultiBuffer, size int32) (MultiBuffer, MultiBuffer) { if len(mb) == 0 { return mb, nil } if mb[0].Len() > size { b := New() copy(b.Extend(size), mb[0].BytesTo(size)) mb[0].Advance(size) return mb, MultiBuffer{b} } totalBytes := int32(0) var r MultiBuffer endIndex := -1 for i := range mb { if totalBytes+mb[i].Len() > size { endIndex = i break } totalBytes += mb[i].Len() r = append(r, mb[i]) mb[i] = nil } if endIndex == -1 { // To reuse mb array mb = mb[:0] } else { mb = mb[endIndex:] } return mb, r } // WriteMultiBuffer writes all buffers from the MultiBuffer to the Writer one by one, and return error if any, with leftover MultiBuffer. func WriteMultiBuffer(writer io.Writer, mb MultiBuffer) (MultiBuffer, error) { for { mb2, b := SplitFirst(mb) mb = mb2 if b == nil { break } _, err := writer.Write(b.Bytes()) b.Release() if err != nil { return mb, err } } return nil, nil } // Len returns the total number of bytes in the MultiBuffer. func (mb MultiBuffer) Len() int32 { if mb == nil { return 0 } size := int32(0) for _, b := range mb { size += b.Len() } return size } // IsEmpty returns true if the MultiBuffer has no content. func (mb MultiBuffer) IsEmpty() bool { for _, b := range mb { if !b.IsEmpty() { return false } } return true } // String returns the content of the MultiBuffer in string. func (mb MultiBuffer) String() string { v := make([]interface{}, len(mb)) for i, b := range mb { v[i] = b } return serial.Concat(v...) } // MultiBufferContainer is a ReadWriteCloser wrapper over MultiBuffer. type MultiBufferContainer struct { MultiBuffer } // Read implements io.Reader. func (c *MultiBufferContainer) Read(b []byte) (int, error) { if c.MultiBuffer.IsEmpty() { return 0, io.EOF } mb, nBytes := SplitBytes(c.MultiBuffer, b) c.MultiBuffer = mb return nBytes, nil } // ReadMultiBuffer implements Reader. func (c *MultiBufferContainer) ReadMultiBuffer() (MultiBuffer, error) { mb := c.MultiBuffer c.MultiBuffer = nil return mb, nil } // Write implements io.Writer. func (c *MultiBufferContainer) Write(b []byte) (int, error) { c.MultiBuffer = MergeBytes(c.MultiBuffer, b) return len(b), nil } // WriteMultiBuffer implements Writer. func (c *MultiBufferContainer) WriteMultiBuffer(b MultiBuffer) error { mb, _ := MergeMulti(c.MultiBuffer, b) c.MultiBuffer = mb return nil } // Close implements io.Closer. func (c *MultiBufferContainer) Close() error { c.MultiBuffer = ReleaseMulti(c.MultiBuffer) return nil } ================================================ FILE: common/buf/multi_buffer_test.go ================================================ package buf_test import ( "bytes" "crypto/rand" "io" "os" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/buf" ) func TestMultiBufferRead(t *testing.T) { b1 := New() common.Must2(b1.WriteString("ab")) b2 := New() common.Must2(b2.WriteString("cd")) mb := MultiBuffer{b1, b2} bs := make([]byte, 32) _, nBytes := SplitBytes(mb, bs) if nBytes != 4 { t.Error("expect 4 bytes split, but got ", nBytes) } if r := cmp.Diff(bs[:nBytes], []byte("abcd")); r != "" { t.Error(r) } } func TestMultiBufferAppend(t *testing.T) { var mb MultiBuffer b := New() common.Must2(b.WriteString("ab")) mb = append(mb, b) if mb.Len() != 2 { t.Error("expected length 2, but got ", mb.Len()) } } func TestMultiBufferSliceBySizeLarge(t *testing.T) { lb := make([]byte, 8*1024) common.Must2(io.ReadFull(rand.Reader, lb)) mb := MergeBytes(nil, lb) mb, mb2 := SplitSize(mb, 1024) if mb2.Len() != 1024 { t.Error("expect length 1024, but got ", mb2.Len()) } if mb.Len() != 7*1024 { t.Error("expect length 7*1024, but got ", mb.Len()) } mb, mb3 := SplitSize(mb, 7*1024) if mb3.Len() != 7*1024 { t.Error("expect length 7*1024, but got", mb.Len()) } if !mb.IsEmpty() { t.Error("expect empty buffer, but got ", mb.Len()) } } func TestMultiBufferSplitFirst(t *testing.T) { b1 := New() b1.WriteString("b1") b2 := New() b2.WriteString("b2") b3 := New() b3.WriteString("b3") var mb MultiBuffer mb = append(mb, b1, b2, b3) mb, c1 := SplitFirst(mb) if diff := cmp.Diff(b1.String(), c1.String()); diff != "" { t.Error(diff) } mb, c2 := SplitFirst(mb) if diff := cmp.Diff(b2.String(), c2.String()); diff != "" { t.Error(diff) } mb, c3 := SplitFirst(mb) if diff := cmp.Diff(b3.String(), c3.String()); diff != "" { t.Error(diff) } if !mb.IsEmpty() { t.Error("expect empty buffer, but got ", mb.String()) } } func TestMultiBufferReadAllToByte(t *testing.T) { { lb := make([]byte, 8*1024) common.Must2(io.ReadFull(rand.Reader, lb)) rd := bytes.NewBuffer(lb) b, err := ReadAllToBytes(rd) common.Must(err) if l := len(b); l != 8*1024 { t.Error("unexpected length from ReadAllToBytes", l) } } { const dat = "data/test_MultiBufferReadAllToByte.dat" f, err := os.Open(dat) common.Must(err) buf2, err := ReadAllToBytes(f) common.Must(err) f.Close() cnt, err := os.ReadFile(dat) common.Must(err) if d := cmp.Diff(buf2, cnt); d != "" { t.Error("fail to read from file: ", d) } } } func TestMultiBufferCopy(t *testing.T) { lb := make([]byte, 8*1024) common.Must2(io.ReadFull(rand.Reader, lb)) reader := bytes.NewBuffer(lb) mb, err := ReadFrom(reader) common.Must(err) lbdst := make([]byte, 8*1024) mb.Copy(lbdst) if d := cmp.Diff(lb, lbdst); d != "" { t.Error("unexpected different from MultiBufferCopy ", d) } } func TestSplitFirstBytes(t *testing.T) { a := New() common.Must2(a.WriteString("ab")) b := New() common.Must2(b.WriteString("bc")) mb := MultiBuffer{a, b} o := make([]byte, 2) _, cnt := SplitFirstBytes(mb, o) if cnt != 2 { t.Error("unexpected cnt from SplitFirstBytes ", cnt) } if d := cmp.Diff(string(o), "ab"); d != "" { t.Error("unexpected splited result from SplitFirstBytes ", d) } } func TestCompact(t *testing.T) { a := New() common.Must2(a.WriteString("ab")) b := New() common.Must2(b.WriteString("bc")) mb := MultiBuffer{a, b} cmb := Compact(mb) if w := cmb.String(); w != "abbc" { t.Error("unexpected Compact result ", w) } } func BenchmarkSplitBytes(b *testing.B) { var mb MultiBuffer raw := make([]byte, Size) b.ResetTimer() for i := 0; i < b.N; i++ { buffer := StackNew() buffer.Extend(Size) mb = append(mb, &buffer) mb, _ = SplitBytes(mb, raw) } } ================================================ FILE: common/buf/reader.go ================================================ package buf import ( "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" ) func readOneUDP(r io.Reader) (*Buffer, error) { b := New() for i := 0; i < 64; i++ { _, err := b.ReadFrom(r) if !b.IsEmpty() { return b, nil } if err != nil { b.Release() return nil, err } } b.Release() return nil, newError("Reader returns too many empty payloads.") } // ReadBuffer reads a Buffer from the given reader. func ReadBuffer(r io.Reader) (*Buffer, error) { b := New() n, err := b.ReadFrom(r) if n > 0 { return b, err } b.Release() return nil, err } // BufferedReader is a Reader that keeps its internal buffer. type BufferedReader struct { // Reader is the underlying reader to be read from Reader Reader // Buffer is the internal buffer to be read from first Buffer MultiBuffer // Spliter is a function to read bytes from MultiBuffer Spliter func(MultiBuffer, []byte) (MultiBuffer, int) } // BufferedBytes returns the number of bytes that is cached in this reader. func (r *BufferedReader) BufferedBytes() int32 { return r.Buffer.Len() } // ReadByte implements io.ByteReader. func (r *BufferedReader) ReadByte() (byte, error) { var b [1]byte _, err := r.Read(b[:]) return b[0], err } // Read implements io.Reader. It reads from internal buffer first (if available) and then reads from the underlying reader. func (r *BufferedReader) Read(b []byte) (int, error) { spliter := r.Spliter if spliter == nil { spliter = SplitBytes } if !r.Buffer.IsEmpty() { buffer, nBytes := spliter(r.Buffer, b) r.Buffer = buffer if r.Buffer.IsEmpty() { r.Buffer = nil } return nBytes, nil } mb, err := r.Reader.ReadMultiBuffer() if err != nil { return 0, err } mb, nBytes := spliter(mb, b) if !mb.IsEmpty() { r.Buffer = mb } return nBytes, nil } // ReadMultiBuffer implements Reader. func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) { if !r.Buffer.IsEmpty() { mb := r.Buffer r.Buffer = nil return mb, nil } return r.Reader.ReadMultiBuffer() } // ReadAtMost returns a MultiBuffer with at most size. func (r *BufferedReader) ReadAtMost(size int32) (MultiBuffer, error) { if r.Buffer.IsEmpty() { mb, err := r.Reader.ReadMultiBuffer() if mb.IsEmpty() && err != nil { return nil, err } r.Buffer = mb } rb, mb := SplitSize(r.Buffer, size) r.Buffer = rb if r.Buffer.IsEmpty() { r.Buffer = nil } return mb, nil } func (r *BufferedReader) writeToInternal(writer io.Writer) (int64, error) { mbWriter := NewWriter(writer) var sc SizeCounter if r.Buffer != nil { sc.Size = int64(r.Buffer.Len()) if err := mbWriter.WriteMultiBuffer(r.Buffer); err != nil { return 0, err } r.Buffer = nil } err := Copy(r.Reader, mbWriter, CountSize(&sc)) return sc.Size, err } // WriteTo implements io.WriterTo. func (r *BufferedReader) WriteTo(writer io.Writer) (int64, error) { nBytes, err := r.writeToInternal(writer) if errors.Cause(err) == io.EOF { return nBytes, nil } return nBytes, err } // Interrupt implements common.Interruptible. func (r *BufferedReader) Interrupt() { common.Interrupt(r.Reader) } // Close implements io.Closer. func (r *BufferedReader) Close() error { return common.Close(r.Reader) } // SingleReader is a Reader that read one Buffer every time. type SingleReader struct { io.Reader } // ReadMultiBuffer implements Reader. func (r *SingleReader) ReadMultiBuffer() (MultiBuffer, error) { b, err := ReadBuffer(r.Reader) return MultiBuffer{b}, err } // PacketReader is a Reader that read one Buffer every time. type PacketReader struct { io.Reader } // ReadMultiBuffer implements Reader. func (r *PacketReader) ReadMultiBuffer() (MultiBuffer, error) { b, err := readOneUDP(r.Reader) if err != nil { return nil, err } return MultiBuffer{b}, nil } ================================================ FILE: common/buf/reader_test.go ================================================ package buf_test import ( "bytes" "io" "strings" "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) func TestBytesReaderWriteTo(t *testing.T) { pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024)) reader := &BufferedReader{Reader: pReader} b1 := New() b1.WriteString("abc") b2 := New() b2.WriteString("efg") common.Must(pWriter.WriteMultiBuffer(MultiBuffer{b1, b2})) pWriter.Close() pReader2, pWriter2 := pipe.New(pipe.WithSizeLimit(1024)) writer := NewBufferedWriter(pWriter2) writer.SetBuffered(false) nBytes, err := io.Copy(writer, reader) common.Must(err) if nBytes != 6 { t.Error("copy: ", nBytes) } mb, err := pReader2.ReadMultiBuffer() common.Must(err) if s := mb.String(); s != "abcefg" { t.Error("content: ", s) } } func TestBytesReaderMultiBuffer(t *testing.T) { pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024)) reader := &BufferedReader{Reader: pReader} b1 := New() b1.WriteString("abc") b2 := New() b2.WriteString("efg") common.Must(pWriter.WriteMultiBuffer(MultiBuffer{b1, b2})) pWriter.Close() mbReader := NewReader(reader) mb, err := mbReader.ReadMultiBuffer() common.Must(err) if s := mb.String(); s != "abcefg" { t.Error("content: ", s) } } func TestReadByte(t *testing.T) { sr := strings.NewReader("abcd") reader := &BufferedReader{ Reader: NewReader(sr), } b, err := reader.ReadByte() common.Must(err) if b != 'a' { t.Error("unexpected byte: ", b, " want a") } if reader.BufferedBytes() != 3 { // 3 bytes left in buffer t.Error("unexpected buffered Bytes: ", reader.BufferedBytes()) } nBytes, err := reader.WriteTo(DiscardBytes) common.Must(err) if nBytes != 3 { t.Error("unexpect bytes written: ", nBytes) } } func TestReadBuffer(t *testing.T) { { sr := strings.NewReader("abcd") buf, err := ReadBuffer(sr) common.Must(err) if s := buf.String(); s != "abcd" { t.Error("unexpected str: ", s, " want abcd") } buf.Release() } } func TestReadAtMost(t *testing.T) { sr := strings.NewReader("abcd") reader := &BufferedReader{ Reader: NewReader(sr), } mb, err := reader.ReadAtMost(3) common.Must(err) if s := mb.String(); s != "abc" { t.Error("unexpected read result: ", s) } nBytes, err := reader.WriteTo(DiscardBytes) common.Must(err) if nBytes != 1 { t.Error("unexpect bytes written: ", nBytes) } } func TestPacketReader_ReadMultiBuffer(t *testing.T) { const alpha = "abcefg" buf := bytes.NewBufferString(alpha) reader := &PacketReader{buf} mb, err := reader.ReadMultiBuffer() common.Must(err) if s := mb.String(); s != alpha { t.Error("content: ", s) } } func TestReaderInterface(t *testing.T) { _ = (io.Reader)(new(ReadVReader)) _ = (Reader)(new(ReadVReader)) _ = (Reader)(new(BufferedReader)) _ = (io.Reader)(new(BufferedReader)) _ = (io.ByteReader)(new(BufferedReader)) _ = (io.WriterTo)(new(BufferedReader)) } ================================================ FILE: common/buf/readv_posix.go ================================================ //go:build !windows && !wasm && !illumos // +build !windows,!wasm,!illumos package buf import ( "syscall" "unsafe" ) type posixReader struct { iovecs []syscall.Iovec } func (r *posixReader) Init(bs []*Buffer) { iovecs := r.iovecs if iovecs == nil { iovecs = make([]syscall.Iovec, 0, len(bs)) } for idx, b := range bs { iovecs = append(iovecs, syscall.Iovec{ Base: &(b.v[0]), }) iovecs[idx].SetLen(int(Size)) } r.iovecs = iovecs } func (r *posixReader) Read(fd uintptr) int32 { n, _, e := syscall.Syscall(syscall.SYS_READV, fd, uintptr(unsafe.Pointer(&r.iovecs[0])), uintptr(len(r.iovecs))) if e != 0 { return -1 } return int32(n) } func (r *posixReader) Clear() { for idx := range r.iovecs { r.iovecs[idx].Base = nil } r.iovecs = r.iovecs[:0] } func newMultiReader() multiReader { return &posixReader{} } ================================================ FILE: common/buf/readv_reader.go ================================================ //go:build !wasm // +build !wasm package buf import ( "io" "runtime" "syscall" "github.com/v2fly/v2ray-core/v5/common/platform" ) type allocStrategy struct { current uint32 } func (s *allocStrategy) Current() uint32 { return s.current } func (s *allocStrategy) Adjust(n uint32) { if n >= s.current { s.current *= 4 } else { s.current = n } if s.current > 32 { s.current = 32 } if s.current == 0 { s.current = 1 } } func (s *allocStrategy) Alloc() []*Buffer { bs := make([]*Buffer, s.current) for i := range bs { bs[i] = New() } return bs } type multiReader interface { Init([]*Buffer) Read(fd uintptr) int32 Clear() } // ReadVReader is a Reader that uses readv(2) syscall to read data. type ReadVReader struct { io.Reader rawConn syscall.RawConn mr multiReader alloc allocStrategy } // NewReadVReader creates a new ReadVReader. func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) *ReadVReader { return &ReadVReader{ Reader: reader, rawConn: rawConn, alloc: allocStrategy{ current: 1, }, mr: newMultiReader(), } } func (r *ReadVReader) readMulti() (MultiBuffer, error) { bs := r.alloc.Alloc() r.mr.Init(bs) var nBytes int32 err := r.rawConn.Read(func(fd uintptr) bool { n := r.mr.Read(fd) if n < 0 { return false } nBytes = n return true }) r.mr.Clear() if err != nil { ReleaseMulti(MultiBuffer(bs)) return nil, err } if nBytes == 0 { ReleaseMulti(MultiBuffer(bs)) return nil, io.EOF } nBuf := 0 for nBuf < len(bs) { if nBytes <= 0 { break } end := nBytes if end > Size { end = Size } bs[nBuf].end = end nBytes -= end nBuf++ } for i := nBuf; i < len(bs); i++ { bs[i].Release() bs[i] = nil } return MultiBuffer(bs[:nBuf]), nil } // ReadMultiBuffer implements Reader. func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) { if r.alloc.Current() == 1 { b, err := ReadBuffer(r.Reader) if b.IsFull() { r.alloc.Adjust(1) } return MultiBuffer{b}, err } mb, err := r.readMulti() if err != nil { return nil, err } r.alloc.Adjust(uint32(len(mb))) return mb, nil } var useReadv = false func init() { const defaultFlagValue = "NOT_DEFINED_AT_ALL" value := platform.NewEnvFlag("v2ray.buf.readv").GetValue(func() string { return defaultFlagValue }) switch value { case defaultFlagValue, "auto": if (runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x") && (runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "windows") { useReadv = true } case "enable": useReadv = true } } ================================================ FILE: common/buf/readv_reader_wasm.go ================================================ //go:build wasm // +build wasm package buf import ( "io" "syscall" ) const useReadv = false func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) Reader { panic("not implemented") } ================================================ FILE: common/buf/readv_test.go ================================================ //go:build !wasm // +build !wasm package buf_test import ( "crypto/rand" "net" "testing" "github.com/google/go-cmp/cmp" "golang.org/x/sync/errgroup" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" ) func TestReadvReader(t *testing.T) { tcpServer := &tcp.Server{ MsgProcessor: func(b []byte) []byte { return b }, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() conn, err := net.Dial("tcp", dest.NetAddr()) common.Must(err) defer conn.Close() const size = 8192 data := make([]byte, 8192) common.Must2(rand.Read(data)) var errg errgroup.Group errg.Go(func() error { writer := NewWriter(conn) mb := MergeBytes(nil, data) return writer.WriteMultiBuffer(mb) }) defer func() { if err := errg.Wait(); err != nil { t.Error(err) } }() rawConn, err := conn.(*net.TCPConn).SyscallConn() common.Must(err) reader := NewReadVReader(conn, rawConn) var rmb MultiBuffer for { mb, err := reader.ReadMultiBuffer() if err != nil { t.Fatal("unexpected error: ", err) } rmb, _ = MergeMulti(rmb, mb) if rmb.Len() == size { break } } rdata := make([]byte, size) SplitBytes(rmb, rdata) if r := cmp.Diff(data, rdata); r != "" { t.Fatal(r) } } ================================================ FILE: common/buf/readv_unix.go ================================================ //go:build illumos // +build illumos package buf import "golang.org/x/sys/unix" type unixReader struct { iovs [][]byte } func (r *unixReader) Init(bs []*Buffer) { iovs := r.iovs if iovs == nil { iovs = make([][]byte, 0, len(bs)) } for _, b := range bs { iovs = append(iovs, b.v) } r.iovs = iovs } func (r *unixReader) Read(fd uintptr) int32 { n, e := unix.Readv(int(fd), r.iovs) if e != nil { return -1 } return int32(n) } func (r *unixReader) Clear() { r.iovs = r.iovs[:0] } func newMultiReader() multiReader { return &unixReader{} } ================================================ FILE: common/buf/readv_windows.go ================================================ package buf import ( "syscall" ) type windowsReader struct { bufs []syscall.WSABuf } func (r *windowsReader) Init(bs []*Buffer) { if r.bufs == nil { r.bufs = make([]syscall.WSABuf, 0, len(bs)) } for _, b := range bs { r.bufs = append(r.bufs, syscall.WSABuf{Len: uint32(Size), Buf: &b.v[0]}) } } func (r *windowsReader) Clear() { for idx := range r.bufs { r.bufs[idx].Buf = nil } r.bufs = r.bufs[:0] } func (r *windowsReader) Read(fd uintptr) int32 { var nBytes uint32 var flags uint32 err := syscall.WSARecv(syscall.Handle(fd), &r.bufs[0], uint32(len(r.bufs)), &nBytes, &flags, nil, nil) if err != nil { return -1 } return int32(nBytes) } func newMultiReader() multiReader { return new(windowsReader) } ================================================ FILE: common/buf/writer.go ================================================ package buf import ( "io" "net" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" ) // BufferToBytesWriter is a Writer that writes alloc.Buffer into underlying writer. type BufferToBytesWriter struct { io.Writer cache [][]byte } // WriteMultiBuffer implements Writer. This method takes ownership of the given buffer. func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error { defer ReleaseMulti(mb) size := mb.Len() if size == 0 { return nil } if len(mb) == 1 { return WriteAllBytes(w.Writer, mb[0].Bytes()) } if cap(w.cache) < len(mb) { w.cache = make([][]byte, 0, len(mb)) } bs := w.cache for _, b := range mb { bs = append(bs, b.Bytes()) } defer func() { for idx := range bs { bs[idx] = nil } }() nb := net.Buffers(bs) for size > 0 { n, err := nb.WriteTo(w.Writer) if err != nil { return err } size -= int32(n) } return nil } // ReadFrom implements io.ReaderFrom. func (w *BufferToBytesWriter) ReadFrom(reader io.Reader) (int64, error) { var sc SizeCounter err := Copy(NewReader(reader), w, CountSize(&sc)) return sc.Size, err } // BufferedWriter is a Writer with internal buffer. type BufferedWriter struct { sync.Mutex writer Writer buffer *Buffer buffered bool } // NewBufferedWriter creates a new BufferedWriter. func NewBufferedWriter(writer Writer) *BufferedWriter { return &BufferedWriter{ writer: writer, buffer: New(), buffered: true, } } // WriteByte implements io.ByteWriter. func (w *BufferedWriter) WriteByte(c byte) error { return common.Error2(w.Write([]byte{c})) } // Write implements io.Writer. func (w *BufferedWriter) Write(b []byte) (int, error) { if len(b) == 0 { return 0, nil } w.Lock() defer w.Unlock() if !w.buffered { if writer, ok := w.writer.(io.Writer); ok { return writer.Write(b) } } totalBytes := 0 for len(b) > 0 { if w.buffer == nil { w.buffer = New() } nBytes, err := w.buffer.Write(b) totalBytes += nBytes if err != nil { return totalBytes, err } if !w.buffered || w.buffer.IsFull() { if err := w.flushInternal(); err != nil { return totalBytes, err } } b = b[nBytes:] } return totalBytes, nil } // WriteMultiBuffer implements Writer. It takes ownership of the given MultiBuffer. func (w *BufferedWriter) WriteMultiBuffer(b MultiBuffer) error { if b.IsEmpty() { return nil } w.Lock() defer w.Unlock() if !w.buffered { return w.writer.WriteMultiBuffer(b) } reader := MultiBufferContainer{ MultiBuffer: b, } defer reader.Close() for !reader.MultiBuffer.IsEmpty() { if w.buffer == nil { w.buffer = New() } common.Must2(w.buffer.ReadFrom(&reader)) if w.buffer.IsFull() { if err := w.flushInternal(); err != nil { return err } } } return nil } // Flush flushes buffered content into underlying writer. func (w *BufferedWriter) Flush() error { w.Lock() defer w.Unlock() return w.flushInternal() } func (w *BufferedWriter) flushInternal() error { if w.buffer.IsEmpty() { return nil } b := w.buffer w.buffer = nil if writer, ok := w.writer.(io.Writer); ok { err := WriteAllBytes(writer, b.Bytes()) b.Release() return err } return w.writer.WriteMultiBuffer(MultiBuffer{b}) } // SetBuffered sets whether the internal buffer is used. If set to false, Flush() will be called to clear the buffer. func (w *BufferedWriter) SetBuffered(f bool) error { w.Lock() defer w.Unlock() w.buffered = f if !f { return w.flushInternal() } return nil } // ReadFrom implements io.ReaderFrom. func (w *BufferedWriter) ReadFrom(reader io.Reader) (int64, error) { if err := w.SetBuffered(false); err != nil { return 0, err } var sc SizeCounter err := Copy(NewReader(reader), w, CountSize(&sc)) return sc.Size, err } // Close implements io.Closable. func (w *BufferedWriter) Close() error { if err := w.Flush(); err != nil { return err } return common.Close(w.writer) } // SequentialWriter is a Writer that writes MultiBuffer sequentially into the underlying io.Writer. type SequentialWriter struct { io.Writer } // WriteMultiBuffer implements Writer. func (w *SequentialWriter) WriteMultiBuffer(mb MultiBuffer) error { mb, err := WriteMultiBuffer(w.Writer, mb) ReleaseMulti(mb) return err } type noOpWriter byte func (noOpWriter) WriteMultiBuffer(b MultiBuffer) error { ReleaseMulti(b) return nil } func (noOpWriter) Write(b []byte) (int, error) { return len(b), nil } func (noOpWriter) ReadFrom(reader io.Reader) (int64, error) { b := New() defer b.Release() totalBytes := int64(0) for { b.Clear() _, err := b.ReadFrom(reader) totalBytes += int64(b.Len()) if err != nil { if errors.Cause(err) == io.EOF { return totalBytes, nil } return totalBytes, err } } } var ( // Discard is a Writer that swallows all contents written in. Discard Writer = noOpWriter(0) // DiscardBytes is an io.Writer that swallows all contents written in. DiscardBytes io.Writer = noOpWriter(0) ) ================================================ FILE: common/buf/writer_test.go ================================================ package buf_test import ( "bufio" "bytes" "crypto/rand" "io" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) func TestWriter(t *testing.T) { lb := New() common.Must2(lb.ReadFrom(rand.Reader)) expectedBytes := append([]byte(nil), lb.Bytes()...) writeBuffer := bytes.NewBuffer(make([]byte, 0, 1024*1024)) writer := NewBufferedWriter(NewWriter(writeBuffer)) writer.SetBuffered(false) common.Must(writer.WriteMultiBuffer(MultiBuffer{lb})) common.Must(writer.Flush()) if r := cmp.Diff(expectedBytes, writeBuffer.Bytes()); r != "" { t.Error(r) } } func TestBytesWriterReadFrom(t *testing.T) { const size = 50000 pReader, pWriter := pipe.New(pipe.WithSizeLimit(size)) reader := bufio.NewReader(io.LimitReader(rand.Reader, size)) writer := NewBufferedWriter(pWriter) writer.SetBuffered(false) nBytes, err := reader.WriteTo(writer) if nBytes != size { t.Fatal("unexpected size of bytes written: ", nBytes) } if err != nil { t.Fatal("expect success, but actually error: ", err.Error()) } mb, err := pReader.ReadMultiBuffer() common.Must(err) if mb.Len() != size { t.Fatal("unexpected size read: ", mb.Len()) } } func TestDiscardBytes(t *testing.T) { b := New() common.Must2(b.ReadFullFrom(rand.Reader, Size)) nBytes, err := io.Copy(DiscardBytes, b) common.Must(err) if nBytes != Size { t.Error("copy size: ", nBytes) } } func TestDiscardBytesMultiBuffer(t *testing.T) { const size = 10240*1024 + 1 buffer := bytes.NewBuffer(make([]byte, 0, size)) common.Must2(buffer.ReadFrom(io.LimitReader(rand.Reader, size))) r := NewReader(buffer) nBytes, err := io.Copy(DiscardBytes, &BufferedReader{Reader: r}) common.Must(err) if nBytes != size { t.Error("copy size: ", nBytes) } } func TestWriterInterface(t *testing.T) { { var writer interface{} = (*BufferToBytesWriter)(nil) switch writer.(type) { case Writer, io.Writer, io.ReaderFrom: default: t.Error("BufferToBytesWriter is not Writer, io.Writer or io.ReaderFrom") } } { var writer interface{} = (*BufferedWriter)(nil) switch writer.(type) { case Writer, io.Writer, io.ReaderFrom, io.ByteWriter: default: t.Error("BufferedWriter is not Writer, io.Writer, io.ReaderFrom or io.ByteWriter") } } } ================================================ FILE: common/bytespool/pool.go ================================================ package bytespool import "sync" func createAllocFunc(size int32) func() interface{} { return func() interface{} { return make([]byte, size) } } // The following parameters controls the size of buffer pools. // There are numPools pools. Starting from 2k size, the size of each pool is sizeMulti of the previous one. // Package buf is guaranteed to not use buffers larger than the largest pool. // Other packets may use larger buffers. const ( numPools = 4 sizeMulti = 4 ) var ( pool [numPools]sync.Pool poolSize [numPools]int32 ) func init() { size := int32(2048) for i := 0; i < numPools; i++ { pool[i] = sync.Pool{ New: createAllocFunc(size), } poolSize[i] = size size *= sizeMulti } } // GetPool returns a sync.Pool that generates bytes array with at least the given size. // It may return nil if no such pool exists. // // v2ray:api:stable func GetPool(size int32) *sync.Pool { for idx, ps := range poolSize { if size <= ps { return &pool[idx] } } return nil } // Alloc returns a byte slice with at least the given size. Minimum size of returned slice is 2048. // // v2ray:api:stable func Alloc(size int32) []byte { pool := GetPool(size) if pool != nil { return pool.Get().([]byte) } return make([]byte, size) } // Free puts a byte slice into the internal pool. // // v2ray:api:stable func Free(b []byte) { size := int32(cap(b)) b = b[0:cap(b)] for i := numPools - 1; i >= 0; i-- { if size >= poolSize[i] { pool[i].Put(b) // nolint: staticcheck return } } } ================================================ FILE: common/cache/lru.go ================================================ package cache import ( "container/list" "sync" ) // Lru simple, fast lru cache implementation type Lru interface { Get(key interface{}) (value interface{}, ok bool) GetKeyFromValue(value interface{}) (key interface{}, ok bool) Put(key, value interface{}) } type lru struct { capacity int doubleLinkedlist *list.List keyToElement *sync.Map valueToElement *sync.Map mu *sync.Mutex } type lruElement struct { key interface{} value interface{} } // NewLru initializes a lru cache func NewLru(cap int) Lru { return &lru{ capacity: cap, doubleLinkedlist: list.New(), keyToElement: new(sync.Map), valueToElement: new(sync.Map), mu: new(sync.Mutex), } } func (l *lru) Get(key interface{}) (value interface{}, ok bool) { l.mu.Lock() defer l.mu.Unlock() if v, ok := l.keyToElement.Load(key); ok { element := v.(*list.Element) l.doubleLinkedlist.MoveToFront(element) return element.Value.(*lruElement).value, true } return nil, false } func (l *lru) GetKeyFromValue(value interface{}) (key interface{}, ok bool) { l.mu.Lock() defer l.mu.Unlock() if k, ok := l.valueToElement.Load(value); ok { element := k.(*list.Element) l.doubleLinkedlist.MoveToFront(element) return element.Value.(*lruElement).key, true } return nil, false } func (l *lru) Put(key, value interface{}) { l.mu.Lock() e := &lruElement{key, value} if v, ok := l.keyToElement.Load(key); ok { element := v.(*list.Element) element.Value = e l.doubleLinkedlist.MoveToFront(element) } else { element := l.doubleLinkedlist.PushFront(e) l.keyToElement.Store(key, element) l.valueToElement.Store(value, element) if l.doubleLinkedlist.Len() > l.capacity { toBeRemove := l.doubleLinkedlist.Back() l.doubleLinkedlist.Remove(toBeRemove) l.keyToElement.Delete(toBeRemove.Value.(*lruElement).key) l.valueToElement.Delete(toBeRemove.Value.(*lruElement).value) } } l.mu.Unlock() } ================================================ FILE: common/cache/lru_test.go ================================================ package cache_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/cache" ) func TestLruReplaceValue(t *testing.T) { lru := NewLru(2) lru.Put(2, 6) lru.Put(1, 5) lru.Put(1, 2) v, _ := lru.Get(1) if v != 2 { t.Error("should get 2", v) } v, _ = lru.Get(2) if v != 6 { t.Error("should get 6", v) } } func TestLruRemoveOld(t *testing.T) { lru := NewLru(2) v, ok := lru.Get(2) if ok { t.Error("should get nil", v) } lru.Put(1, 1) lru.Put(2, 2) v, _ = lru.Get(1) if v != 1 { t.Error("should get 1", v) } lru.Put(3, 3) v, ok = lru.Get(2) if ok { t.Error("should get nil", v) } lru.Put(4, 4) v, ok = lru.Get(1) if ok { t.Error("should get nil", v) } v, _ = lru.Get(3) if v != 3 { t.Error("should get 3", v) } v, _ = lru.Get(4) if v != 4 { t.Error("should get 4", v) } } func TestGetKeyFromValue(t *testing.T) { lru := NewLru(2) lru.Put(3, 3) lru.Put(2, 2) lru.Put(1, 1) v, ok := lru.GetKeyFromValue(3) if ok { t.Error("should get nil", v) } v, _ = lru.GetKeyFromValue(2) if v != 2 { t.Error("should get 2", v) } } ================================================ FILE: common/cmdarg/arg.go ================================================ package cmdarg import ( "bytes" "io" "os" ) // LoadArg loads one arg, maybe an remote url, or local file path func LoadArg(arg string) (out io.Reader, err error) { bs, err := LoadArgToBytes(arg) if err != nil { return nil, err } out = bytes.NewBuffer(bs) return } // LoadArgToBytes loads one arg to []byte, maybe an remote url, or local file path func LoadArgToBytes(arg string) (out []byte, err error) { out, err = os.ReadFile(arg) if err != nil { return } return } ================================================ FILE: common/cmdarg/cmdarg.go ================================================ package cmdarg import "strings" // Arg is used by flag to accept multiple argument. type Arg []string func (c *Arg) String() string { return strings.Join([]string(*c), " ") } // Set is the method flag package calls func (c *Arg) Set(value string) error { *c = append(*c, value) return nil } ================================================ FILE: common/cmdarg/errors.generated.go ================================================ package cmdarg import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/common.go ================================================ // Package common contains common utilities that are shared among other packages. // See each sub-package for detail. package common import ( "fmt" "go/build" "io" "net/http" "net/url" "os" "path/filepath" "strings" "time" "github.com/v2fly/v2ray-core/v5/common/errors" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen // ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route. var ErrNoClue = errors.New("not enough information for making a decision") // Must panics if err is not nil. func Must(err error) { if err != nil { panic(err) } } // Must2 panics if the second parameter is not nil, otherwise returns the first parameter. func Must2(v interface{}, err error) interface{} { Must(err) return v } // Error2 returns the err from the 2nd parameter. func Error2(v interface{}, err error) error { return err } // envFile returns the name of the Go environment configuration file. // Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166 func envFile() (string, error) { if file := os.Getenv("GOENV"); file != "" { if file == "off" { return "", fmt.Errorf("GOENV=off") } return file, nil } dir, err := os.UserConfigDir() if err != nil { return "", err } if dir == "" { return "", fmt.Errorf("missing user-config dir") } return filepath.Join(dir, "go", "env"), nil } // GetRuntimeEnv returns the value of runtime environment variable, // that is set by running following command: `go env -w key=value`. func GetRuntimeEnv(key string) (string, error) { file, err := envFile() if err != nil { return "", err } if file == "" { return "", fmt.Errorf("missing runtime env file") } var data []byte var runtimeEnv string data, readErr := os.ReadFile(file) if readErr != nil { return "", readErr } envStrings := strings.Split(string(data), "\n") for _, envItem := range envStrings { envItem = strings.TrimSuffix(envItem, "\r") envKeyValue := strings.Split(envItem, "=") if strings.EqualFold(strings.TrimSpace(envKeyValue[0]), key) { runtimeEnv = strings.TrimSpace(envKeyValue[1]) } } return runtimeEnv, nil } // GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty. func GetGOBIN() string { // The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command` GOBIN := os.Getenv("GOBIN") if GOBIN == "" { var err error // The one set by user by running `go env -w GOBIN=/path` GOBIN, err = GetRuntimeEnv("GOBIN") if err != nil { // The default one that Golang uses return filepath.Join(build.Default.GOPATH, "bin") } if GOBIN == "" { return filepath.Join(build.Default.GOPATH, "bin") } return GOBIN } return GOBIN } // GetGOPATH returns GOPATH environment variable as a string. It will NOT be empty. func GetGOPATH() string { // The one set by user explicitly by `export GOPATH=/path` or `env GOPATH=/path command` GOPATH := os.Getenv("GOPATH") if GOPATH == "" { var err error // The one set by user by running `go env -w GOPATH=/path` GOPATH, err = GetRuntimeEnv("GOPATH") if err != nil { // The default one that Golang uses return build.Default.GOPATH } if GOPATH == "" { return build.Default.GOPATH } return GOPATH } return GOPATH } // FetchHTTPContent dials http(s) for remote content func FetchHTTPContent(target string) ([]byte, error) { parsedTarget, err := url.Parse(target) if err != nil { return nil, newError("invalid URL: ", target).Base(err) } if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" { return nil, newError("invalid scheme: ", parsedTarget.Scheme) } client := &http.Client{ Timeout: 30 * time.Second, } resp, err := client.Do(&http.Request{ Method: "GET", URL: parsedTarget, Close: true, }) if err != nil { return nil, newError("failed to dial to ", target).Base(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, newError("unexpected HTTP status code: ", resp.StatusCode) } content, err := io.ReadAll(resp.Body) if err != nil { return nil, newError("failed to read HTTP response").Base(err) } return content, nil } ================================================ FILE: common/common_test.go ================================================ package common_test import ( "errors" "testing" . "github.com/v2fly/v2ray-core/v5/common" ) func TestMust(t *testing.T) { hasPanic := func(f func()) (ret bool) { defer func() { if r := recover(); r != nil { ret = true } }() f() return false } testCases := []struct { Input func() Panic bool }{ { Panic: true, Input: func() { Must(func() error { return errors.New("test error") }()) }, }, { Panic: true, Input: func() { Must2(func() (int, error) { return 0, errors.New("test error") }()) }, }, { Panic: false, Input: func() { Must(func() error { return nil }()) }, }, } for idx, test := range testCases { if hasPanic(test.Input) != test.Panic { t.Error("test case #", idx, " expect panic ", test.Panic, " but actually not") } } } ================================================ FILE: common/crypto/aes.go ================================================ package crypto import ( "crypto/aes" "crypto/cipher" "github.com/v2fly/v2ray-core/v5/common" ) // NewAesDecryptionStream creates a new AES encryption stream based on given key and IV. // Caller must ensure the length of key and IV is either 16, 24 or 32 bytes. func NewAesDecryptionStream(key []byte, iv []byte) cipher.Stream { return NewAesStreamMethod(key, iv, cipher.NewCFBDecrypter) } // NewAesEncryptionStream creates a new AES description stream based on given key and IV. // Caller must ensure the length of key and IV is either 16, 24 or 32 bytes. func NewAesEncryptionStream(key []byte, iv []byte) cipher.Stream { return NewAesStreamMethod(key, iv, cipher.NewCFBEncrypter) } func NewAesStreamMethod(key []byte, iv []byte, f func(cipher.Block, []byte) cipher.Stream) cipher.Stream { aesBlock, err := aes.NewCipher(key) common.Must(err) return f(aesBlock, iv) } // NewAesCTRStream creates a stream cipher based on AES-CTR. func NewAesCTRStream(key []byte, iv []byte) cipher.Stream { return NewAesStreamMethod(key, iv, cipher.NewCTR) } // NewAesGcm creates a AEAD cipher based on AES-GCM. func NewAesGcm(key []byte) cipher.AEAD { block, err := aes.NewCipher(key) common.Must(err) aead, err := cipher.NewGCM(block) common.Must(err) return aead } ================================================ FILE: common/crypto/auth.go ================================================ package crypto import ( "crypto/cipher" "crypto/rand" "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/bytespool" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/protocol" ) type BytesGenerator func() []byte func GenerateEmptyBytes() BytesGenerator { var b [1]byte return func() []byte { return b[:0] } } func GenerateStaticBytes(content []byte) BytesGenerator { return func() []byte { return content } } func GenerateIncreasingNonce(nonce []byte) BytesGenerator { c := append([]byte(nil), nonce...) return func() []byte { for i := range c { c[i]++ if c[i] != 0 { break } } return c } } func GenerateInitialAEADNonce() BytesGenerator { return GenerateIncreasingNonce([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) } type Authenticator interface { NonceSize() int Overhead() int Open(dst, cipherText []byte) ([]byte, error) Seal(dst, plainText []byte) ([]byte, error) } type AEADAuthenticator struct { cipher.AEAD NonceGenerator BytesGenerator AdditionalDataGenerator BytesGenerator } func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) { iv := v.NonceGenerator() if len(iv) != v.AEAD.NonceSize() { return nil, newError("invalid AEAD nonce size: ", len(iv)) } var additionalData []byte if v.AdditionalDataGenerator != nil { additionalData = v.AdditionalDataGenerator() } return v.AEAD.Open(dst, iv, cipherText, additionalData) } func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) { iv := v.NonceGenerator() if len(iv) != v.AEAD.NonceSize() { return nil, newError("invalid AEAD nonce size: ", len(iv)) } var additionalData []byte if v.AdditionalDataGenerator != nil { additionalData = v.AdditionalDataGenerator() } return v.AEAD.Seal(dst, iv, plainText, additionalData), nil } type AuthenticationReader struct { auth Authenticator reader *buf.BufferedReader sizeParser ChunkSizeDecoder sizeBytes []byte transferType protocol.TransferType padding PaddingLengthGenerator size uint16 sizeOffset uint16 paddingLen uint16 hasSize bool done bool } func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType, paddingLen PaddingLengthGenerator) *AuthenticationReader { r := &AuthenticationReader{ auth: auth, sizeParser: sizeParser, transferType: transferType, padding: paddingLen, sizeBytes: make([]byte, sizeParser.SizeBytes()), } if chunkSizeDecoderWithOffset, ok := sizeParser.(ChunkSizeDecoderWithOffset); ok { r.sizeOffset = chunkSizeDecoderWithOffset.HasConstantOffset() } if breader, ok := reader.(*buf.BufferedReader); ok { r.reader = breader } else { r.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)} } return r } func (r *AuthenticationReader) readSize() (uint16, uint16, error) { if r.hasSize { r.hasSize = false return r.size, r.paddingLen, nil } if _, err := io.ReadFull(r.reader, r.sizeBytes); err != nil { return 0, 0, err } var padding uint16 if r.padding != nil { padding = r.padding.NextPaddingLen() } size, err := r.sizeParser.Decode(r.sizeBytes) return size, padding, err } var errSoft = newError("waiting for more data") func (r *AuthenticationReader) readBuffer(size int32, padding int32) (*buf.Buffer, error) { b := buf.New() if _, err := b.ReadFullFrom(r.reader, size); err != nil { b.Release() return nil, err } size -= padding rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(size)) if err != nil { b.Release() return nil, err } b.Resize(0, int32(len(rb))) return b, nil } func (r *AuthenticationReader) readInternal(soft bool, mb *buf.MultiBuffer) error { if soft && r.reader.BufferedBytes() < r.sizeParser.SizeBytes() { return errSoft } if r.done { return io.EOF } size, padding, err := r.readSize() if err != nil { return err } if size+r.sizeOffset == uint16(r.auth.Overhead())+padding { r.done = true return io.EOF } effectiveSize := int32(size) + int32(r.sizeOffset) if soft && effectiveSize > r.reader.BufferedBytes() { r.size = size r.paddingLen = padding r.hasSize = true return errSoft } if effectiveSize <= buf.Size { b, err := r.readBuffer(effectiveSize, int32(padding)) if err != nil { return err } *mb = append(*mb, b) return nil } payload := bytespool.Alloc(effectiveSize) defer bytespool.Free(payload) if _, err := io.ReadFull(r.reader, payload[:effectiveSize]); err != nil { return err } effectiveSize -= int32(padding) rb, err := r.auth.Open(payload[:0], payload[:effectiveSize]) if err != nil { return err } *mb = buf.MergeBytes(*mb, rb) return nil } func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) { const readSize = 16 mb := make(buf.MultiBuffer, 0, readSize) if err := r.readInternal(false, &mb); err != nil { buf.ReleaseMulti(mb) return nil, err } for i := 1; i < readSize; i++ { err := r.readInternal(true, &mb) if err == errSoft || err == io.EOF { break } if err != nil { buf.ReleaseMulti(mb) return nil, err } } return mb, nil } type AuthenticationWriter struct { auth Authenticator writer buf.Writer sizeParser ChunkSizeEncoder transferType protocol.TransferType padding PaddingLengthGenerator } func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, writer io.Writer, transferType protocol.TransferType, padding PaddingLengthGenerator) *AuthenticationWriter { w := &AuthenticationWriter{ auth: auth, writer: buf.NewWriter(writer), sizeParser: sizeParser, transferType: transferType, } if padding != nil { w.padding = padding } return w } func (w *AuthenticationWriter) seal(b []byte) (*buf.Buffer, error) { encryptedSize := int32(len(b) + w.auth.Overhead()) var paddingSize int32 if w.padding != nil { paddingSize = int32(w.padding.NextPaddingLen()) } sizeBytes := w.sizeParser.SizeBytes() totalSize := sizeBytes + encryptedSize + paddingSize if totalSize > buf.Size { return nil, newError("size too large: ", totalSize) } eb := buf.New() w.sizeParser.Encode(uint16(encryptedSize+paddingSize), eb.Extend(sizeBytes)) if _, err := w.auth.Seal(eb.Extend(encryptedSize)[:0], b); err != nil { eb.Release() return nil, err } if paddingSize > 0 { // These paddings will send in clear text. // To avoid leakage of PRNG internal state, a cryptographically secure PRNG should be used. paddingBytes := eb.Extend(paddingSize) common.Must2(rand.Read(paddingBytes)) } return eb, nil } func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error { defer buf.ReleaseMulti(mb) var maxPadding int32 if w.padding != nil { maxPadding = int32(w.padding.MaxPaddingLen()) } payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes() - maxPadding if len(mb)+10 > 64*1024*1024 { return errors.New("value too large") } sliceSize := len(mb) + 10 mb2Write := make(buf.MultiBuffer, 0, sliceSize) temp := buf.New() defer temp.Release() rawBytes := temp.Extend(payloadSize) for { nb, nBytes := buf.SplitBytes(mb, rawBytes) mb = nb eb, err := w.seal(rawBytes[:nBytes]) if err != nil { buf.ReleaseMulti(mb2Write) return err } mb2Write = append(mb2Write, eb) if mb.IsEmpty() { break } } return w.writer.WriteMultiBuffer(mb2Write) } func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error { defer buf.ReleaseMulti(mb) if len(mb)+1 > 64*1024*1024 { return errors.New("value too large") } sliceSize := len(mb) + 1 mb2Write := make(buf.MultiBuffer, 0, sliceSize) for _, b := range mb { if b.IsEmpty() { continue } eb, err := w.seal(b.Bytes()) if err != nil { continue } mb2Write = append(mb2Write, eb) } if mb2Write.IsEmpty() { return nil } return w.writer.WriteMultiBuffer(mb2Write) } // WriteMultiBuffer implements buf.Writer. func (w *AuthenticationWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { if mb.IsEmpty() { eb, err := w.seal([]byte{}) common.Must(err) return w.writer.WriteMultiBuffer(buf.MultiBuffer{eb}) } if w.transferType == protocol.TransferTypeStream { return w.writeStream(mb) } return w.writePacket(mb) } ================================================ FILE: common/crypto/auth_test.go ================================================ package crypto_test import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" "io" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/common/crypto" "github.com/v2fly/v2ray-core/v5/common/protocol" ) func TestAuthenticationReaderWriter(t *testing.T) { key := make([]byte, 16) rand.Read(key) block, err := aes.NewCipher(key) common.Must(err) aead, err := cipher.NewGCM(block) common.Must(err) const payloadSize = 1024 * 80 rawPayload := make([]byte, payloadSize) rand.Read(rawPayload) payload := buf.MergeBytes(nil, rawPayload) cache := bytes.NewBuffer(nil) iv := make([]byte, 12) rand.Read(iv) writer := NewAuthenticationWriter(&AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateStaticBytes(iv), AdditionalDataGenerator: GenerateEmptyBytes(), }, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream, nil) common.Must(writer.WriteMultiBuffer(payload)) if cache.Len() <= 1024*80 { t.Error("cache len: ", cache.Len()) } common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{})) reader := NewAuthenticationReader(&AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateStaticBytes(iv), AdditionalDataGenerator: GenerateEmptyBytes(), }, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream, nil) var mb buf.MultiBuffer for mb.Len() < payloadSize { mb2, err := reader.ReadMultiBuffer() common.Must(err) mb, _ = buf.MergeMulti(mb, mb2) } if mb.Len() != payloadSize { t.Error("mb len: ", mb.Len()) } mbContent := make([]byte, payloadSize) buf.SplitBytes(mb, mbContent) if r := cmp.Diff(mbContent, rawPayload); r != "" { t.Error(r) } _, err = reader.ReadMultiBuffer() if err != io.EOF { t.Error("error: ", err) } } func TestAuthenticationReaderWriterPacket(t *testing.T) { key := make([]byte, 16) common.Must2(rand.Read(key)) block, err := aes.NewCipher(key) common.Must(err) aead, err := cipher.NewGCM(block) common.Must(err) cache := buf.New() iv := make([]byte, 12) rand.Read(iv) writer := NewAuthenticationWriter(&AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateStaticBytes(iv), AdditionalDataGenerator: GenerateEmptyBytes(), }, PlainChunkSizeParser{}, cache, protocol.TransferTypePacket, nil) var payload buf.MultiBuffer pb1 := buf.New() pb1.Write([]byte("abcd")) payload = append(payload, pb1) pb2 := buf.New() pb2.Write([]byte("efgh")) payload = append(payload, pb2) common.Must(writer.WriteMultiBuffer(payload)) if cache.Len() == 0 { t.Error("cache len: ", cache.Len()) } common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{})) reader := NewAuthenticationReader(&AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateStaticBytes(iv), AdditionalDataGenerator: GenerateEmptyBytes(), }, PlainChunkSizeParser{}, cache, protocol.TransferTypePacket, nil) mb, err := reader.ReadMultiBuffer() common.Must(err) mb, b1 := buf.SplitFirst(mb) if b1.String() != "abcd" { t.Error("b1: ", b1.String()) } mb, b2 := buf.SplitFirst(mb) if b2.String() != "efgh" { t.Error("b2: ", b2.String()) } if !mb.IsEmpty() { t.Error("not empty") } _, err = reader.ReadMultiBuffer() if err != io.EOF { t.Error("error: ", err) } } ================================================ FILE: common/crypto/benchmark_test.go ================================================ package crypto_test import ( "crypto/cipher" "testing" . "github.com/v2fly/v2ray-core/v5/common/crypto" ) const benchSize = 1024 * 1024 func benchmarkStream(b *testing.B, c cipher.Stream) { b.SetBytes(benchSize) input := make([]byte, benchSize) output := make([]byte, benchSize) b.ResetTimer() for i := 0; i < b.N; i++ { c.XORKeyStream(output, input) } } func BenchmarkChaCha20(b *testing.B) { key := make([]byte, 32) nonce := make([]byte, 8) c := NewChaCha20Stream(key, nonce) benchmarkStream(b, c) } func BenchmarkChaCha20IETF(b *testing.B) { key := make([]byte, 32) nonce := make([]byte, 12) c := NewChaCha20Stream(key, nonce) benchmarkStream(b, c) } func BenchmarkAESEncryption(b *testing.B) { key := make([]byte, 32) iv := make([]byte, 16) c := NewAesEncryptionStream(key, iv) benchmarkStream(b, c) } func BenchmarkAESDecryption(b *testing.B) { key := make([]byte, 32) iv := make([]byte, 16) c := NewAesDecryptionStream(key, iv) benchmarkStream(b, c) } ================================================ FILE: common/crypto/chacha20.go ================================================ package crypto import ( "crypto/cipher" "github.com/v2fly/v2ray-core/v5/common/crypto/internal" ) // NewChaCha20Stream creates a new Chacha20 encryption/descryption stream based on give key and IV. // Caller must ensure the length of key is 32 bytes, and length of IV is either 8 or 12 bytes. func NewChaCha20Stream(key []byte, iv []byte) cipher.Stream { return internal.NewChaCha20Stream(key, iv, 20) } ================================================ FILE: common/crypto/chacha20_test.go ================================================ package crypto_test import ( "crypto/rand" "encoding/hex" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/crypto" ) func mustDecodeHex(s string) []byte { b, err := hex.DecodeString(s) common.Must(err) return b } func TestChaCha20Stream(t *testing.T) { cases := []struct { key []byte iv []byte output []byte }{ { key: mustDecodeHex("0000000000000000000000000000000000000000000000000000000000000000"), iv: mustDecodeHex("0000000000000000"), output: mustDecodeHex("76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" + "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586" + "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed" + "29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f"), }, { key: mustDecodeHex("5555555555555555555555555555555555555555555555555555555555555555"), iv: mustDecodeHex("5555555555555555"), output: mustDecodeHex("bea9411aa453c5434a5ae8c92862f564396855a9ea6e22d6d3b50ae1b3663311" + "a4a3606c671d605ce16c3aece8e61ea145c59775017bee2fa6f88afc758069f7" + "e0b8f676e644216f4d2a3422d7fa36c6c4931aca950e9da42788e6d0b6d1cd83" + "8ef652e97b145b14871eae6c6804c7004db5ac2fce4c68c726d004b10fcaba86"), }, { key: mustDecodeHex("0000000000000000000000000000000000000000000000000000000000000000"), iv: mustDecodeHex("000000000000000000000000"), output: mustDecodeHex("76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"), }, } for _, c := range cases { s := NewChaCha20Stream(c.key, c.iv) input := make([]byte, len(c.output)) actualOutput := make([]byte, len(c.output)) s.XORKeyStream(actualOutput, input) if r := cmp.Diff(c.output, actualOutput); r != "" { t.Fatal(r) } } } func TestChaCha20Decoding(t *testing.T) { key := make([]byte, 32) common.Must2(rand.Read(key)) iv := make([]byte, 8) common.Must2(rand.Read(iv)) stream := NewChaCha20Stream(key, iv) payload := make([]byte, 1024) common.Must2(rand.Read(payload)) x := make([]byte, len(payload)) stream.XORKeyStream(x, payload) stream2 := NewChaCha20Stream(key, iv) stream2.XORKeyStream(x, x) if r := cmp.Diff(x, payload); r != "" { t.Fatal(r) } } ================================================ FILE: common/crypto/chunk.go ================================================ package crypto import ( "encoding/binary" "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" ) // ChunkSizeDecoder is a utility class to decode size value from bytes. type ChunkSizeDecoder interface { // SizeBytes must be stable, return the same value across all calls SizeBytes() int32 Decode([]byte) (uint16, error) } type ChunkSizeDecoderWithOffset interface { ChunkSizeDecoder // HasConstantOffset set the constant offset of Decode // The effective size should be HasConstantOffset() + Decode(_).[0](uint64) HasConstantOffset() uint16 } // ChunkSizeEncoder is a utility class to encode size value into bytes. type ChunkSizeEncoder interface { SizeBytes() int32 Encode(uint16, []byte) []byte } type PaddingLengthGenerator interface { MaxPaddingLen() uint16 NextPaddingLen() uint16 } type PlainChunkSizeParser struct{} func (PlainChunkSizeParser) SizeBytes() int32 { return 2 } func (PlainChunkSizeParser) Encode(size uint16, b []byte) []byte { binary.BigEndian.PutUint16(b, size) return b[:2] } func (PlainChunkSizeParser) Decode(b []byte) (uint16, error) { return binary.BigEndian.Uint16(b), nil } type AEADChunkSizeParser struct { Auth *AEADAuthenticator } func (p *AEADChunkSizeParser) SizeBytes() int32 { return 2 + int32(p.Auth.Overhead()) } func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte { binary.BigEndian.PutUint16(b, size-uint16(p.Auth.Overhead())) b, err := p.Auth.Seal(b[:0], b[:2]) common.Must(err) return b } func (p *AEADChunkSizeParser) Decode(b []byte) (uint16, error) { b, err := p.Auth.Open(b[:0], b) if err != nil { return 0, err } return binary.BigEndian.Uint16(b) + uint16(p.Auth.Overhead()), nil } type ChunkStreamReader struct { sizeDecoder ChunkSizeDecoder reader *buf.BufferedReader buffer []byte leftOverSize int32 maxNumChunk uint32 numChunk uint32 } func NewChunkStreamReader(sizeDecoder ChunkSizeDecoder, reader io.Reader) *ChunkStreamReader { return NewChunkStreamReaderWithChunkCount(sizeDecoder, reader, 0) } func NewChunkStreamReaderWithChunkCount(sizeDecoder ChunkSizeDecoder, reader io.Reader, maxNumChunk uint32) *ChunkStreamReader { r := &ChunkStreamReader{ sizeDecoder: sizeDecoder, buffer: make([]byte, sizeDecoder.SizeBytes()), maxNumChunk: maxNumChunk, } if breader, ok := reader.(*buf.BufferedReader); ok { r.reader = breader } else { r.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)} } return r } func (r *ChunkStreamReader) readSize() (uint16, error) { if _, err := io.ReadFull(r.reader, r.buffer); err != nil { return 0, err } return r.sizeDecoder.Decode(r.buffer) } func (r *ChunkStreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) { size := r.leftOverSize if size == 0 { r.numChunk++ if r.maxNumChunk > 0 && r.numChunk > r.maxNumChunk { return nil, io.EOF } nextSize, err := r.readSize() if err != nil { return nil, err } if nextSize == 0 { return nil, io.EOF } size = int32(nextSize) } r.leftOverSize = size mb, err := r.reader.ReadAtMost(size) if !mb.IsEmpty() { r.leftOverSize -= mb.Len() return mb, nil } return nil, err } type ChunkStreamWriter struct { sizeEncoder ChunkSizeEncoder writer buf.Writer } func NewChunkStreamWriter(sizeEncoder ChunkSizeEncoder, writer io.Writer) *ChunkStreamWriter { return &ChunkStreamWriter{ sizeEncoder: sizeEncoder, writer: buf.NewWriter(writer), } } func (w *ChunkStreamWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { const sliceSize = 8192 mbLen := mb.Len() mb2Write := make(buf.MultiBuffer, 0, mbLen/buf.Size+mbLen/sliceSize+2) for { mb2, slice := buf.SplitSize(mb, sliceSize) mb = mb2 b := buf.New() w.sizeEncoder.Encode(uint16(slice.Len()), b.Extend(w.sizeEncoder.SizeBytes())) mb2Write = append(mb2Write, b) mb2Write = append(mb2Write, slice...) if mb.IsEmpty() { break } } return w.writer.WriteMultiBuffer(mb2Write) } ================================================ FILE: common/crypto/chunk_test.go ================================================ package crypto_test import ( "bytes" "io" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/common/crypto" ) func TestChunkStreamIO(t *testing.T) { cache := bytes.NewBuffer(make([]byte, 0, 8192)) writer := NewChunkStreamWriter(PlainChunkSizeParser{}, cache) reader := NewChunkStreamReader(PlainChunkSizeParser{}, cache) b := buf.New() b.WriteString("abcd") common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{b})) b = buf.New() b.WriteString("efg") common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{b})) common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{})) if cache.Len() != 13 { t.Fatalf("Cache length is %d, want 13", cache.Len()) } mb, err := reader.ReadMultiBuffer() common.Must(err) if s := mb.String(); s != "abcd" { t.Error("content: ", s) } mb, err = reader.ReadMultiBuffer() common.Must(err) if s := mb.String(); s != "efg" { t.Error("content: ", s) } _, err = reader.ReadMultiBuffer() if err != io.EOF { t.Error("error: ", err) } } ================================================ FILE: common/crypto/crypto.go ================================================ // Package crypto provides common crypto libraries for V2Ray. package crypto //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: common/crypto/errors.generated.go ================================================ package crypto import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/crypto/internal/chacha.go ================================================ package internal //go:generate go run chacha_core_gen.go import ( "encoding/binary" ) const ( wordSize = 4 // the size of ChaCha20's words stateSize = 16 // the size of ChaCha20's state, in words blockSize = stateSize * wordSize // the size of ChaCha20's block, in bytes ) type ChaCha20Stream struct { state [stateSize]uint32 // the state as an array of 16 32-bit words block [blockSize]byte // the keystream as an array of 64 bytes offset int // the offset of used bytes in block rounds int } func NewChaCha20Stream(key []byte, nonce []byte, rounds int) *ChaCha20Stream { s := new(ChaCha20Stream) // the magic constants for 256-bit keys s.state[0] = 0x61707865 s.state[1] = 0x3320646e s.state[2] = 0x79622d32 s.state[3] = 0x6b206574 for i := 0; i < 8; i++ { s.state[i+4] = binary.LittleEndian.Uint32(key[i*4 : i*4+4]) } switch len(nonce) { case 8: s.state[14] = binary.LittleEndian.Uint32(nonce[0:]) s.state[15] = binary.LittleEndian.Uint32(nonce[4:]) case 12: s.state[13] = binary.LittleEndian.Uint32(nonce[0:4]) s.state[14] = binary.LittleEndian.Uint32(nonce[4:8]) s.state[15] = binary.LittleEndian.Uint32(nonce[8:12]) default: panic("bad nonce length") } s.rounds = rounds ChaCha20Block(&s.state, s.block[:], s.rounds) return s } func (s *ChaCha20Stream) XORKeyStream(dst, src []byte) { // Stride over the input in 64-byte blocks, minus the amount of keystream // previously used. This will produce best results when processing blocks // of a size evenly divisible by 64. i := 0 max := len(src) for i < max { gap := blockSize - s.offset limit := i + gap if limit > max { limit = max } o := s.offset for j := i; j < limit; j++ { dst[j] = src[j] ^ s.block[o] o++ } i += gap s.offset = o if o == blockSize { s.offset = 0 s.state[12]++ ChaCha20Block(&s.state, s.block[:], s.rounds) } } } ================================================ FILE: common/crypto/internal/chacha_core.generated.go ================================================ package internal import "encoding/binary" func ChaCha20Block(s *[16]uint32, out []byte, rounds int) { x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15] for i := 0; i < rounds; i += 2 { var x uint32 x0 += x4 x = x12 ^ x0 x12 = (x << 16) | (x >> (32 - 16)) x8 += x12 x = x4 ^ x8 x4 = (x << 12) | (x >> (32 - 12)) x0 += x4 x = x12 ^ x0 x12 = (x << 8) | (x >> (32 - 8)) x8 += x12 x = x4 ^ x8 x4 = (x << 7) | (x >> (32 - 7)) x1 += x5 x = x13 ^ x1 x13 = (x << 16) | (x >> (32 - 16)) x9 += x13 x = x5 ^ x9 x5 = (x << 12) | (x >> (32 - 12)) x1 += x5 x = x13 ^ x1 x13 = (x << 8) | (x >> (32 - 8)) x9 += x13 x = x5 ^ x9 x5 = (x << 7) | (x >> (32 - 7)) x2 += x6 x = x14 ^ x2 x14 = (x << 16) | (x >> (32 - 16)) x10 += x14 x = x6 ^ x10 x6 = (x << 12) | (x >> (32 - 12)) x2 += x6 x = x14 ^ x2 x14 = (x << 8) | (x >> (32 - 8)) x10 += x14 x = x6 ^ x10 x6 = (x << 7) | (x >> (32 - 7)) x3 += x7 x = x15 ^ x3 x15 = (x << 16) | (x >> (32 - 16)) x11 += x15 x = x7 ^ x11 x7 = (x << 12) | (x >> (32 - 12)) x3 += x7 x = x15 ^ x3 x15 = (x << 8) | (x >> (32 - 8)) x11 += x15 x = x7 ^ x11 x7 = (x << 7) | (x >> (32 - 7)) x0 += x5 x = x15 ^ x0 x15 = (x << 16) | (x >> (32 - 16)) x10 += x15 x = x5 ^ x10 x5 = (x << 12) | (x >> (32 - 12)) x0 += x5 x = x15 ^ x0 x15 = (x << 8) | (x >> (32 - 8)) x10 += x15 x = x5 ^ x10 x5 = (x << 7) | (x >> (32 - 7)) x1 += x6 x = x12 ^ x1 x12 = (x << 16) | (x >> (32 - 16)) x11 += x12 x = x6 ^ x11 x6 = (x << 12) | (x >> (32 - 12)) x1 += x6 x = x12 ^ x1 x12 = (x << 8) | (x >> (32 - 8)) x11 += x12 x = x6 ^ x11 x6 = (x << 7) | (x >> (32 - 7)) x2 += x7 x = x13 ^ x2 x13 = (x << 16) | (x >> (32 - 16)) x8 += x13 x = x7 ^ x8 x7 = (x << 12) | (x >> (32 - 12)) x2 += x7 x = x13 ^ x2 x13 = (x << 8) | (x >> (32 - 8)) x8 += x13 x = x7 ^ x8 x7 = (x << 7) | (x >> (32 - 7)) x3 += x4 x = x14 ^ x3 x14 = (x << 16) | (x >> (32 - 16)) x9 += x14 x = x4 ^ x9 x4 = (x << 12) | (x >> (32 - 12)) x3 += x4 x = x14 ^ x3 x14 = (x << 8) | (x >> (32 - 8)) x9 += x14 x = x4 ^ x9 x4 = (x << 7) | (x >> (32 - 7)) } binary.LittleEndian.PutUint32(out[0:4], s[0]+x0) binary.LittleEndian.PutUint32(out[4:8], s[1]+x1) binary.LittleEndian.PutUint32(out[8:12], s[2]+x2) binary.LittleEndian.PutUint32(out[12:16], s[3]+x3) binary.LittleEndian.PutUint32(out[16:20], s[4]+x4) binary.LittleEndian.PutUint32(out[20:24], s[5]+x5) binary.LittleEndian.PutUint32(out[24:28], s[6]+x6) binary.LittleEndian.PutUint32(out[28:32], s[7]+x7) binary.LittleEndian.PutUint32(out[32:36], s[8]+x8) binary.LittleEndian.PutUint32(out[36:40], s[9]+x9) binary.LittleEndian.PutUint32(out[40:44], s[10]+x10) binary.LittleEndian.PutUint32(out[44:48], s[11]+x11) binary.LittleEndian.PutUint32(out[48:52], s[12]+x12) binary.LittleEndian.PutUint32(out[52:56], s[13]+x13) binary.LittleEndian.PutUint32(out[56:60], s[14]+x14) binary.LittleEndian.PutUint32(out[60:64], s[15]+x15) } ================================================ FILE: common/crypto/internal/chacha_core_gen.go ================================================ //go:build generate // +build generate package main import ( "fmt" "log" "os" ) func writeQuarterRound(file *os.File, a, b, c, d int) { add := "x%d+=x%d\n" xor := "x=x%d^x%d\n" rotate := "x%d=(x << %d) | (x >> (32 - %d))\n" fmt.Fprintf(file, add, a, b) fmt.Fprintf(file, xor, d, a) fmt.Fprintf(file, rotate, d, 16, 16) fmt.Fprintf(file, add, c, d) fmt.Fprintf(file, xor, b, c) fmt.Fprintf(file, rotate, b, 12, 12) fmt.Fprintf(file, add, a, b) fmt.Fprintf(file, xor, d, a) fmt.Fprintf(file, rotate, d, 8, 8) fmt.Fprintf(file, add, c, d) fmt.Fprintf(file, xor, b, c) fmt.Fprintf(file, rotate, b, 7, 7) } func writeChacha20Block(file *os.File) { fmt.Fprintln(file, ` func ChaCha20Block(s *[16]uint32, out []byte, rounds int) { var x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15 = s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],s[9],s[10],s[11],s[12],s[13],s[14],s[15] for i := 0; i < rounds; i+=2 { var x uint32 `) writeQuarterRound(file, 0, 4, 8, 12) writeQuarterRound(file, 1, 5, 9, 13) writeQuarterRound(file, 2, 6, 10, 14) writeQuarterRound(file, 3, 7, 11, 15) writeQuarterRound(file, 0, 5, 10, 15) writeQuarterRound(file, 1, 6, 11, 12) writeQuarterRound(file, 2, 7, 8, 13) writeQuarterRound(file, 3, 4, 9, 14) fmt.Fprintln(file, "}") for i := 0; i < 16; i++ { fmt.Fprintf(file, "binary.LittleEndian.PutUint32(out[%d:%d], s[%d]+x%d)\n", i*4, i*4+4, i, i) } fmt.Fprintln(file, "}") fmt.Fprintln(file) } func main() { file, err := os.OpenFile("chacha_core.generated.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o644) if err != nil { log.Fatalf("Failed to generate chacha_core.go: %v", err) } defer file.Close() fmt.Fprintln(file, "package internal") fmt.Fprintln(file) fmt.Fprintln(file, "import \"encoding/binary\"") fmt.Fprintln(file) writeChacha20Block(file) } ================================================ FILE: common/crypto/io.go ================================================ package crypto import ( "crypto/cipher" "io" "github.com/v2fly/v2ray-core/v5/common/buf" ) type CryptionReader struct { stream cipher.Stream reader io.Reader } func NewCryptionReader(stream cipher.Stream, reader io.Reader) *CryptionReader { return &CryptionReader{ stream: stream, reader: reader, } } func (r *CryptionReader) Read(data []byte) (int, error) { nBytes, err := r.reader.Read(data) if nBytes > 0 { r.stream.XORKeyStream(data[:nBytes], data[:nBytes]) } return nBytes, err } var _ buf.Writer = (*CryptionWriter)(nil) type CryptionWriter struct { stream cipher.Stream writer io.Writer bufWriter buf.Writer } // NewCryptionWriter creates a new CryptionWriter. func NewCryptionWriter(stream cipher.Stream, writer io.Writer) *CryptionWriter { return &CryptionWriter{ stream: stream, writer: writer, bufWriter: buf.NewWriter(writer), } } // Write implements io.Writer.Write(). func (w *CryptionWriter) Write(data []byte) (int, error) { w.stream.XORKeyStream(data, data) if err := buf.WriteAllBytes(w.writer, data); err != nil { return 0, err } return len(data), nil } // WriteMultiBuffer implements buf.Writer. func (w *CryptionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { for _, b := range mb { w.stream.XORKeyStream(b.Bytes(), b.Bytes()) } return w.bufWriter.WriteMultiBuffer(mb) } ================================================ FILE: common/dice/dice.go ================================================ // Package dice contains common functions to generate random number. // It also initialize math/rand with the time in seconds at launch time. package dice import ( crand "crypto/rand" "io" "math/big" "math/rand" "time" "github.com/v2fly/v2ray-core/v5/common" ) // Roll returns a non-negative number between 0 (inclusive) and n (exclusive). func Roll(n int) int { if n == 1 { return 0 } return rand.Intn(n) } // RollWith returns a non-negative number between 0 (inclusive) and n (exclusive). // Use random as the random source, if read fails, it panics. func RollWith(n int, random io.Reader) int { if n == 1 { return 0 } mrand, err := crand.Int(random, big.NewInt(int64(n))) common.Must(err) return int(mrand.Int64()) } // Roll returns a non-negative number between 0 (inclusive) and n (exclusive). func RollDeterministic(n int, seed int64) int { if n == 1 { return 0 } return rand.New(rand.NewSource(seed)).Intn(n) } // RollUint16 returns a random uint16 value. func RollUint16() uint16 { return uint16(rand.Int63() >> 47) } func RollUint64() uint64 { return rand.Uint64() } func NewDeterministicDice(seed int64) *DeterministicDice { return &DeterministicDice{rand.New(rand.NewSource(seed))} } type DeterministicDice struct { *rand.Rand } func (dd *DeterministicDice) Roll(n int) int { if n == 1 { return 0 } return dd.Intn(n) } func init() { rand.Seed(time.Now().Unix()) } ================================================ FILE: common/dice/dice_test.go ================================================ package dice_test import ( "math/rand" "testing" . "github.com/v2fly/v2ray-core/v5/common/dice" ) func BenchmarkRoll1(b *testing.B) { for i := 0; i < b.N; i++ { Roll(1) } } func BenchmarkRoll20(b *testing.B) { for i := 0; i < b.N; i++ { Roll(20) } } func BenchmarkIntn1(b *testing.B) { for i := 0; i < b.N; i++ { rand.Intn(1) //nolint:staticcheck } } func BenchmarkIntn20(b *testing.B) { for i := 0; i < b.N; i++ { rand.Intn(20) } } func BenchmarkInt63(b *testing.B) { for i := 0; i < b.N; i++ { _ = uint16(rand.Int63() >> 47) } } func BenchmarkInt31(b *testing.B) { for i := 0; i < b.N; i++ { _ = uint16(rand.Int31() >> 15) } } func BenchmarkIntn(b *testing.B) { for i := 0; i < b.N; i++ { _ = uint16(rand.Intn(65536)) } } ================================================ FILE: common/drain/drain.go ================================================ package drain import "io" //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type Drainer interface { AcknowledgeReceive(size int) Drain(reader io.Reader) error } ================================================ FILE: common/drain/drainer.go ================================================ package drain import ( "io" "github.com/v2fly/v2ray-core/v5/common/dice" ) type BehaviorSeedLimitedDrainer struct { DrainSize int } func NewBehaviorSeedLimitedDrainer(behaviorSeed int64, drainFoundation, maxBaseDrainSize, maxRandDrain int) (Drainer, error) { behaviorRand := dice.NewDeterministicDice(behaviorSeed) BaseDrainSize := behaviorRand.Roll(maxBaseDrainSize) RandDrainMax := behaviorRand.Roll(maxRandDrain) + 1 RandDrainRolled := dice.Roll(RandDrainMax) DrainSize := drainFoundation + BaseDrainSize + RandDrainRolled return &BehaviorSeedLimitedDrainer{DrainSize: DrainSize}, nil } func (d *BehaviorSeedLimitedDrainer) AcknowledgeReceive(size int) { d.DrainSize -= size } func (d *BehaviorSeedLimitedDrainer) Drain(reader io.Reader) error { if d.DrainSize > 0 { err := drainReadN(reader, d.DrainSize) if err == nil { return newError("drained connection") } return newError("unable to drain connection").Base(err) } return nil } func drainReadN(reader io.Reader, n int) error { _, err := io.CopyN(io.Discard, reader, int64(n)) return err } func WithError(drainer Drainer, reader io.Reader, err error) error { drainErr := drainer.Drain(reader) if drainErr == nil { return err } return newError(drainErr).Base(err) } type NopDrainer struct{} func (n NopDrainer) AcknowledgeReceive(size int) { } func (n NopDrainer) Drain(reader io.Reader) error { return nil } func NewNopDrainer() Drainer { return &NopDrainer{} } ================================================ FILE: common/drain/errors.generated.go ================================================ package drain import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/dualStack/fusedPacketConn/fusedPacketSocket.go ================================================ package fusedPacketConn import ( "errors" "time" "github.com/v2fly/v2ray-core/v5/common/net" ) var errClosed = errors.New("fused packet conn is closed") // FusedPacketConn combines two PacketConn socket to create a dual stack PacketConn // When sending packet, the correct PacketConn for that destination address will be chosen // When receiving packet, will receive packet from either socket // Other operations will be done on both conn type FusedPacketConn struct { ipv6 net.PacketConn ipv4 net.PacketConn readCh chan readResult done chan struct{} localAddrPreferIPv6 bool } type readResult struct { data []byte addr net.Addr err error } func NewFusedPacketConn(ipv4, ipv6 net.PacketConn, readBufSize int, localAddrPreferIPv6 bool) *FusedPacketConn { f := &FusedPacketConn{ ipv4: ipv4, ipv6: ipv6, readCh: make(chan readResult, 2), done: make(chan struct{}), localAddrPreferIPv6: localAddrPreferIPv6, } go f.readLoop(ipv4, readBufSize) go f.readLoop(ipv6, readBufSize) return f } func (f *FusedPacketConn) readLoop(conn net.PacketConn, bufSize int) { for { buf := make([]byte, bufSize) n, addr, err := conn.ReadFrom(buf) select { case <-f.done: return case f.readCh <- readResult{data: buf[:n], addr: addr, err: err}: } if err != nil { return } } } func (f *FusedPacketConn) ReadFrom(p []byte) (int, net.Addr, error) { select { case <-f.done: return 0, nil, errClosed case r := <-f.readCh: if r.err != nil { return 0, r.addr, r.err } n := copy(p, r.data) return n, r.addr, nil } } func isIPv4Addr(addr net.Addr) bool { udpAddr, ok := addr.(*net.UDPAddr) if !ok { return false } return udpAddr.IP.To4() != nil } func (f *FusedPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) { if isIPv4Addr(addr) { return f.ipv4.WriteTo(p, addr) } return f.ipv6.WriteTo(p, addr) } func (f *FusedPacketConn) Close() error { close(f.done) err4 := f.ipv4.Close() err6 := f.ipv6.Close() if err4 != nil { return err4 } return err6 } func (f *FusedPacketConn) LocalAddr() net.Addr { if f.localAddrPreferIPv6 { return f.ipv6.LocalAddr() } return f.ipv4.LocalAddr() } func (f *FusedPacketConn) SetDeadline(t time.Time) error { err4 := f.ipv4.SetDeadline(t) err6 := f.ipv6.SetDeadline(t) if err4 != nil { return err4 } return err6 } func (f *FusedPacketConn) SetReadDeadline(t time.Time) error { err4 := f.ipv4.SetReadDeadline(t) err6 := f.ipv6.SetReadDeadline(t) if err4 != nil { return err4 } return err6 } func (f *FusedPacketConn) SetWriteDeadline(t time.Time) error { err4 := f.ipv4.SetWriteDeadline(t) err6 := f.ipv6.SetWriteDeadline(t) if err4 != nil { return err4 } return err6 } ================================================ FILE: common/dualStack/happyEyeball/racingDialer.go ================================================ package happyEyeball import ( "context" "fmt" "time" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/common/task/taskDerive" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type Dialer func(ctx context.Context, domainDestination net.Destination, ips net.IP) (internet.Connection, error) func RacingDialer(ctx context.Context, domainDestination net.Destination, ips []net.IP, dialer Dialer, preferIPv6 bool, preferredHeadStart time.Duration) (internet.Connection, error) { // check if they are of a single family, if so no one have head start hasIPv4 := false hasIPv6 := false for _, a := range ips { if a == nil { continue } switch a.To4() != nil { case true: hasIPv4 = true case false: hasIPv6 = true } } // If there is only one family present, there is no head start if !hasIPv4 || !hasIPv6 { preferredHeadStart = 0 } if preferredHeadStart < 0 { preferredHeadStart = 0 } if len(ips) == 0 { return nil, fmt.Errorf("no addresses to dial") } connCh := make(chan internet.Connection, 1) finished := done.New() tasks := make([]func() error, 0, len(ips)) for _, a := range ips { addr := a // determine delay for this address delay := time.Duration(0) if preferredHeadStart > 0 && addr != nil { isIPv6 := a.To4() == nil // if preferIPv6 is true, IPv4 gets the head start delay (i.e. IPv6 starts earlier) if preferIPv6 { if !isIPv6 { delay = preferredHeadStart } } else { // prefer IPv4, so IPv6 gets delayed if isIPv6 { delay = preferredHeadStart } } } // capture addr and delay in closure tasks = append(tasks, func() error { if delay > 0 { select { case <-time.After(delay): case <-ctx.Done(): return ctx.Err() } } if finished.Done() { return fmt.Errorf("dial attempt cancelled due to another successful dial") } c, err := dialer(ctx, domainDestination, addr) if err != nil { return err } if c == nil { return fmt.Errorf("dialer returned nil connection") } // send the successful conn (non-blocking due to buffer) select { case connCh <- c: // stored default: // channel already has a conn, close this extra one _ = c.Close() } _ = finished.Close() return nil }) } errs := taskDerive.RunTryAll(ctx, tasks...) if errs == nil { // at least one dial succeeded; return the conn select { case c := <-connCh: return c, nil case <-ctx.Done(): return nil, ctx.Err() default: // theoretically should not happen, but handle defensively return nil, fmt.Errorf("dial succeeded but no connection available") } } // all attempts failed, return the first non-nil error for _, e := range errs { if e != nil { return nil, e } } return nil, fmt.Errorf("all dial attempts failed") } ================================================ FILE: common/environment/app.go ================================================ package environment type AppEnvironmentCapabilitySet interface { BaseEnvironmentCapabilitySet SystemNetworkCapabilitySet InstanceNetworkCapabilitySet FileSystemCapabilitySet PersistentStorageCapabilitySet TransientStorageCapabilitySet } type AppEnvironment interface { AppEnvironmentCapabilitySet NarrowScope(key string) (AppEnvironment, error) doNotImpl() } ================================================ FILE: common/environment/base.go ================================================ package environment import ( "github.com/v2fly/v2ray-core/v5/common/environment/filesystemcap" "github.com/v2fly/v2ray-core/v5/features/extension/storage" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tagged" ) type BaseEnvironmentCapabilitySet interface { FeaturesLookupCapabilitySet LogCapabilitySet } type BaseEnvironment interface { BaseEnvironmentCapabilitySet doNotImpl() } type SystemNetworkCapabilitySet interface { Dialer() internet.SystemDialer Listener() internet.SystemListener } type InstanceNetworkCapabilitySet interface { OutboundDialer() tagged.DialFunc } type FeaturesLookupCapabilitySet interface { RequireFeatures() interface{} } type LogCapabilitySet interface { RecordLog() interface{} } type FileSystemCapabilitySet interface { filesystemcap.FileSystemCapabilitySet } type PersistentStorageCapabilitySet interface { PersistentStorage() storage.ScopedPersistentStorage } type TransientStorageCapabilitySet interface { TransientStorage() storage.ScopedTransientStorage } type ProxyMetadataCapabilitySet interface { SelfProxyTag() string } ================================================ FILE: common/environment/connection.go ================================================ package environment import "github.com/v2fly/v2ray-core/v5/common/log" type ConnectionCapabilitySet interface { ConnectionLogCapabilitySet } type ConnectionEnvironment interface { ConnectionCapabilitySet doNotImpl() } type ConnectionLogCapabilitySet interface { RecordConnectionLog(msg log.Message) } ================================================ FILE: common/environment/deferredpersistentstorage/defereredPersistentStorage.go ================================================ package deferredpersistentstorage import ( "context" "github.com/v2fly/v2ray-core/v5/app/persistentstorage" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/features/extension/storage" ) type DeferredPersistentStorage interface { storage.ScopedPersistentStorage ProvideInner(ctx context.Context, inner persistentstorage.ScopedPersistentStorage) } var errNotExist = errors.New("persistent storage does not exist") type deferredPersistentStorage struct { ready context.Context done context.CancelFunc inner persistentstorage.ScopedPersistentStorage awaitingChildren []*deferredPersistentStorage intoScopes []string } func (d *deferredPersistentStorage) ScopedPersistentStorageEngine() { } func (d *deferredPersistentStorage) Put(ctx context.Context, key []byte, value []byte) error { <-d.ready.Done() if d.inner == nil { return errNotExist } return d.inner.Put(ctx, key, value) } func (d *deferredPersistentStorage) Get(ctx context.Context, key []byte) ([]byte, error) { <-d.ready.Done() if d.inner == nil { return nil, errNotExist } return d.inner.Get(ctx, key) } func (d *deferredPersistentStorage) List(ctx context.Context, keyPrefix []byte) ([][]byte, error) { <-d.ready.Done() if d.inner == nil { return nil, errNotExist } return d.inner.List(ctx, keyPrefix) } func (d *deferredPersistentStorage) Clear(ctx context.Context) { <-d.ready.Done() if d.inner == nil { return } d.inner.Clear(ctx) } func (d *deferredPersistentStorage) NarrowScope(ctx context.Context, key []byte) (storage.ScopedPersistentStorage, error) { if d.ready.Err() != nil { return d.inner.NarrowScope(ctx, key) } ready, done := context.WithCancel(ctx) swallowCopyScopes := d.intoScopes dps := &deferredPersistentStorage{ ready: ready, done: done, inner: nil, intoScopes: append(swallowCopyScopes, string(key)), } d.awaitingChildren = append(d.awaitingChildren, dps) return dps, nil } func (d *deferredPersistentStorage) DropScope(ctx context.Context, key []byte) error { <-d.ready.Done() if d.inner == nil { return errNotExist } return d.inner.DropScope(ctx, key) } func (d *deferredPersistentStorage) ProvideInner(ctx context.Context, inner persistentstorage.ScopedPersistentStorage) { d.inner = inner if inner != nil { for _, scope := range d.intoScopes { newScope, err := inner.NarrowScope(ctx, []byte(scope)) if err != nil { panic(err) } d.inner = newScope } } for _, child := range d.awaitingChildren { child.ProvideInner(ctx, d.inner) } d.done() } func NewDeferredPersistentStorage(ctx context.Context) DeferredPersistentStorage { ready, done := context.WithCancel(ctx) return &deferredPersistentStorage{ ready: ready, done: done, inner: nil, } } ================================================ FILE: common/environment/envctx/env.go ================================================ package envctx import "context" const ( environmentKey string = "v2.environment" ) func ContextWithEnvironment(ctx context.Context, environment interface{}) context.Context { return context.WithValue(ctx, environmentKey, environment) //nolint: revive,staticcheck } func EnvironmentFromContext(ctx context.Context) interface{} { return ctx.Value(environmentKey) } ================================================ FILE: common/environment/envimpl/fs.go ================================================ package envimpl import ( "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem/fsifce" ) type fileSystemDefaultImpl struct{} func (f fileSystemDefaultImpl) ReadDir() fsifce.FileReadDirFunc { return filesystem.NewFileReadDir } func (f fileSystemDefaultImpl) RemoveFile() fsifce.FileRemoveFunc { return filesystem.NewFileRemover } func (f fileSystemDefaultImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc { return filesystem.NewFileSeeker } func (f fileSystemDefaultImpl) OpenFileForRead() fsifce.FileReaderFunc { return filesystem.NewFileReader } func (f fileSystemDefaultImpl) OpenFileForWrite() fsifce.FileWriterFunc { return filesystem.NewFileWriter } func NewDefaultFileSystemDefaultImpl() environment.FileSystemCapabilitySet { return fileSystemDefaultImpl{} } ================================================ FILE: common/environment/filesystemcap/fscap.go ================================================ package filesystemcap import "github.com/v2fly/v2ray-core/v5/common/platform/filesystem/fsifce" type FileSystemCapabilitySet interface { OpenFileForReadSeek() fsifce.FileSeekerFunc OpenFileForRead() fsifce.FileReaderFunc OpenFileForWrite() fsifce.FileWriterFunc ReadDir() fsifce.FileReadDirFunc RemoveFile() fsifce.FileRemoveFunc } ================================================ FILE: common/environment/filesystemimpl/fsimpl.go ================================================ package filesystemimpl import ( "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem/fsifce" ) func NewDefaultFileSystemDefaultImpl() environment.FileSystemCapabilitySet { return fsCapImpl{} } type fsCapImpl struct{} func (f fsCapImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc { return filesystem.NewFileSeeker } func (f fsCapImpl) OpenFileForRead() fsifce.FileReaderFunc { return filesystem.NewFileReader } func (f fsCapImpl) OpenFileForWrite() fsifce.FileWriterFunc { return filesystem.NewFileWriter } func (f fsCapImpl) ReadDir() fsifce.FileReadDirFunc { return filesystem.NewFileReadDir } func (f fsCapImpl) RemoveFile() fsifce.FileRemoveFunc { return filesystem.NewFileRemover } ================================================ FILE: common/environment/proxy.go ================================================ package environment type ProxyEnvironmentCapabilitySet interface { BaseEnvironmentCapabilitySet InstanceNetworkCapabilitySet TransientStorageCapabilitySet ProxyMetadataCapabilitySet } // TODO Add NarrowScopeToConnection type ProxyEnvironment interface { ProxyEnvironmentCapabilitySet NarrowScope(key string) (ProxyEnvironment, error) NarrowScopeToTransport(key string) (TransportEnvironment, error) doNotImpl() } ================================================ FILE: common/environment/rootcap.go ================================================ package environment type RootEnvironment interface { AppEnvironment(tag string) AppEnvironment ProxyEnvironment(tag string) ProxyEnvironment DropProxyEnvironment(tag string) error doNotImpl() } ================================================ FILE: common/environment/rootcap_impl.go ================================================ package environment import ( "context" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem/fsifce" "github.com/v2fly/v2ray-core/v5/features/extension/storage" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tagged" ) func NewRootEnvImpl(ctx context.Context, transientStorage storage.ScopedTransientStorage, systemDialer internet.SystemDialer, systemListener internet.SystemListener, filesystem FileSystemCapabilitySet, persistStorage storage.ScopedPersistentStorage, ) RootEnvironment { return &rootEnvImpl{ transientStorage: transientStorage, systemListener: systemListener, systemDialer: systemDialer, filesystem: filesystem, persistStorage: persistStorage, ctx: ctx, } } type rootEnvImpl struct { persistStorage storage.ScopedPersistentStorage transientStorage storage.ScopedTransientStorage systemDialer internet.SystemDialer systemListener internet.SystemListener filesystem FileSystemCapabilitySet ctx context.Context } func (r *rootEnvImpl) doNotImpl() { panic("placeholder doNotImpl") } func (r *rootEnvImpl) AppEnvironment(tag string) AppEnvironment { transientStorage, err := r.transientStorage.NarrowScope(r.ctx, tag) if err != nil { return nil } persistStorage, err := r.persistStorage.NarrowScope(r.ctx, []byte(tag)) if err != nil { return nil } return &appEnvImpl{ transientStorage: transientStorage, persistStorage: persistStorage, systemListener: r.systemListener, systemDialer: r.systemDialer, filesystem: r.filesystem, ctx: r.ctx, } } func (r *rootEnvImpl) ProxyEnvironment(tag string) ProxyEnvironment { transientStorage, err := r.transientStorage.NarrowScope(r.ctx, tag) if err != nil { return nil } return &proxyEnvImpl{ transientStorage: transientStorage, systemListener: r.systemListener, systemDialer: r.systemDialer, ctx: r.ctx, } } func (r *rootEnvImpl) DropProxyEnvironment(tag string) error { transientStorage, err := r.transientStorage.NarrowScope(r.ctx, tag) if err != nil { return err } transientStorage.Clear(r.ctx) return r.transientStorage.DropScope(r.ctx, tag) } type appEnvImpl struct { persistStorage storage.ScopedPersistentStorage transientStorage storage.ScopedTransientStorage systemDialer internet.SystemDialer systemListener internet.SystemListener filesystem FileSystemCapabilitySet ctx context.Context } func (a *appEnvImpl) RequireFeatures() interface{} { panic("implement me") } func (a *appEnvImpl) RecordLog() interface{} { panic("implement me") } func (a *appEnvImpl) Dialer() internet.SystemDialer { panic("implement me") } func (a *appEnvImpl) Listener() internet.SystemListener { panic("implement me") } func (a *appEnvImpl) OutboundDialer() tagged.DialFunc { return internet.DialTaggedOutbound } func (a *appEnvImpl) OpenFileForReadSeek() fsifce.FileSeekerFunc { return a.filesystem.OpenFileForReadSeek() } func (a *appEnvImpl) OpenFileForRead() fsifce.FileReaderFunc { return a.filesystem.OpenFileForRead() } func (a *appEnvImpl) OpenFileForWrite() fsifce.FileWriterFunc { return a.filesystem.OpenFileForWrite() } func (a *appEnvImpl) ReadDir() fsifce.FileReadDirFunc { return a.filesystem.ReadDir() } func (a *appEnvImpl) RemoveFile() fsifce.FileRemoveFunc { return a.filesystem.RemoveFile() } func (a *appEnvImpl) PersistentStorage() storage.ScopedPersistentStorage { return a.persistStorage } func (a *appEnvImpl) TransientStorage() storage.ScopedTransientStorage { return a.transientStorage } func (a *appEnvImpl) NarrowScope(key string) (AppEnvironment, error) { transientStorage, err := a.transientStorage.NarrowScope(a.ctx, key) if err != nil { return nil, err } return &appEnvImpl{ transientStorage: transientStorage, systemDialer: a.systemDialer, systemListener: a.systemListener, ctx: a.ctx, }, nil } func (a *appEnvImpl) doNotImpl() { panic("placeholder doNotImpl") } type proxyEnvImpl struct { transientStorage storage.ScopedTransientStorage systemDialer internet.SystemDialer systemListener internet.SystemListener scopeName string ctx context.Context } func (p *proxyEnvImpl) RequireFeatures() interface{} { panic("implement me") } func (p *proxyEnvImpl) RecordLog() interface{} { panic("implement me") } func (p *proxyEnvImpl) OutboundDialer() tagged.DialFunc { panic("implement me") } func (p *proxyEnvImpl) TransientStorage() storage.ScopedTransientStorage { return p.transientStorage } func (p *proxyEnvImpl) SelfProxyTag() string { return p.scopeName } func (p *proxyEnvImpl) NarrowScope(key string) (ProxyEnvironment, error) { transientStorage, err := p.transientStorage.NarrowScope(p.ctx, key) if err != nil { return nil, err } return &proxyEnvImpl{ transientStorage: transientStorage, scopeName: p.scopeName, ctx: p.ctx, }, nil } func (p *proxyEnvImpl) NarrowScopeToTransport(key string) (TransportEnvironment, error) { transientStorage, err := p.transientStorage.NarrowScope(p.ctx, key) if err != nil { return nil, err } return &transportEnvImpl{ ctx: p.ctx, transientStorage: transientStorage, systemDialer: p.systemDialer, systemListener: p.systemListener, selfProxyTag: p.scopeName, }, nil } func (p *proxyEnvImpl) doNotImpl() { panic("placeholder doNotImpl") } type transportEnvImpl struct { transientStorage storage.ScopedTransientStorage systemDialer internet.SystemDialer systemListener internet.SystemListener ctx context.Context selfProxyTag string } func (t *transportEnvImpl) RequireFeatures() interface{} { panic("implement me") } func (t *transportEnvImpl) SelfProxyTag() string { return t.selfProxyTag } func (t *transportEnvImpl) RecordLog() interface{} { panic("implement me") } func (t *transportEnvImpl) Dialer() internet.SystemDialer { return t.systemDialer } func (t *transportEnvImpl) Listener() internet.SystemListener { return t.systemListener } func (t *transportEnvImpl) OutboundDialer() tagged.DialFunc { return tagged.Dialer } func (t *transportEnvImpl) TransientStorage() storage.ScopedTransientStorage { return t.transientStorage } func (t *transportEnvImpl) NarrowScope(key string) (TransportEnvironment, error) { transientStorage, err := t.transientStorage.NarrowScope(t.ctx, key) if err != nil { return nil, err } return &transportEnvImpl{ ctx: t.ctx, transientStorage: transientStorage, }, nil } func (t *transportEnvImpl) doNotImpl() { panic("implement me") } ================================================ FILE: common/environment/systemnetworkimpl/systemnetwork.go ================================================ package systemnetworkimpl import ( "context" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func NewSystemNetworkImpl(listener internet.SystemListener, dialer internet.SystemDialer) environment.SystemNetworkCapabilitySet { return &systemNetworkImpl{dialer: dialer, listener: listener} } type systemDefaultDialer struct{} func (s systemDefaultDialer) Listen(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.Listener, error) { return internet.ListenSystem(ctx, addr, sockopt) } func (s systemDefaultDialer) ListenPacket(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.PacketConn, error) { return internet.ListenSystemPacket(ctx, addr, sockopt) } func (s systemDefaultDialer) Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *internet.SocketConfig) (net.Conn, error) { return internet.DialSystem(ctx, destination, sockopt) } func NewSystemNetworkDefault() environment.SystemNetworkCapabilitySet { systemDefault := systemDefaultDialer{} return &systemNetworkImpl{dialer: systemDefault, listener: systemDefault} } type systemNetworkImpl struct { listener internet.SystemListener dialer internet.SystemDialer } func (s systemNetworkImpl) Dialer() internet.SystemDialer { return s.dialer } func (s systemNetworkImpl) Listener() internet.SystemListener { return s.listener } func NewSystemListenerWithDefaultOpt(listener internet.SystemListener, opt *internet.SocketConfig) internet.SystemListener { return systemListenerWithDefaultOpt{SystemListener: listener, opt: opt} } type systemListenerWithDefaultOpt struct { internet.SystemListener opt *internet.SocketConfig } func (s systemListenerWithDefaultOpt) Listen(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.Listener, error) { if sockopt == nil { return s.Listen(ctx, addr, s.opt) } return s.Listen(ctx, addr, sockopt) } func (s systemListenerWithDefaultOpt) ListenPacket(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.PacketConn, error) { if sockopt == nil { return s.ListenPacket(ctx, addr, s.opt) } return s.ListenPacket(ctx, addr, sockopt) } func NewSystemDialerWithDefaultOpt(listener internet.SystemDialer, opt *internet.SocketConfig) internet.SystemDialer { return systemDialerWithDefaultOpt{SystemDialer: listener, opt: opt} } type systemDialerWithDefaultOpt struct { internet.SystemDialer opt *internet.SocketConfig } func (s systemDialerWithDefaultOpt) Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *internet.SocketConfig) (net.Conn, error) { if sockopt == nil { return s.Dial(ctx, source, destination, s.opt) } return s.Dial(ctx, source, destination, sockopt) } ================================================ FILE: common/environment/transientstorageimpl/errors.generated.go ================================================ package transientstorageimpl import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/environment/transientstorageimpl/storage.go ================================================ package transientstorageimpl //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "strings" "sync" "github.com/v2fly/v2ray-core/v5/features/extension/storage" ) func NewScopedTransientStorageImpl() storage.ScopedTransientStorage { return &scopedTransientStorageImpl{scopes: map[string]storage.ScopedTransientStorage{}, values: map[string]interface{}{}} } type scopedTransientStorageImpl struct { access sync.Mutex scopes map[string]storage.ScopedTransientStorage values map[string]interface{} } func (s *scopedTransientStorageImpl) ScopedTransientStorage() { panic("implement me") } func (s *scopedTransientStorageImpl) Put(ctx context.Context, key string, value interface{}) error { s.access.Lock() defer s.access.Unlock() s.values[key] = value return nil } func (s *scopedTransientStorageImpl) Get(ctx context.Context, key string) (interface{}, error) { s.access.Lock() defer s.access.Unlock() sw, ok := s.values[key] if !ok { return nil, newError("unable to find ") } return sw, nil } func (s *scopedTransientStorageImpl) List(ctx context.Context, keyPrefix string) ([]string, error) { s.access.Lock() defer s.access.Unlock() var ret []string for key := range s.values { if strings.HasPrefix(key, keyPrefix) { ret = append(ret, key) } } return ret, nil } func (s *scopedTransientStorageImpl) Clear(ctx context.Context) { s.access.Lock() defer s.access.Unlock() for _, v := range s.values { if sw, ok := v.(storage.TransientStorageLifecycleReceiver); ok { _ = sw.Close() } } s.values = map[string]interface{}{} for _, v := range s.scopes { v.Clear(ctx) } s.scopes = map[string]storage.ScopedTransientStorage{} } func (s *scopedTransientStorageImpl) NarrowScope(ctx context.Context, key string) (storage.ScopedTransientStorage, error) { s.access.Lock() defer s.access.Unlock() sw, ok := s.scopes[key] if !ok { scope := NewScopedTransientStorageImpl() s.scopes[key] = scope return scope, nil } return sw, nil } func (s *scopedTransientStorageImpl) DropScope(ctx context.Context, key string) error { s.access.Lock() defer s.access.Unlock() if v, ok := s.scopes[key]; ok { v.Clear(ctx) } delete(s.scopes, key) return nil } ================================================ FILE: common/environment/transport.go ================================================ package environment type TransportEnvironmentCapacitySet interface { BaseEnvironmentCapabilitySet SystemNetworkCapabilitySet InstanceNetworkCapabilitySet TransientStorageCapabilitySet ProxyMetadataCapabilitySet } type TransportEnvironment interface { TransportEnvironmentCapacitySet NarrowScope(key string) (TransportEnvironment, error) doNotImpl() } ================================================ FILE: common/errors/errors.go ================================================ // Package errors is a drop-in replacement for Golang lib 'errors'. package errors import ( "os" "reflect" "strings" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/serial" ) type hasInnerError interface { // Inner returns the underlying error of this one. Inner() error } type hasSeverity interface { Severity() log.Severity } // Error is an error object with underlying error. type Error struct { pathObj interface{} prefix []interface{} message []interface{} inner error severity log.Severity } func (err *Error) WithPathObj(obj interface{}) *Error { err.pathObj = obj return err } func (err *Error) pkgPath() string { if err.pathObj == nil { return "" } path := reflect.TypeOf(err.pathObj).PkgPath() // TODO update required on release path = strings.TrimPrefix(path, "github.com/v2fly/v2ray-core/v5/") path = strings.TrimPrefix(path, "github.com/v2fly/v2ray-core/v5") return path } // Error implements error.Error(). func (err *Error) Error() string { builder := strings.Builder{} for _, prefix := range err.prefix { builder.WriteByte('[') builder.WriteString(serial.ToString(prefix)) builder.WriteString("] ") } path := err.pkgPath() if len(path) > 0 { builder.WriteString(path) builder.WriteString(": ") } msg := serial.Concat(err.message...) builder.WriteString(msg) if err.inner != nil { builder.WriteString(" > ") builder.WriteString(err.inner.Error()) } return builder.String() } // Inner implements hasInnerError.Inner() func (err *Error) Inner() error { if err.inner == nil { return nil } return err.inner } func (err *Error) Base(e error) *Error { err.inner = e return err } func (err *Error) atSeverity(s log.Severity) *Error { err.severity = s return err } func (err *Error) Severity() log.Severity { if err.inner == nil { return err.severity } if s, ok := err.inner.(hasSeverity); ok { as := s.Severity() if as < err.severity { return as } } return err.severity } // AtDebug sets the severity to debug. func (err *Error) AtDebug() *Error { return err.atSeverity(log.Severity_Debug) } // AtInfo sets the severity to info. func (err *Error) AtInfo() *Error { return err.atSeverity(log.Severity_Info) } // AtWarning sets the severity to warning. func (err *Error) AtWarning() *Error { return err.atSeverity(log.Severity_Warning) } // AtError sets the severity to error. func (err *Error) AtError() *Error { return err.atSeverity(log.Severity_Error) } // String returns the string representation of this error. func (err *Error) String() string { return err.Error() } // WriteToLog writes current error into log. func (err *Error) WriteToLog(opts ...ExportOption) { var holder ExportOptionHolder for _, opt := range opts { opt(&holder) } if holder.SessionID > 0 { err.prefix = append(err.prefix, holder.SessionID) } log.Record(&log.GeneralMessage{ Severity: GetSeverity(err), Content: err, }) } type ExportOptionHolder struct { SessionID uint32 } type ExportOption func(*ExportOptionHolder) // New returns a new error object with message formed from given arguments. func New(msg ...interface{}) *Error { return &Error{ message: msg, severity: log.Severity_Info, } } // Cause returns the root cause of this error. func Cause(err error) error { if err == nil { return nil } L: for { switch inner := err.(type) { case hasInnerError: if inner.Inner() == nil { break L } err = inner.Inner() case *os.PathError: if inner.Err == nil { break L } err = inner.Err case *os.SyscallError: if inner.Err == nil { break L } err = inner.Err default: break L } } return err } // GetSeverity returns the actual severity of the error, including inner errors. func GetSeverity(err error) log.Severity { if s, ok := err.(hasSeverity); ok { return s.Severity() } return log.Severity_Info } ================================================ FILE: common/errors/errors_test.go ================================================ package errors_test import ( "io" "strings" "testing" "github.com/google/go-cmp/cmp" . "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/log" ) func TestError(t *testing.T) { err := New("TestError") if v := GetSeverity(err); v != log.Severity_Info { t.Error("severity: ", v) } err = New("TestError2").Base(io.EOF) if v := GetSeverity(err); v != log.Severity_Info { t.Error("severity: ", v) } err = New("TestError3").Base(io.EOF).AtWarning() if v := GetSeverity(err); v != log.Severity_Warning { t.Error("severity: ", v) } err = New("TestError4").Base(io.EOF).AtWarning() err = New("TestError5").Base(err) if v := GetSeverity(err); v != log.Severity_Warning { t.Error("severity: ", v) } if v := err.Error(); !strings.Contains(v, "EOF") { t.Error("error: ", v) } } type e struct{} func TestErrorMessage(t *testing.T) { data := []struct { err error msg string }{ { err: New("a").Base(New("b")).WithPathObj(e{}), msg: "common/errors_test: a > b", }, { err: New("a").Base(New("b").WithPathObj(e{})), msg: "a > common/errors_test: b", }, } for _, d := range data { if diff := cmp.Diff(d.msg, d.err.Error()); diff != "" { t.Error(diff) } } } ================================================ FILE: common/errors/multi_error.go ================================================ package errors import ( "strings" ) type multiError []error func (e multiError) Error() string { var r strings.Builder r.WriteString("multierr: ") for _, err := range e { r.WriteString(err.Error()) r.WriteString(" | ") } return r.String() } func Combine(maybeError ...error) error { var errs multiError for _, err := range maybeError { if err != nil { errs = append(errs, err) } } if len(errs) == 0 { return nil } return errs } ================================================ FILE: common/errors.generated.go ================================================ package common import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/interfaces.go ================================================ package common import "github.com/v2fly/v2ray-core/v5/common/errors" // Closable is the interface for objects that can release its resources. // // v2ray:api:beta type Closable interface { // Close release all resources used by this object, including goroutines. Close() error } // Interruptible is an interface for objects that can be stopped before its completion. // // v2ray:api:beta type Interruptible interface { Interrupt() } // Close closes the obj if it is a Closable. // // v2ray:api:beta func Close(obj interface{}) error { if c, ok := obj.(Closable); ok { return c.Close() } return nil } // Interrupt calls Interrupt() if object implements Interruptible interface, or Close() if the object implements Closable interface. // // v2ray:api:beta func Interrupt(obj interface{}) error { if c, ok := obj.(Interruptible); ok { c.Interrupt() return nil } return Close(obj) } // Runnable is the interface for objects that can start to work and stop on demand. type Runnable interface { // Start starts the runnable object. Upon the method returning nil, the object begins to function properly. Start() error Closable } // HasType is the interface for objects that knows its type. type HasType interface { // Type returns the type of the object. // Usually it returns (*Type)(nil) of the object. Type() interface{} } // ChainedClosable is a Closable that consists of multiple Closable objects. type ChainedClosable []Closable // Close implements Closable. func (cc ChainedClosable) Close() error { var errs []error for _, c := range cc { if err := c.Close(); err != nil { errs = append(errs, err) } } return errors.Combine(errs...) } ================================================ FILE: common/log/access.go ================================================ package log import ( "context" "strings" "github.com/v2fly/v2ray-core/v5/common/serial" ) type logKey int const ( accessMessageKey logKey = iota ) type AccessStatus string const ( AccessAccepted = AccessStatus("accepted") AccessRejected = AccessStatus("rejected") ) type AccessMessage struct { From interface{} To interface{} Status AccessStatus Reason interface{} Email string Detour string } func (m *AccessMessage) String() string { builder := strings.Builder{} builder.WriteString(serial.ToString(m.From)) builder.WriteByte(' ') builder.WriteString(string(m.Status)) builder.WriteByte(' ') builder.WriteString(serial.ToString(m.To)) if len(m.Detour) > 0 { builder.WriteString(" [") builder.WriteString(m.Detour) builder.WriteByte(']') } if reason := serial.ToString(m.Reason); len(reason) > 0 { builder.WriteString(" ") builder.WriteString(reason) } if len(m.Email) > 0 { builder.WriteString(" email: ") builder.WriteString(m.Email) } return builder.String() } func ContextWithAccessMessage(ctx context.Context, accessMessage *AccessMessage) context.Context { return context.WithValue(ctx, accessMessageKey, accessMessage) } func AccessMessageFromContext(ctx context.Context) *AccessMessage { if accessMessage, ok := ctx.Value(accessMessageKey).(*AccessMessage); ok { return accessMessage } return nil } ================================================ FILE: common/log/log.go ================================================ package log import ( "sync" "github.com/v2fly/v2ray-core/v5/common/serial" ) // Message is the interface for all log messages. type Message interface { String() string } // Handler is the interface for log handler. type Handler interface { Handle(msg Message) } // Follower is the interface for following logs. type Follower interface { AddFollower(func(msg Message)) RemoveFollower(func(msg Message)) } // GeneralMessage is a general log message that can contain all kind of content. type GeneralMessage struct { Severity Severity Content interface{} } // String implements Message. func (m *GeneralMessage) String() string { return serial.Concat("[", m.Severity, "] ", m.Content) } // Record writes a message into log stream. func Record(msg Message) { logHandler.Handle(msg) } var logHandler syncHandler // RegisterHandler register a new handler as current log handler. Previous registered handler will be discarded. func RegisterHandler(handler Handler) { if handler == nil { panic("Log handler is nil") } logHandler.Set(handler) } type syncHandler struct { sync.RWMutex Handler } func (h *syncHandler) Handle(msg Message) { h.RLock() defer h.RUnlock() if h.Handler != nil { h.Handler.Handle(msg) } } func (h *syncHandler) Set(handler Handler) { h.Lock() defer h.Unlock() h.Handler = handler } ================================================ FILE: common/log/log.pb.go ================================================ package log import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Severity int32 const ( Severity_Unknown Severity = 0 Severity_Error Severity = 1 Severity_Warning Severity = 2 Severity_Info Severity = 3 Severity_Debug Severity = 4 ) // Enum value maps for Severity. var ( Severity_name = map[int32]string{ 0: "Unknown", 1: "Error", 2: "Warning", 3: "Info", 4: "Debug", } Severity_value = map[string]int32{ "Unknown": 0, "Error": 1, "Warning": 2, "Info": 3, "Debug": 4, } ) func (x Severity) Enum() *Severity { p := new(Severity) *p = x return p } func (x Severity) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Severity) Descriptor() protoreflect.EnumDescriptor { return file_common_log_log_proto_enumTypes[0].Descriptor() } func (Severity) Type() protoreflect.EnumType { return &file_common_log_log_proto_enumTypes[0] } func (x Severity) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Severity.Descriptor instead. func (Severity) EnumDescriptor() ([]byte, []int) { return file_common_log_log_proto_rawDescGZIP(), []int{0} } var File_common_log_log_proto protoreflect.FileDescriptor const file_common_log_log_proto_rawDesc = "" + "\n" + "\x14common/log/log.proto\x12\x15v2ray.core.common.log*D\n" + "\bSeverity\x12\v\n" + "\aUnknown\x10\x00\x12\t\n" + "\x05Error\x10\x01\x12\v\n" + "\aWarning\x10\x02\x12\b\n" + "\x04Info\x10\x03\x12\t\n" + "\x05Debug\x10\x04B`\n" + "\x19com.v2ray.core.common.logP\x01Z)github.com/v2fly/v2ray-core/v5/common/log\xaa\x02\x15V2Ray.Core.Common.Logb\x06proto3" var ( file_common_log_log_proto_rawDescOnce sync.Once file_common_log_log_proto_rawDescData []byte ) func file_common_log_log_proto_rawDescGZIP() []byte { file_common_log_log_proto_rawDescOnce.Do(func() { file_common_log_log_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_log_log_proto_rawDesc), len(file_common_log_log_proto_rawDesc))) }) return file_common_log_log_proto_rawDescData } var file_common_log_log_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_common_log_log_proto_goTypes = []any{ (Severity)(0), // 0: v2ray.core.common.log.Severity } var file_common_log_log_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_common_log_log_proto_init() } func file_common_log_log_proto_init() { if File_common_log_log_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_log_log_proto_rawDesc), len(file_common_log_log_proto_rawDesc)), NumEnums: 1, NumMessages: 0, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_log_log_proto_goTypes, DependencyIndexes: file_common_log_log_proto_depIdxs, EnumInfos: file_common_log_log_proto_enumTypes, }.Build() File_common_log_log_proto = out.File file_common_log_log_proto_goTypes = nil file_common_log_log_proto_depIdxs = nil } ================================================ FILE: common/log/log.proto ================================================ syntax = "proto3"; package v2ray.core.common.log; option csharp_namespace = "V2Ray.Core.Common.Log"; option go_package = "github.com/v2fly/v2ray-core/v5/common/log"; option java_package = "com.v2ray.core.common.log"; option java_multiple_files = true; enum Severity { Unknown = 0; Error = 1; Warning = 2; Info = 3; Debug = 4; } ================================================ FILE: common/log/log_test.go ================================================ package log_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" ) type testLogger struct { value string } func (l *testLogger) Handle(msg log.Message) { l.value = msg.String() } func TestLogRecord(t *testing.T) { var logger testLogger log.RegisterHandler(&logger) ip := "8.8.8.8" log.Record(&log.GeneralMessage{ Severity: log.Severity_Error, Content: net.ParseAddress(ip), }) if diff := cmp.Diff("[Error] "+ip, logger.value); diff != "" { t.Error(diff) } } ================================================ FILE: common/log/logger.go ================================================ package log import ( "io" "log" "os" "time" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/common/signal/semaphore" ) // Writer is the interface for writing logs. type Writer interface { Write(string) error io.Closer } // WriterCreator is a function to create LogWriters. type WriterCreator func() Writer type generalLogger struct { creator WriterCreator buffer chan Message access *semaphore.Instance done *done.Instance } // NewLogger returns a generic log handler that can handle all type of messages. func NewLogger(logWriterCreator WriterCreator) Handler { return &generalLogger{ creator: logWriterCreator, buffer: make(chan Message, 16), access: semaphore.New(1), done: done.New(), } } func (l *generalLogger) run() { defer l.access.Signal() dataWritten := false ticker := time.NewTicker(time.Minute) defer ticker.Stop() logger := l.creator() if logger == nil { return } defer logger.Close() for { select { case <-l.done.Wait(): return case msg := <-l.buffer: logger.Write(msg.String() + platform.LineSeparator()) dataWritten = true case <-ticker.C: if !dataWritten { return } dataWritten = false } } } func (l *generalLogger) Handle(msg Message) { select { case l.buffer <- msg: default: } select { case <-l.access.Wait(): go l.run() default: } } func (l *generalLogger) Close() error { return l.done.Close() } type consoleLogWriter struct { logger *log.Logger } func (w *consoleLogWriter) Write(s string) error { w.logger.Print(s) return nil } func (w *consoleLogWriter) Close() error { return nil } type fileLogWriter struct { file *os.File logger *log.Logger } func (w *fileLogWriter) Write(s string) error { w.logger.Print(s) return nil } func (w *fileLogWriter) Close() error { return w.file.Close() } // CreateStdoutLogWriter returns a LogWriterCreator that creates LogWriter for stdout. func CreateStdoutLogWriter() WriterCreator { return func() Writer { return &consoleLogWriter{ logger: log.New(os.Stdout, "", log.Ldate|log.Ltime), } } } // CreateStderrLogWriter returns a LogWriterCreator that creates LogWriter for stderr. func CreateStderrLogWriter() WriterCreator { return func() Writer { return &consoleLogWriter{ logger: log.New(os.Stderr, "", log.Ldate|log.Ltime), } } } // CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file. func CreateFileLogWriter(path string) (WriterCreator, error) { file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) if err != nil { return nil, err } file.Close() return func() Writer { file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) if err != nil { return nil } return &fileLogWriter{ file: file, logger: log.New(file, "", log.Ldate|log.Ltime), } }, nil } func init() { RegisterHandler(NewLogger(CreateStdoutLogWriter())) } ================================================ FILE: common/log/logger_test.go ================================================ package log_test import ( "os" "strings" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/common/log" ) func TestFileLogger(t *testing.T) { f, err := os.CreateTemp("", "vtest") common.Must(err) path := f.Name() common.Must(f.Close()) creator, err := CreateFileLogWriter(path) common.Must(err) handler := NewLogger(creator) handler.Handle(&GeneralMessage{Content: "Test Log"}) time.Sleep(2 * time.Second) common.Must(common.Close(handler)) f, err = os.Open(path) common.Must(err) defer f.Close() b, err := buf.ReadAllToBytes(f) common.Must(err) if !strings.Contains(string(b), "Test Log") { t.Fatal("Expect log text contains 'Test Log', but actually: ", string(b)) } } ================================================ FILE: common/mux/client.go ================================================ package mux import ( "context" "io" "sync" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) type ClientManager struct { Enabled bool // wheather mux is enabled from user config Picker WorkerPicker } func (m *ClientManager) Dispatch(ctx context.Context, link *transport.Link) error { for i := 0; i < 16; i++ { worker, err := m.Picker.PickAvailable() if err != nil { return err } if worker.Dispatch(ctx, link) { return nil } } return newError("unable to find an available mux client").AtWarning() } type WorkerPicker interface { PickAvailable() (*ClientWorker, error) } type IncrementalWorkerPicker struct { Factory ClientWorkerFactory access sync.Mutex workers []*ClientWorker cleanupTask *task.Periodic } func (p *IncrementalWorkerPicker) cleanupFunc() error { p.access.Lock() defer p.access.Unlock() if len(p.workers) == 0 { return newError("no worker") } p.cleanup() return nil } func (p *IncrementalWorkerPicker) cleanup() { var activeWorkers []*ClientWorker for _, w := range p.workers { if !w.Closed() { activeWorkers = append(activeWorkers, w) } } p.workers = activeWorkers } func (p *IncrementalWorkerPicker) findAvailable() int { for idx, w := range p.workers { if !w.IsFull() { return idx } } return -1 } func (p *IncrementalWorkerPicker) pickInternal() (*ClientWorker, bool, error) { p.access.Lock() defer p.access.Unlock() idx := p.findAvailable() if idx >= 0 { n := len(p.workers) if n > 1 && idx != n-1 { p.workers[n-1], p.workers[idx] = p.workers[idx], p.workers[n-1] } return p.workers[idx], false, nil } p.cleanup() worker, err := p.Factory.Create() if err != nil { return nil, false, err } p.workers = append(p.workers, worker) if p.cleanupTask == nil { p.cleanupTask = &task.Periodic{ Interval: time.Second * 30, Execute: p.cleanupFunc, } } return worker, true, nil } func (p *IncrementalWorkerPicker) PickAvailable() (*ClientWorker, error) { worker, start, err := p.pickInternal() if start { common.Must(p.cleanupTask.Start()) } return worker, err } type ClientWorkerFactory interface { Create() (*ClientWorker, error) } type DialingWorkerFactory struct { Proxy proxy.Outbound Dialer internet.Dialer Strategy ClientStrategy ctx context.Context } func NewDialingWorkerFactory(ctx context.Context, proxy proxy.Outbound, dialer internet.Dialer, strategy ClientStrategy) *DialingWorkerFactory { return &DialingWorkerFactory{ Proxy: proxy, Dialer: dialer, Strategy: strategy, ctx: ctx, } } func (f *DialingWorkerFactory) Create() (*ClientWorker, error) { opts := []pipe.Option{pipe.WithSizeLimit(64 * 1024)} uplinkReader, upLinkWriter := pipe.New(opts...) downlinkReader, downlinkWriter := pipe.New(opts...) c, err := NewClientWorker(transport.Link{ Reader: downlinkReader, Writer: upLinkWriter, }, f.Strategy) if err != nil { return nil, err } go func(p proxy.Outbound, d internet.Dialer, c common.Closable) { ctx := session.ContextWithOutbound(f.ctx, &session.Outbound{ Target: net.TCPDestination(muxCoolAddress, muxCoolPort), }) ctx, cancel := context.WithCancel(ctx) if err := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); err != nil { errors.New("failed to handler mux client connection").Base(err).WriteToLog() } common.Must(c.Close()) cancel() }(f.Proxy, f.Dialer, c.done) return c, nil } type ClientStrategy struct { MaxConcurrency uint32 MaxConnection uint32 } type ClientWorker struct { sessionManager *SessionManager link transport.Link done *done.Instance strategy ClientStrategy } var ( muxCoolAddress = net.DomainAddress("v1.mux.cool") muxCoolPort = net.Port(9527) ) // NewClientWorker creates a new mux.Client. func NewClientWorker(stream transport.Link, s ClientStrategy) (*ClientWorker, error) { c := &ClientWorker{ sessionManager: NewSessionManager(), link: stream, done: done.New(), strategy: s, } go c.fetchOutput() go c.monitor() return c, nil } func (m *ClientWorker) TotalConnections() uint32 { return uint32(m.sessionManager.Count()) } func (m *ClientWorker) ActiveConnections() uint32 { return uint32(m.sessionManager.Size()) } // Closed returns true if this Client is closed. func (m *ClientWorker) Closed() bool { return m.done.Done() } func (m *ClientWorker) monitor() { timer := time.NewTicker(time.Second * 16) defer timer.Stop() for { select { case <-m.done.Wait(): m.sessionManager.Close() common.Close(m.link.Writer) common.Interrupt(m.link.Reader) return case <-timer.C: size := m.sessionManager.Size() if size == 0 && m.sessionManager.CloseIfNoSession() { common.Must(m.done.Close()) } } } } func writeFirstPayload(reader buf.Reader, writer *Writer) error { err := buf.CopyOnceTimeout(reader, writer, time.Millisecond*100) if err == buf.ErrNotTimeoutReader || err == buf.ErrReadTimeout { return writer.WriteMultiBuffer(buf.MultiBuffer{}) } if err != nil { return err } return nil } func fetchInput(ctx context.Context, s *Session, output buf.Writer) { dest := session.OutboundFromContext(ctx).Target transferType := protocol.TransferTypeStream if dest.Network == net.Network_UDP { transferType = protocol.TransferTypePacket } s.transferType = transferType writer := NewWriter(s.ID, dest, output, transferType) defer s.Close() defer writer.Close() newError("dispatching request to ", dest).WriteToLog(session.ExportIDToError(ctx)) if err := writeFirstPayload(s.input, writer); err != nil { newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx)) writer.hasError = true common.Interrupt(s.input) return } if err := buf.Copy(s.input, writer); err != nil { newError("failed to fetch all input").Base(err).WriteToLog(session.ExportIDToError(ctx)) writer.hasError = true common.Interrupt(s.input) return } } func (m *ClientWorker) IsClosing() bool { sm := m.sessionManager if m.strategy.MaxConnection > 0 && sm.Count() >= int(m.strategy.MaxConnection) { return true } return false } func (m *ClientWorker) IsFull() bool { if m.IsClosing() || m.Closed() { return true } sm := m.sessionManager if m.strategy.MaxConcurrency > 0 && sm.Size() >= int(m.strategy.MaxConcurrency) { return true } return false } func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool { if m.IsFull() || m.Closed() { return false } sm := m.sessionManager s := sm.Allocate() if s == nil { return false } s.input = link.Reader s.output = link.Writer go fetchInput(ctx, s, m.link.Writer) return true } func (m *ClientWorker) handleStatueKeepAlive(meta *FrameMetadata, reader *buf.BufferedReader) error { if meta.Option.Has(OptionData) { return buf.Copy(NewStreamReader(reader), buf.Discard) } return nil } func (m *ClientWorker) handleStatusNew(meta *FrameMetadata, reader *buf.BufferedReader) error { if meta.Option.Has(OptionData) { return buf.Copy(NewStreamReader(reader), buf.Discard) } return nil } func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReader) error { if !meta.Option.Has(OptionData) { return nil } s, found := m.sessionManager.Get(meta.SessionID) if !found { // Notify remote peer to close this session. closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream) closingWriter.Close() return buf.Copy(NewStreamReader(reader), buf.Discard) } rr := s.NewReader(reader) err := buf.Copy(rr, s.output) if err != nil && buf.IsWriteError(err) { newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog() // Notify remote peer to close this session. closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream) closingWriter.Close() drainErr := buf.Copy(rr, buf.Discard) common.Interrupt(s.input) s.Close() return drainErr } return err } func (m *ClientWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error { if s, found := m.sessionManager.Get(meta.SessionID); found { if meta.Option.Has(OptionError) { common.Interrupt(s.input) common.Interrupt(s.output) } s.Close() } if meta.Option.Has(OptionData) { return buf.Copy(NewStreamReader(reader), buf.Discard) } return nil } func (m *ClientWorker) fetchOutput() { defer func() { common.Must(m.done.Close()) }() reader := &buf.BufferedReader{Reader: m.link.Reader} var meta FrameMetadata for { err := meta.Unmarshal(reader) if err != nil { if errors.Cause(err) != io.EOF { newError("failed to read metadata").Base(err).WriteToLog() } break } switch meta.SessionStatus { case SessionStatusKeepAlive: err = m.handleStatueKeepAlive(&meta, reader) case SessionStatusEnd: err = m.handleStatusEnd(&meta, reader) case SessionStatusNew: err = m.handleStatusNew(&meta, reader) case SessionStatusKeep: err = m.handleStatusKeep(&meta, reader) default: status := meta.SessionStatus newError("unknown status: ", status).AtError().WriteToLog() return } if err != nil { newError("failed to process data").Base(err).WriteToLog() return } } } ================================================ FILE: common/mux/client_test.go ================================================ package mux_test import ( "context" "testing" "time" "github.com/golang/mock/gomock" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/testing/mocks" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) func TestIncrementalPickerFailure(t *testing.T) { mockCtl := gomock.NewController(t) defer mockCtl.Finish() mockWorkerFactory := mocks.NewMuxClientWorkerFactory(mockCtl) mockWorkerFactory.EXPECT().Create().Return(nil, errors.New("test")) picker := mux.IncrementalWorkerPicker{ Factory: mockWorkerFactory, } _, err := picker.PickAvailable() if err == nil { t.Error("expected error, but nil") } } func TestClientWorkerEOF(t *testing.T) { reader, writer := pipe.New(pipe.WithoutSizeLimit()) common.Must(writer.Close()) worker, err := mux.NewClientWorker(transport.Link{Reader: reader, Writer: writer}, mux.ClientStrategy{}) common.Must(err) time.Sleep(time.Millisecond * 500) f := worker.Dispatch(context.Background(), nil) if f { t.Error("expected failed dispatching, but actually not") } } func TestClientWorkerClose(t *testing.T) { mockCtl := gomock.NewController(t) defer mockCtl.Finish() r1, w1 := pipe.New(pipe.WithoutSizeLimit()) worker1, err := mux.NewClientWorker(transport.Link{ Reader: r1, Writer: w1, }, mux.ClientStrategy{ MaxConcurrency: 4, MaxConnection: 4, }) common.Must(err) r2, w2 := pipe.New(pipe.WithoutSizeLimit()) worker2, err := mux.NewClientWorker(transport.Link{ Reader: r2, Writer: w2, }, mux.ClientStrategy{ MaxConcurrency: 4, MaxConnection: 4, }) common.Must(err) factory := mocks.NewMuxClientWorkerFactory(mockCtl) gomock.InOrder( factory.EXPECT().Create().Return(worker1, nil), factory.EXPECT().Create().Return(worker2, nil), ) picker := &mux.IncrementalWorkerPicker{ Factory: factory, } manager := &mux.ClientManager{ Picker: picker, } tr1, tw1 := pipe.New(pipe.WithoutSizeLimit()) ctx1 := session.ContextWithOutbound(context.Background(), &session.Outbound{ Target: net.TCPDestination(net.DomainAddress("www.v2fly.org"), 80), }) common.Must(manager.Dispatch(ctx1, &transport.Link{ Reader: tr1, Writer: tw1, })) defer tw1.Close() common.Must(w1.Close()) time.Sleep(time.Millisecond * 500) if !worker1.Closed() { t.Error("worker1 is not finished") } tr2, tw2 := pipe.New(pipe.WithoutSizeLimit()) ctx2 := session.ContextWithOutbound(context.Background(), &session.Outbound{ Target: net.TCPDestination(net.DomainAddress("www.v2fly.org"), 80), }) common.Must(manager.Dispatch(ctx2, &transport.Link{ Reader: tr2, Writer: tw2, })) defer tw2.Close() common.Must(w2.Close()) } ================================================ FILE: common/mux/errors.generated.go ================================================ package mux import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/mux/frame.go ================================================ package mux import ( "encoding/binary" "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/bitmask" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" ) type SessionStatus byte const ( SessionStatusNew SessionStatus = 0x01 SessionStatusKeep SessionStatus = 0x02 SessionStatusEnd SessionStatus = 0x03 SessionStatusKeepAlive SessionStatus = 0x04 ) const ( OptionData bitmask.Byte = 0x01 OptionError bitmask.Byte = 0x02 ) type TargetNetwork byte const ( TargetNetworkTCP TargetNetwork = 0x01 TargetNetworkUDP TargetNetwork = 0x02 ) var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6), protocol.PortThenAddress(), ) /* Frame format 2 bytes - length 2 bytes - session id 1 bytes - status 1 bytes - option 1 byte - network 2 bytes - port n bytes - address */ type FrameMetadata struct { Target net.Destination SessionID uint16 Option bitmask.Byte SessionStatus SessionStatus } func (f FrameMetadata) WriteTo(b *buf.Buffer) error { lenBytes := b.Extend(2) len0 := b.Len() sessionBytes := b.Extend(2) binary.BigEndian.PutUint16(sessionBytes, f.SessionID) common.Must(b.WriteByte(byte(f.SessionStatus))) common.Must(b.WriteByte(byte(f.Option))) if f.SessionStatus == SessionStatusNew { switch f.Target.Network { case net.Network_TCP: common.Must(b.WriteByte(byte(TargetNetworkTCP))) case net.Network_UDP: common.Must(b.WriteByte(byte(TargetNetworkUDP))) } if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil { return err } } len1 := b.Len() binary.BigEndian.PutUint16(lenBytes, uint16(len1-len0)) return nil } // Unmarshal reads FrameMetadata from the given reader. func (f *FrameMetadata) Unmarshal(reader io.Reader) error { metaLen, err := serial.ReadUint16(reader) if err != nil { return err } if metaLen > 512 { return newError("invalid metalen ", metaLen).AtError() } b := buf.New() defer b.Release() if _, err := b.ReadFullFrom(reader, int32(metaLen)); err != nil { return err } return f.UnmarshalFromBuffer(b) } // UnmarshalFromBuffer reads a FrameMetadata from the given buffer. // Visible for testing only. func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error { if b.Len() < 4 { return newError("insufficient buffer: ", b.Len()) } f.SessionID = binary.BigEndian.Uint16(b.BytesTo(2)) f.SessionStatus = SessionStatus(b.Byte(2)) f.Option = bitmask.Byte(b.Byte(3)) f.Target.Network = net.Network_Unknown if f.SessionStatus == SessionStatusNew { if b.Len() < 8 { return newError("insufficient buffer: ", b.Len()) } network := TargetNetwork(b.Byte(4)) b.Advance(5) addr, port, err := addrParser.ReadAddressPort(nil, b) if err != nil { return newError("failed to parse address and port").Base(err) } switch network { case TargetNetworkTCP: f.Target = net.TCPDestination(addr, port) case TargetNetworkUDP: f.Target = net.UDPDestination(addr, port) default: return newError("unknown network type: ", network) } } return nil } ================================================ FILE: common/mux/frame_test.go ================================================ package mux_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" ) func BenchmarkFrameWrite(b *testing.B) { frame := mux.FrameMetadata{ Target: net.TCPDestination(net.DomainAddress("www.v2fly.org"), net.Port(80)), SessionID: 1, SessionStatus: mux.SessionStatusNew, } writer := buf.New() defer writer.Release() for i := 0; i < b.N; i++ { common.Must(frame.WriteTo(writer)) writer.Clear() } } ================================================ FILE: common/mux/mux.go ================================================ package mux //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: common/mux/mux_test.go ================================================ package mux_test import ( "io" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) func readAll(reader buf.Reader) (buf.MultiBuffer, error) { var mb buf.MultiBuffer for { b, err := reader.ReadMultiBuffer() if err == io.EOF { break } if err != nil { return nil, err } mb = append(mb, b...) } return mb, nil } func TestReaderWriter(t *testing.T) { pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024)) dest := net.TCPDestination(net.DomainAddress("v2fly.org"), 80) writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream) dest2 := net.TCPDestination(net.LocalHostIP, 443) writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream) dest3 := net.TCPDestination(net.LocalHostIPv6, 18374) writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream) writePayload := func(writer *Writer, payload ...byte) error { b := buf.New() b.Write(payload) return writer.WriteMultiBuffer(buf.MultiBuffer{b}) } common.Must(writePayload(writer, 'a', 'b', 'c', 'd')) common.Must(writePayload(writer2)) common.Must(writePayload(writer, 'e', 'f', 'g', 'h')) common.Must(writePayload(writer3, 'x')) writer.Close() writer3.Close() common.Must(writePayload(writer2, 'y')) writer2.Close() bytesReader := &buf.BufferedReader{Reader: pReader} { var meta FrameMetadata common.Must(meta.Unmarshal(bytesReader)) if r := cmp.Diff(meta, FrameMetadata{ SessionID: 1, SessionStatus: SessionStatusNew, Target: dest, Option: OptionData, }); r != "" { t.Error("metadata: ", r) } data, err := readAll(NewStreamReader(bytesReader)) common.Must(err) if s := data.String(); s != "abcd" { t.Error("data: ", s) } } { var meta FrameMetadata common.Must(meta.Unmarshal(bytesReader)) if r := cmp.Diff(meta, FrameMetadata{ SessionStatus: SessionStatusNew, SessionID: 2, Option: 0, Target: dest2, }); r != "" { t.Error("meta: ", r) } } { var meta FrameMetadata common.Must(meta.Unmarshal(bytesReader)) if r := cmp.Diff(meta, FrameMetadata{ SessionID: 1, SessionStatus: SessionStatusKeep, Option: 1, }); r != "" { t.Error("meta: ", r) } data, err := readAll(NewStreamReader(bytesReader)) common.Must(err) if s := data.String(); s != "efgh" { t.Error("data: ", s) } } { var meta FrameMetadata common.Must(meta.Unmarshal(bytesReader)) if r := cmp.Diff(meta, FrameMetadata{ SessionID: 3, SessionStatus: SessionStatusNew, Option: 1, Target: dest3, }); r != "" { t.Error("meta: ", r) } data, err := readAll(NewStreamReader(bytesReader)) common.Must(err) if s := data.String(); s != "x" { t.Error("data: ", s) } } { var meta FrameMetadata common.Must(meta.Unmarshal(bytesReader)) if r := cmp.Diff(meta, FrameMetadata{ SessionID: 1, SessionStatus: SessionStatusEnd, Option: 0, }); r != "" { t.Error("meta: ", r) } } { var meta FrameMetadata common.Must(meta.Unmarshal(bytesReader)) if r := cmp.Diff(meta, FrameMetadata{ SessionID: 3, SessionStatus: SessionStatusEnd, Option: 0, }); r != "" { t.Error("meta: ", r) } } { var meta FrameMetadata common.Must(meta.Unmarshal(bytesReader)) if r := cmp.Diff(meta, FrameMetadata{ SessionID: 2, SessionStatus: SessionStatusKeep, Option: 1, }); r != "" { t.Error("meta: ", r) } data, err := readAll(NewStreamReader(bytesReader)) common.Must(err) if s := data.String(); s != "y" { t.Error("data: ", s) } } { var meta FrameMetadata common.Must(meta.Unmarshal(bytesReader)) if r := cmp.Diff(meta, FrameMetadata{ SessionID: 2, SessionStatus: SessionStatusEnd, Option: 0, }); r != "" { t.Error("meta: ", r) } } pWriter.Close() { var meta FrameMetadata err := meta.Unmarshal(bytesReader) if err == nil { t.Error("nil error") } } } ================================================ FILE: common/mux/reader.go ================================================ package mux import ( "io" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/crypto" "github.com/v2fly/v2ray-core/v5/common/serial" ) // PacketReader is an io.Reader that reads whole chunk of Mux frames every time. type PacketReader struct { reader io.Reader eof bool } // NewPacketReader creates a new PacketReader. func NewPacketReader(reader io.Reader) *PacketReader { return &PacketReader{ reader: reader, eof: false, } } // ReadMultiBuffer implements buf.Reader. func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if r.eof { return nil, io.EOF } size, err := serial.ReadUint16(r.reader) if err != nil { return nil, err } if size > buf.Size { return nil, newError("packet size too large: ", size) } b := buf.New() if _, err := b.ReadFullFrom(r.reader, int32(size)); err != nil { b.Release() return nil, err } r.eof = true return buf.MultiBuffer{b}, nil } // NewStreamReader creates a new StreamReader. func NewStreamReader(reader *buf.BufferedReader) buf.Reader { return crypto.NewChunkStreamReaderWithChunkCount(crypto.PlainChunkSizeParser{}, reader, 1) } ================================================ FILE: common/mux/server.go ================================================ package mux import ( "context" "io" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) type Server struct { dispatcher routing.Dispatcher } // NewServer creates a new mux.Server. func NewServer(ctx context.Context) *Server { s := &Server{} core.RequireFeatures(ctx, func(d routing.Dispatcher) { s.dispatcher = d }) return s } // Type implements common.HasType. func (s *Server) Type() interface{} { return s.dispatcher.Type() } // Dispatch implements routing.Dispatcher func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { if dest.Address != muxCoolAddress { return s.dispatcher.Dispatch(ctx, dest) } opts := pipe.OptionsFromContext(ctx) uplinkReader, uplinkWriter := pipe.New(opts...) downlinkReader, downlinkWriter := pipe.New(opts...) _, err := NewServerWorker(ctx, s.dispatcher, &transport.Link{ Reader: uplinkReader, Writer: downlinkWriter, }) if err != nil { return nil, err } return &transport.Link{Reader: downlinkReader, Writer: uplinkWriter}, nil } // Start implements common.Runnable. func (s *Server) Start() error { return nil } // Close implements common.Closable. func (s *Server) Close() error { return nil } type ServerWorker struct { dispatcher routing.Dispatcher link *transport.Link sessionManager *SessionManager } func NewServerWorker(ctx context.Context, d routing.Dispatcher, link *transport.Link) (*ServerWorker, error) { worker := &ServerWorker{ dispatcher: d, link: link, sessionManager: NewSessionManager(), } go worker.run(ctx) return worker, nil } func handle(ctx context.Context, s *Session, output buf.Writer) { writer := NewResponseWriter(s.ID, output, s.transferType) if err := buf.Copy(s.input, writer); err != nil { newError("session ", s.ID, " ends.").Base(err).WriteToLog(session.ExportIDToError(ctx)) writer.hasError = true } writer.Close() s.Close() } func (w *ServerWorker) ActiveConnections() uint32 { return uint32(w.sessionManager.Size()) } func (w *ServerWorker) Closed() bool { return w.sessionManager.Closed() } func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.BufferedReader) error { if meta.Option.Has(OptionData) { return buf.Copy(NewStreamReader(reader), buf.Discard) } return nil } func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error { newError("received request for ", meta.Target).WriteToLog(session.ExportIDToError(ctx)) { msg := &log.AccessMessage{ To: meta.Target, Status: log.AccessAccepted, Reason: "", } if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.IsValid() { msg.From = inbound.Source msg.Email = inbound.User.Email } ctx = log.ContextWithAccessMessage(ctx, msg) } link, err := w.dispatcher.Dispatch(ctx, meta.Target) if err != nil { if meta.Option.Has(OptionData) { buf.Copy(NewStreamReader(reader), buf.Discard) } return newError("failed to dispatch request.").Base(err) } s := &Session{ input: link.Reader, output: link.Writer, parent: w.sessionManager, ID: meta.SessionID, transferType: protocol.TransferTypeStream, } if meta.Target.Network == net.Network_UDP { s.transferType = protocol.TransferTypePacket } w.sessionManager.Add(s) go handle(ctx, s, w.link.Writer) if !meta.Option.Has(OptionData) { return nil } rr := s.NewReader(reader) if err := buf.Copy(rr, s.output); err != nil { buf.Copy(rr, buf.Discard) common.Interrupt(s.input) return s.Close() } return nil } func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReader) error { if !meta.Option.Has(OptionData) { return nil } s, found := w.sessionManager.Get(meta.SessionID) if !found { // Notify remote peer to close this session. closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream) closingWriter.Close() return buf.Copy(NewStreamReader(reader), buf.Discard) } rr := s.NewReader(reader) err := buf.Copy(rr, s.output) if err != nil && buf.IsWriteError(err) { newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog() // Notify remote peer to close this session. closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream) closingWriter.Close() drainErr := buf.Copy(rr, buf.Discard) common.Interrupt(s.input) s.Close() return drainErr } return err } func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error { if s, found := w.sessionManager.Get(meta.SessionID); found { if meta.Option.Has(OptionError) { common.Interrupt(s.input) common.Interrupt(s.output) } s.Close() } if meta.Option.Has(OptionData) { return buf.Copy(NewStreamReader(reader), buf.Discard) } return nil } func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedReader) error { var meta FrameMetadata err := meta.Unmarshal(reader) if err != nil { return newError("failed to read metadata").Base(err) } switch meta.SessionStatus { case SessionStatusKeepAlive: err = w.handleStatusKeepAlive(&meta, reader) case SessionStatusEnd: err = w.handleStatusEnd(&meta, reader) case SessionStatusNew: err = w.handleStatusNew(ctx, &meta, reader) case SessionStatusKeep: err = w.handleStatusKeep(&meta, reader) default: status := meta.SessionStatus return newError("unknown status: ", status).AtError() } if err != nil { return newError("failed to process data").Base(err) } return nil } func (w *ServerWorker) run(ctx context.Context) { input := w.link.Reader reader := &buf.BufferedReader{Reader: input} defer w.sessionManager.Close() for { select { case <-ctx.Done(): return default: err := w.handleFrame(ctx, reader) if err != nil { if errors.Cause(err) != io.EOF { newError("unexpected EOF").Base(err).WriteToLog(session.ExportIDToError(ctx)) common.Interrupt(input) } return } } } } ================================================ FILE: common/mux/session.go ================================================ package mux import ( "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/protocol" ) type SessionManager struct { sync.RWMutex sessions map[uint16]*Session count uint16 closed bool } func NewSessionManager() *SessionManager { return &SessionManager{ count: 0, sessions: make(map[uint16]*Session, 16), } } func (m *SessionManager) Closed() bool { m.RLock() defer m.RUnlock() return m.closed } func (m *SessionManager) Size() int { m.RLock() defer m.RUnlock() return len(m.sessions) } func (m *SessionManager) Count() int { m.RLock() defer m.RUnlock() return int(m.count) } func (m *SessionManager) Allocate() *Session { m.Lock() defer m.Unlock() if m.closed { return nil } m.count++ s := &Session{ ID: m.count, parent: m, } m.sessions[s.ID] = s return s } func (m *SessionManager) Add(s *Session) { m.Lock() defer m.Unlock() if m.closed { return } m.count++ m.sessions[s.ID] = s } func (m *SessionManager) Remove(id uint16) { m.Lock() defer m.Unlock() if m.closed { return } delete(m.sessions, id) if len(m.sessions) == 0 { m.sessions = make(map[uint16]*Session, 16) } } func (m *SessionManager) Get(id uint16) (*Session, bool) { m.RLock() defer m.RUnlock() if m.closed { return nil, false } s, found := m.sessions[id] return s, found } func (m *SessionManager) CloseIfNoSession() bool { m.Lock() defer m.Unlock() if m.closed { return true } if len(m.sessions) != 0 { return false } m.closed = true return true } func (m *SessionManager) Close() error { m.Lock() defer m.Unlock() if m.closed { return nil } m.closed = true for _, s := range m.sessions { common.Close(s.input) common.Close(s.output) } m.sessions = nil return nil } // Session represents a client connection in a Mux connection. type Session struct { input buf.Reader output buf.Writer parent *SessionManager ID uint16 transferType protocol.TransferType } // Close closes all resources associated with this session. func (s *Session) Close() error { common.Close(s.output) common.Close(s.input) s.parent.Remove(s.ID) return nil } // NewReader creates a buf.Reader based on the transfer type of this Session. func (s *Session) NewReader(reader *buf.BufferedReader) buf.Reader { if s.transferType == protocol.TransferTypeStream { return NewStreamReader(reader) } return NewPacketReader(reader) } ================================================ FILE: common/mux/session_test.go ================================================ package mux_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/mux" ) func TestSessionManagerAdd(t *testing.T) { m := NewSessionManager() s := m.Allocate() if s.ID != 1 { t.Error("id: ", s.ID) } if m.Size() != 1 { t.Error("size: ", m.Size()) } s = m.Allocate() if s.ID != 2 { t.Error("id: ", s.ID) } if m.Size() != 2 { t.Error("size: ", m.Size()) } s = &Session{ ID: 4, } m.Add(s) if s.ID != 4 { t.Error("id: ", s.ID) } if m.Size() != 3 { t.Error("size: ", m.Size()) } } func TestSessionManagerClose(t *testing.T) { m := NewSessionManager() s := m.Allocate() if m.CloseIfNoSession() { t.Error("able to close") } m.Remove(s.ID) if !m.CloseIfNoSession() { t.Error("not able to close") } } ================================================ FILE: common/mux/writer.go ================================================ package mux import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" ) type Writer struct { dest net.Destination writer buf.Writer id uint16 followup bool hasError bool transferType protocol.TransferType } func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType) *Writer { return &Writer{ id: id, dest: dest, writer: writer, followup: false, transferType: transferType, } } func NewResponseWriter(id uint16, writer buf.Writer, transferType protocol.TransferType) *Writer { return &Writer{ id: id, writer: writer, followup: true, transferType: transferType, } } func (w *Writer) getNextFrameMeta() FrameMetadata { meta := FrameMetadata{ SessionID: w.id, Target: w.dest, } if w.followup { meta.SessionStatus = SessionStatusKeep } else { w.followup = true meta.SessionStatus = SessionStatusNew } return meta } func (w *Writer) writeMetaOnly() error { meta := w.getNextFrameMeta() b := buf.New() if err := meta.WriteTo(b); err != nil { return err } return w.writer.WriteMultiBuffer(buf.MultiBuffer{b}) } func writeMetaWithFrame(writer buf.Writer, meta FrameMetadata, data buf.MultiBuffer) error { frame := buf.New() if err := meta.WriteTo(frame); err != nil { return err } if _, err := serial.WriteUint16(frame, uint16(data.Len())); err != nil { return err } if len(data)+1 > 64*1024*1024 { return errors.New("value too large") } sliceSize := len(data) + 1 mb2 := make(buf.MultiBuffer, 0, sliceSize) mb2 = append(mb2, frame) mb2 = append(mb2, data...) return writer.WriteMultiBuffer(mb2) } func (w *Writer) writeData(mb buf.MultiBuffer) error { meta := w.getNextFrameMeta() meta.Option.Set(OptionData) return writeMetaWithFrame(w.writer, meta, mb) } // WriteMultiBuffer implements buf.Writer. func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error { defer buf.ReleaseMulti(mb) if mb.IsEmpty() { return w.writeMetaOnly() } for !mb.IsEmpty() { var chunk buf.MultiBuffer if w.transferType == protocol.TransferTypeStream { mb, chunk = buf.SplitSize(mb, 8*1024) } else { mb2, b := buf.SplitFirst(mb) mb = mb2 chunk = buf.MultiBuffer{b} } if err := w.writeData(chunk); err != nil { return err } } return nil } // Close implements common.Closable. func (w *Writer) Close() error { meta := FrameMetadata{ SessionID: w.id, SessionStatus: SessionStatusEnd, } if w.hasError { meta.Option.Set(OptionError) } frame := buf.New() common.Must(meta.WriteTo(frame)) w.writer.WriteMultiBuffer(buf.MultiBuffer{frame}) return nil } ================================================ FILE: common/natTraversal/stun/filteredStunConnection.go ================================================ package stun import ( "github.com/pion/stun/v3" "github.com/v2fly/v2ray-core/v5/common/net" ) type STUNMessageCallback func(b []byte, addr net.Addr) func NewFilteredConnection(inner net.PacketConn, callback STUNMessageCallback) (*FilteredConnection, error) { return &FilteredConnection{ PacketConn: inner, stunMsgCallback: callback, }, nil } type FilteredConnection struct { net.PacketConn stunMsgCallback STUNMessageCallback } func (f *FilteredConnection) ReadFrom(b []byte) (int, net.Addr, error) { n, addr, err := f.PacketConn.ReadFrom(b) if stun.IsMessage(b[:n]) { f.stunMsgCallback(b[:n], addr) } return n, addr, err } ================================================ FILE: common/natTraversal/stun/natTypeTest.go ================================================ package stun // Mostly Machine Generated Code import ( "context" "encoding/binary" "errors" "net" "sync" "time" "github.com/pion/stun/v3" "github.com/v2fly/v2ray-core/v5/common/task" ) type NATDependantType int const ( Unknown NATDependantType = iota Independent EndpointDependent EndpointPortDependent EndpointPortDependentPinned ) type NATYesOrNoUnknownType int const ( NATYesOrNoUnknownType_Unknown NATYesOrNoUnknownType = iota NATYesOrNoUnknownType_Yes NATYesOrNoUnknownType_No ) type NATTypeTest struct { newStunConn func() (net.PacketConn, error) testsTranscript []TestConducted transcriptMux sync.Mutex Timeout time.Duration Attempts int FilterBehaviour NATDependantType MappingBehaviour NATDependantType HairpinBehaviour NATYesOrNoUnknownType StableMappingOnSecondaryServer NATYesOrNoUnknownType // Calculated values from testsTranscript PreserveSourcePortWhenSourceNATMapping NATYesOrNoUnknownType SingleSourceIPSourceNATMapping NATYesOrNoUnknownType // PreserveSourceIPPortWhenDestNATMapping // means when receiving packets, // whether the real source address is preserved in the reply message // some time a bad proxy would fill in a default value rather the real remote address // this can be detected when asking remote server to reply from a different ip or port PreserveSourceIPPortWhenDestNATMapping NATYesOrNoUnknownType TestServer net.Addr TestServerSecondary net.Addr SourceIPs []net.IP } func NewNATTypeTest(newStunConn func() (net.PacketConn, error), testServer net.Addr, testServerSecondary net.Addr, timeout time.Duration, attempts int) *NATTypeTest { return &NATTypeTest{ newStunConn: newStunConn, Timeout: timeout, Attempts: attempts, TestServer: testServer, TestServerSecondary: testServerSecondary, } } type TestConducted struct { Req stun.Message ReqSentTo net.Addr ReqSentFrom net.Addr Resp *stun.Message RespFrom net.Addr } func changeRequestSetter(changeIP, changePort bool) stun.RawAttribute { val := make([]byte, 4) var flags uint32 if changeIP { flags |= 0x04 } if changePort { flags |= 0x02 } binary.BigEndian.PutUint32(val, flags) return stun.RawAttribute{ Type: stun.AttrChangeRequest, Value: val, } } func startBackgroundReader(conn *StunClientConn) { go func() { buf := make([]byte, 1500) for { _, _, err := conn.ReadFrom(buf) if err != nil { return } } }() } func (t *NATTypeTest) recordTransaction(tc TestConducted) { t.transcriptMux.Lock() defer t.transcriptMux.Unlock() t.testsTranscript = append(t.testsTranscript, tc) } // doTransactionWithRetry sends multiple STUN requests at once (each with a fresh // transaction ID) and waits for the first response within a single timeout window. // This avoids sequential retry delays caused by UDP packet loss. // Non-timeout errors from sending are returned immediately. func (t *NATTypeTest) doTransactionWithRetry(conn *StunClientConn, localAddr net.Addr, dest net.Addr, attempts int, setters ...stun.Setter) (*stun.Message, net.Addr, error) { //nolint:unparam type result struct { msg stun.Message addr net.Addr } ch := make(chan result, attempts) var txIDs []stunTransactionID var firstMsg *stun.Message for i := 0; i < attempts; i++ { msg := stun.MustBuild(setters...) if i == 0 { firstMsg = msg } _, _, err := conn.ExecuteSTUNMessageAsync(*msg, dest, func(_ [stun.TransactionIDSize]byte, respMsg stun.Message, respAddr net.Addr) { ch <- result{msg: respMsg, addr: respAddr} }) if err != nil { for _, id := range txIDs { conn.processor.CancelTransaction(id) } return nil, nil, err } txIDs = append(txIDs, msg.TransactionID) } // Wait for first response or timeout var resp *result select { case r := <-ch: resp = &r case <-time.After(t.Timeout): } // Cancel all remaining pending transactions for _, id := range txIDs { conn.processor.CancelTransaction(id) } // Record result if resp != nil { respMsg := resp.msg t.recordTransaction(TestConducted{ Req: *firstMsg, ReqSentTo: dest, ReqSentFrom: localAddr, Resp: &respMsg, RespFrom: resp.addr, }) return &respMsg, resp.addr, nil } t.recordTransaction(TestConducted{ Req: *firstMsg, ReqSentTo: dest, ReqSentFrom: localAddr, }) return nil, nil, ErrTimeout } // TestFilterBehaviour determines NAT filtering behavior per RFC 5780 Section 4.4. func (t *NATTypeTest) TestFilterBehaviour() error { rawConn, err := t.newStunConn() if err != nil { return err } conn, err := NewStunClientConn(rawConn) if err != nil { rawConn.Close() return err } defer conn.Close() localAddr := rawConn.LocalAddr() startBackgroundReader(conn) // Test I: Regular binding to confirm connectivity and get OTHER-ADDRESS resp1, _, err := t.doTransactionWithRetry(conn, localAddr, t.TestServer, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil { return err } // Check if server supports RFC 5780 (OTHER-ADDRESS). // Without it, CHANGE-REQUEST results are unreliable. var filterOtherAddr stun.OtherAddress if err := filterOtherAddr.GetFrom(resp1); err != nil { t.FilterBehaviour = Unknown return nil } // Test II: Request server to respond from different IP and port _, _, err = t.doTransactionWithRetry(conn, localAddr, t.TestServer, t.Attempts, stun.TransactionID, stun.BindingRequest, changeRequestSetter(true, true)) if err == nil { t.FilterBehaviour = Independent return nil } if !errors.Is(err, ErrTimeout) { return err } // Test III: Request server to respond from different port only _, _, err = t.doTransactionWithRetry(conn, localAddr, t.TestServer, t.Attempts, stun.TransactionID, stun.BindingRequest, changeRequestSetter(false, true)) if err == nil { t.FilterBehaviour = EndpointDependent return nil } if !errors.Is(err, ErrTimeout) { return err } // Test IV: Check if sending outbound UDP can open the filter for a new endpoint. // Send a binding to the alternative address to create a NAT filter entry, // then ask the original server to reply from that alternative address. // If the response arrives, the filter can be opened by outbound packets. altAddr := &net.UDPAddr{IP: filterOtherAddr.IP, Port: filterOtherAddr.Port} // Send binding to alt address to open the NAT filter for that endpoint _, _, err = t.doTransactionWithRetry(conn, localAddr, altAddr, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil && !errors.Is(err, ErrTimeout) { return err } // Now ask original server to reply from the alternative address _, _, err = t.doTransactionWithRetry(conn, localAddr, t.TestServer, t.Attempts, stun.TransactionID, stun.BindingRequest, changeRequestSetter(true, true)) if err == nil { t.FilterBehaviour = EndpointPortDependent return nil } if !errors.Is(err, ErrTimeout) { return err } t.FilterBehaviour = EndpointPortDependentPinned return nil } // TestMappingBehaviour determines NAT mapping behavior per RFC 5780 Section 4.3. func (t *NATTypeTest) TestMappingBehaviour() error { rawConn, err := t.newStunConn() if err != nil { return err } conn, err := NewStunClientConn(rawConn) if err != nil { rawConn.Close() return err } defer conn.Close() localAddr := rawConn.LocalAddr() startBackgroundReader(conn) // Test I: Regular binding to primary server resp1, _, err := t.doTransactionWithRetry(conn, localAddr, t.TestServer, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil { return err } var mappedAddr1 stun.XORMappedAddress if err := mappedAddr1.GetFrom(resp1); err != nil { return err } var otherAddr stun.OtherAddress if err := otherAddr.GetFrom(resp1); err != nil { // Server does not support RFC 5780 (no OTHER-ADDRESS), cannot test mapping t.MappingBehaviour = Unknown return nil } // Validate OTHER-ADDRESS has both a different IP and port from the primary server. // The mapping behavior test requires a genuinely distinct endpoint to produce meaningful results. serverUDPForValidation, ok := t.TestServer.(*net.UDPAddr) if !ok { return errors.New("TestServer is not a UDP address") } if otherAddr.IP.Equal(serverUDPForValidation.IP) || otherAddr.Port == serverUDPForValidation.Port { return errors.New("OTHER-ADDRESS from STUN server does not differ in both IP and port from the primary server") } // Test II: From same socket, binding to OTHER-ADDRESS (different IP and port) altAddr := &net.UDPAddr{IP: otherAddr.IP, Port: otherAddr.Port} resp2, _, err := t.doTransactionWithRetry(conn, localAddr, altAddr, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil { return err } var mappedAddr2 stun.XORMappedAddress if err := mappedAddr2.GetFrom(resp2); err != nil { return err } if mappedAddr1.String() == mappedAddr2.String() { t.MappingBehaviour = Independent return nil } // Test III: From same socket, binding to (other IP, original port) serverUDP, ok := t.TestServer.(*net.UDPAddr) if !ok { return errors.New("TestServer is not a UDP address") } altAddr2 := &net.UDPAddr{IP: otherAddr.IP, Port: serverUDP.Port} resp3, _, err := t.doTransactionWithRetry(conn, localAddr, altAddr2, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil { return err } var mappedAddr3 stun.XORMappedAddress if err := mappedAddr3.GetFrom(resp3); err != nil { return err } if mappedAddr2.String() == mappedAddr3.String() { t.MappingBehaviour = EndpointDependent } else { t.MappingBehaviour = EndpointPortDependent } return nil } func (t *NATTypeTest) TestMappingBehaviourWithSecondaryServer() error { if t.TestServerSecondary == nil { t.StableMappingOnSecondaryServer = NATYesOrNoUnknownType_Unknown return nil } rawConn, err := t.newStunConn() if err != nil { return err } conn, err := NewStunClientConn(rawConn) if err != nil { rawConn.Close() return err } defer conn.Close() localAddr := rawConn.LocalAddr() startBackgroundReader(conn) // Binding to primary server resp1, _, err := t.doTransactionWithRetry(conn, localAddr, t.TestServer, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil { return err } var mappedAddr1 stun.XORMappedAddress if err := mappedAddr1.GetFrom(resp1); err != nil { return err } // Binding to secondary server from the same socket resp2, _, err := t.doTransactionWithRetry(conn, localAddr, t.TestServerSecondary, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil { return err } var mappedAddr2 stun.XORMappedAddress if err := mappedAddr2.GetFrom(resp2); err != nil { return err } if mappedAddr1.String() == mappedAddr2.String() { t.StableMappingOnSecondaryServer = NATYesOrNoUnknownType_Yes } else { t.StableMappingOnSecondaryServer = NATYesOrNoUnknownType_No } return nil } // TestHairpinBehaviour determines if the NAT supports hairpinning per RFC 5780 Section 4.5. // Both sockets must first get their mapped addresses via STUN, then send to each other's // mapped address. This ensures the NAT filter is opened for the peer's mapped address // before the hairpin test packet arrives, avoiding false negatives from filtering. func (t *NATTypeTest) TestHairpinBehaviour() error { // Socket 1: get mapped address rawConn1, err := t.newStunConn() if err != nil { return err } conn1, err := NewStunClientConn(rawConn1) if err != nil { rawConn1.Close() return err } defer conn1.Close() localAddr1 := rawConn1.LocalAddr() startBackgroundReader(conn1) resp1, _, err := t.doTransactionWithRetry(conn1, localAddr1, t.TestServer, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil { return err } var mappedAddr1 stun.XORMappedAddress if err := mappedAddr1.GetFrom(resp1); err != nil { return err } selfAddr1 := &net.UDPAddr{IP: mappedAddr1.IP, Port: mappedAddr1.Port} // Socket 2: get mapped address rawConn2, err := t.newStunConn() if err != nil { return err } conn2, err := NewStunClientConn(rawConn2) if err != nil { rawConn2.Close() return err } defer conn2.Close() localAddr2 := rawConn2.LocalAddr() startBackgroundReader(conn2) resp2, _, err := t.doTransactionWithRetry(conn2, localAddr2, t.TestServer, t.Attempts, stun.TransactionID, stun.BindingRequest) if err != nil { return err } var mappedAddr2 stun.XORMappedAddress if err := mappedAddr2.GetFrom(resp2); err != nil { return err } selfAddr2 := &net.UDPAddr{IP: mappedAddr2.IP, Port: mappedAddr2.Port} // Socket 1 sends to MA2 to open the NAT filter for MA2 on socket 1's side. // Without this, a hairpinned packet from socket 2 (appearing as MA2) would be // filtered by endpoint-dependent filtering on socket 1. openMsg := stun.MustBuild(stun.TransactionID, stun.BindingRequest) openMsg.Encode() conn1.WriteTo(openMsg.Raw, selfAddr2) // Build hairpin test messages: register on conn1's processor, send from conn2. // Hairpinned packets arrive at conn1 from MA2 (now allowed by filter). type result struct { msg stun.Message addr net.Addr } ch := make(chan result, t.Attempts) var txIDs []stunTransactionID var firstMsg *stun.Message for i := 0; i < t.Attempts; i++ { msg := stun.MustBuild(stun.TransactionID, stun.BindingRequest) if i == 0 { firstMsg = msg } msg.Encode() // Register on conn1's processor (hairpinned packet arrives at conn1) conn1.processor.AddPendingTransactionListener(msg.TransactionID, func(_ [stun.TransactionIDSize]byte, respMsg stun.Message, respAddr net.Addr) { ch <- result{msg: respMsg, addr: respAddr} }) txIDs = append(txIDs, msg.TransactionID) // Send from conn2 to MA1 (socket 1's mapped address) if _, err := conn2.WriteTo(msg.Raw, selfAddr1); err != nil { for _, id := range txIDs { conn1.processor.CancelTransaction(id) } return err } } // Wait for hairpinned packet on conn1 var respResult *result select { case r := <-ch: respResult = &r case <-time.After(t.Timeout): } // Cancel all remaining pending transactions for _, id := range txIDs { conn1.processor.CancelTransaction(id) } t.HairpinBehaviour = NATYesOrNoUnknownType_No if respResult != nil { respMsg := respResult.msg t.recordTransaction(TestConducted{ Req: *firstMsg, ReqSentTo: selfAddr1, ReqSentFrom: localAddr2, Resp: &respMsg, RespFrom: respResult.addr, }) if respMsg.Type == stun.BindingRequest { t.HairpinBehaviour = NATYesOrNoUnknownType_Yes } return nil } t.recordTransaction(TestConducted{ Req: *firstMsg, ReqSentTo: selfAddr1, ReqSentFrom: localAddr2, }) return nil } // TestAll runs all NAT behavior tests in parallel, then calculates derived values. func (t *NATTypeTest) TestAll() error { err := task.Run(context.Background(), t.TestFilterBehaviour, t.TestMappingBehaviour, t.TestHairpinBehaviour, t.TestMappingBehaviourWithSecondaryServer, ) if err != nil { return err } return t.CalcReminderValues() } // CalcReminderValues derives additional NAT properties from the collected test transcripts. func (t *NATTypeTest) CalcReminderValues() error { t.transcriptMux.Lock() transcripts := make([]TestConducted, len(t.testsTranscript)) copy(transcripts, t.testsTranscript) t.transcriptMux.Unlock() type addrKey struct { ip string port int } var mappedAddrs []addrKey for _, tc := range transcripts { if tc.Resp == nil { continue } var addr stun.XORMappedAddress if err := addr.GetFrom(tc.Resp); err != nil { continue } mappedAddrs = append(mappedAddrs, addrKey{ip: addr.IP.String(), port: addr.Port}) } // Collect unique mapped source IPs seenIPs := make(map[string]struct{}) t.SourceIPs = nil for _, m := range mappedAddrs { if _, ok := seenIPs[m.ip]; !ok { seenIPs[m.ip] = struct{}{} t.SourceIPs = append(t.SourceIPs, net.ParseIP(m.ip)) } } // Need at least 2 mapped addresses to draw any meaningful comparison if len(mappedAddrs) < 2 { t.SingleSourceIPSourceNATMapping = NATYesOrNoUnknownType_Unknown t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Unknown t.PreserveSourcePortWhenSourceNATMapping = NATYesOrNoUnknownType_Unknown return nil } // SingleSourceIPSourceNATMapping: check if all mapped IPs are the same allSameIP := true for _, m := range mappedAddrs[1:] { if m.ip != mappedAddrs[0].ip { allSameIP = false break } } if allSameIP { t.SingleSourceIPSourceNATMapping = NATYesOrNoUnknownType_Yes } else { t.SingleSourceIPSourceNATMapping = NATYesOrNoUnknownType_No } // PreserveSourceIPPortWhenDestNATMapping: check using RESPONSE-ORIGIN if available, // otherwise fall back to CHANGE-REQUEST hairpin detection. allResponseOriginMatchRespFrom := true responseOriginPairCount := 0 for _, tc := range transcripts { if tc.Resp == nil || tc.RespFrom == nil || tc.ReqSentTo == nil { continue } var responseOrigin stun.ResponseOrigin if err := responseOrigin.GetFrom(tc.Resp); err != nil { continue } responseOriginAddr := net.UDPAddr{IP: responseOrigin.IP, Port: responseOrigin.Port} if responseOriginAddr.String() == tc.ReqSentTo.String() { continue } responseOriginPairCount++ if tc.RespFrom.String() != responseOriginAddr.String() { allResponseOriginMatchRespFrom = false break } } if responseOriginPairCount >= 1 { if allResponseOriginMatchRespFrom { t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Yes } else { t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_No } } else { // Fallback: detect if we receive our own hairpin packets. // If RespFrom == ReqSentTo for all responses, the source address was rewritten (not preserved). allSendToMatchRespFrom := true respPairCount := 0 for _, tc := range transcripts { if tc.Resp == nil || tc.RespFrom == nil || tc.ReqSentTo == nil { continue } // Only count hairpin packets: responses that are binding requests (not binding responses) if tc.Resp.Type != stun.BindingRequest { continue } respPairCount++ if tc.RespFrom.String() != tc.ReqSentTo.String() { allSendToMatchRespFrom = false break } } switch { case respPairCount < 1: t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Unknown case allSendToMatchRespFrom: t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_No default: t.PreserveSourceIPPortWhenDestNATMapping = NATYesOrNoUnknownType_Yes } } // PreserveSourcePortWhenSourceNATMapping: check if mapped port matches local source port preserves := true validCount := 0 for _, tc := range transcripts { if tc.Resp == nil || tc.ReqSentFrom == nil { continue } localUDP, ok := tc.ReqSentFrom.(*net.UDPAddr) if !ok || localUDP.Port == 0 { continue } var addr stun.XORMappedAddress if err := addr.GetFrom(tc.Resp); err != nil { continue } validCount++ if addr.Port != localUDP.Port { preserves = false } } if validCount >= 2 { if preserves { t.PreserveSourcePortWhenSourceNATMapping = NATYesOrNoUnknownType_Yes } else { t.PreserveSourcePortWhenSourceNATMapping = NATYesOrNoUnknownType_No } } else { t.PreserveSourcePortWhenSourceNATMapping = NATYesOrNoUnknownType_Unknown } return nil } ================================================ FILE: common/natTraversal/stun/processor.go ================================================ package stun import ( "sync" "time" "github.com/pion/stun/v3" "github.com/v2fly/v2ray-core/v5/common/net" ) type stunTransactionID = [stun.TransactionIDSize]byte type pendingTransaction struct { handler PendingTransactionHandler createdAt time.Time } type Processor struct { pendingStunRequest map[stunTransactionID]pendingTransaction closed bool mux sync.Mutex } func NewProcessor() *Processor { return &Processor{ pendingStunRequest: make(map[stunTransactionID]pendingTransaction), } } func (p *Processor) HandleStunPacket(b []byte, addr net.Addr) { var msg stun.Message if err := stun.Decode(b, &msg); err != nil { return } p.mux.Lock() pt, ok := p.pendingStunRequest[msg.TransactionID] if ok { delete(p.pendingStunRequest, msg.TransactionID) } p.mux.Unlock() if ok { pt.handler(msg.TransactionID, msg, addr) } } type PendingTransactionHandler func(transactionID [stun.TransactionIDSize]byte, msg stun.Message, addr net.Addr) func (p *Processor) AddPendingTransactionListener(transactionID [stun.TransactionIDSize]byte, handler PendingTransactionHandler) { p.mux.Lock() defer p.mux.Unlock() p.pendingStunRequest[transactionID] = pendingTransaction{ handler: handler, createdAt: time.Now(), } } func (p *Processor) CancelTransaction(transactionID [stun.TransactionIDSize]byte) { p.mux.Lock() defer p.mux.Unlock() delete(p.pendingStunRequest, transactionID) } func (p *Processor) ExpiredTransaction(newerThanThisTimeOrExpire time.Time) int { p.mux.Lock() defer p.mux.Unlock() expired := 0 for id, pt := range p.pendingStunRequest { if pt.createdAt.Before(newerThanThisTimeOrExpire) { delete(p.pendingStunRequest, id) expired++ } } return expired } ================================================ FILE: common/natTraversal/stun/stunClientConn.go ================================================ package stun import ( "errors" "time" "github.com/pion/stun/v3" "github.com/v2fly/v2ray-core/v5/common/net" ) var ErrTimeout = errors.New("STUN transaction timed out") func NewStunClientConn(conn net.PacketConn) (*StunClientConn, error) { processor := NewProcessor() filtered, err := NewFilteredConnection(conn, processor.HandleStunPacket) if err != nil { return nil, err } return &StunClientConn{ PacketConn: filtered, processor: processor, }, nil } type StunClientConn struct { net.PacketConn processor *Processor } func (conn *StunClientConn) ExecuteSTUNMessage(msg stun.Message, dest net.Addr, timeout time.Duration) (resp stun.Message, addr net.Addr, err error) { type result struct { msg stun.Message addr net.Addr } ch := make(chan result, 1) _, _, err = conn.ExecuteSTUNMessageAsync(msg, dest, func(_ [stun.TransactionIDSize]byte, respMsg stun.Message, respAddr net.Addr) { ch <- result{msg: respMsg, addr: respAddr} }) if err != nil { return resp, nil, err } select { case r := <-ch: return r.msg, r.addr, nil case <-time.After(timeout): conn.processor.CancelTransaction(msg.TransactionID) return resp, nil, ErrTimeout } } func (conn *StunClientConn) ExecuteSTUNMessageAsync(msg stun.Message, dest net.Addr, callback PendingTransactionHandler) (resp stun.Message, addr net.Addr, err error) { msg.Encode() conn.processor.AddPendingTransactionListener(msg.TransactionID, callback) if _, err = conn.WriteTo(msg.Raw, dest); err != nil { conn.processor.CancelTransaction(msg.TransactionID) return resp, nil, err } return resp, nil, nil } ================================================ FILE: common/natTraversal/stun/stuncli/stuncli.go ================================================ package stuncli // Mostly machine generated code import ( "flag" "fmt" "net" "time" "github.com/v2fly/v2ray-core/v5/common/buf" stunlib "github.com/v2fly/v2ray-core/v5/common/natTraversal/stun" vnet "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/main/commands/all/engineering" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/proxy/socks" ) var ( server *string server2 *string timeout *int attempts *int socks5udp *string ) var cmdStunTest = &base.Command{ UsageLine: "{{.Exec}} engineering stun-nat-type-discovery", Short: "run STUN NAT type tests", Long: ` Run STUN NAT behavior discovery tests (RFC 5780) against a STUN server. Tests NAT filtering, mapping, and hairpin behavior, then reports results. The STUN server must support RFC 5780 (OTHER-ADDRESS and CHANGE-REQUEST) for full test coverage. Usage: {{.Exec}} engineering stun-test -server [-server2 ] [-timeout ] [-attempts ] [-socks5udp ] Options: -server The STUN server address (required) -server2 A secondary STUN server address for cross-server mapping stability test -timeout Timeout per test in milliseconds (default: 3000) -attempts Number of parallel requests per test for UDP loss resilience (default: 3) -socks5udp SOCKS5 UDP relay address (skips TCP handshake, sends UDP directly) Example: {{.Exec}} engineering stun-test -server stun.example.com:3478 {{.Exec}} engineering stun-test -server stun.example.com:3478 -server2 stun2.example.com:3478 {{.Exec}} engineering stun-test -server stun.example.com:3478 -socks5udp 127.0.0.1:1080 `, Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) server = fs.String("server", "", "STUN server address (host:port)") server2 = fs.String("server2", "", "secondary STUN server address (host:port)") timeout = fs.Int("timeout", 3000, "timeout per test in milliseconds") attempts = fs.Int("attempts", 3, "number of parallel requests per test") socks5udp = fs.String("socks5udp", "", "SOCKS5 UDP relay address (host:port)") return *fs }(), Run: executeStunTest, } func init() { engineering.AddCommand(cmdStunTest) } // socks5UDPConn wraps a PacketConn to encapsulate/decapsulate SOCKS5 UDP packets. // All outgoing packets are wrapped in a SOCKS5 UDP header and sent to the relay. // All incoming packets are unwrapped, with the real source address extracted from the header. type socks5UDPConn struct { net.PacketConn relayAddr net.Addr } func (c *socks5UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { udpAddr := addr.(*net.UDPAddr) dest := vnet.UDPDestination(vnet.IPAddress(udpAddr.IP), vnet.Port(udpAddr.Port)) packet, err := socks.EncodeUDPPacketFromAddress(dest, p) if err != nil { return 0, err } defer packet.Release() _, err = c.PacketConn.WriteTo(packet.Bytes(), c.relayAddr) if err != nil { return 0, err } return len(p), nil } func (c *socks5UDPConn) ReadFrom(p []byte) (int, net.Addr, error) { // Allocate enough space for SOCKS5 header + payload rawBuf := make([]byte, len(p)+256) n, _, err := c.PacketConn.ReadFrom(rawBuf) if err != nil { return 0, nil, err } packet := buf.FromBytes(rawBuf[:n]) req, err := socks.DecodeUDPPacket(packet) if err != nil { return 0, nil, err } // After DecodeUDPPacket, packet.Bytes() contains the payload dataN := copy(p, packet.Bytes()) srcAddr := &net.UDPAddr{ IP: req.Address.IP(), Port: int(req.Port), } return dataN, srcAddr, nil } func natDependantTypeString(t stunlib.NATDependantType) string { switch t { case stunlib.Unknown: return "Unknown" case stunlib.Independent: return "Independent" case stunlib.EndpointDependent: return "Endpoint Dependent" case stunlib.EndpointPortDependent: return "Endpoint+Port Dependent" case stunlib.EndpointPortDependentPinned: return "Endpoint+Port Dependent (Pinned)" default: return fmt.Sprintf("Unknown(%d)", t) } } func natYesOrNoString(t stunlib.NATYesOrNoUnknownType) string { switch t { case stunlib.NATYesOrNoUnknownType_Unknown: return "Unknown" case stunlib.NATYesOrNoUnknownType_Yes: return "Yes" case stunlib.NATYesOrNoUnknownType_No: return "No" default: return fmt.Sprintf("Unknown(%d)", t) } } func executeStunTest(cmd *base.Command, args []string) { err := cmd.Flag.Parse(args) if err != nil { base.Fatalf("failed to parse flags: %v", err) } if *server == "" { base.Fatalf("-server is required") } host, portStr, err := net.SplitHostPort(*server) if err != nil { base.Fatalf("invalid server address %q: %v", *server, err) } ips, err := net.ResolveIPAddr("ip", host) if err != nil { base.Fatalf("failed to resolve %q: %v", host, err) } port, err := net.LookupPort("udp", portStr) if err != nil { base.Fatalf("invalid port %q: %v", portStr, err) } serverAddr := &net.UDPAddr{IP: ips.IP, Port: port} // Resolve secondary STUN server address if provided var server2Addr *net.UDPAddr if *server2 != "" { host2, portStr2, err := net.SplitHostPort(*server2) if err != nil { base.Fatalf("invalid server2 address %q: %v", *server2, err) } ips2, err := net.ResolveIPAddr("ip", host2) if err != nil { base.Fatalf("failed to resolve server2 host %q: %v", host2, err) } port2, err := net.LookupPort("udp", portStr2) if err != nil { base.Fatalf("invalid server2 port %q: %v", portStr2, err) } server2Addr = &net.UDPAddr{IP: ips2.IP, Port: port2} } // Resolve SOCKS5 UDP relay address if provided var relayAddr *net.UDPAddr if *socks5udp != "" { rHost, rPortStr, err := net.SplitHostPort(*socks5udp) if err != nil { base.Fatalf("invalid socks5udp address %q: %v", *socks5udp, err) } rIPs, err := net.ResolveIPAddr("ip", rHost) if err != nil { base.Fatalf("failed to resolve socks5udp host %q: %v", rHost, err) } rPort, err := net.LookupPort("udp", rPortStr) if err != nil { base.Fatalf("invalid socks5udp port %q: %v", rPortStr, err) } relayAddr = &net.UDPAddr{IP: rIPs.IP, Port: rPort} } fmt.Printf("STUN server: %s\n", serverAddr) if server2Addr != nil { fmt.Printf("STUN server 2: %s\n", server2Addr) } if relayAddr != nil { fmt.Printf("SOCKS5 UDP relay: %s\n", relayAddr) } fmt.Printf("Timeout: %dms, Attempts: %d\n\n", *timeout, *attempts) newConn := func() (net.PacketConn, error) { conn, err := net.ListenPacket("udp", ":0") if err != nil { return nil, err } if relayAddr != nil { return &socks5UDPConn{PacketConn: conn, relayAddr: relayAddr}, nil } return conn, nil } var secondaryServer net.Addr if server2Addr != nil { secondaryServer = server2Addr } test := stunlib.NewNATTypeTest( newConn, serverAddr, secondaryServer, time.Duration(*timeout)*time.Millisecond, *attempts, ) fmt.Println("Running tests...") if err := test.TestAll(); err != nil { base.Fatalf("test failed: %v", err) } fmt.Println() fmt.Println("=== NAT Behavior Test Results ===") fmt.Printf(" Filter Behaviour: %s\n", natDependantTypeString(test.FilterBehaviour)) fmt.Printf(" Mapping Behaviour: %s\n", natDependantTypeString(test.MappingBehaviour)) fmt.Printf(" Hairpin Behaviour: %s\n", natYesOrNoString(test.HairpinBehaviour)) fmt.Printf(" Stable Mapping on Secondary Server: %s\n", natYesOrNoString(test.StableMappingOnSecondaryServer)) fmt.Println() fmt.Println("=== Derived Properties ===") fmt.Printf(" Preserve Source Port (Source NAT): %s\n", natYesOrNoString(test.PreserveSourcePortWhenSourceNATMapping)) fmt.Printf(" Single Source IP (Source NAT): %s\n", natYesOrNoString(test.SingleSourceIPSourceNATMapping)) fmt.Printf(" Preserve Source Addr (Dest NAT Reply): %s\n", natYesOrNoString(test.PreserveSourceIPPortWhenDestNATMapping)) fmt.Println() fmt.Println("=== Source IPs ===") for _, ip := range test.SourceIPs { fmt.Printf(" %s\n", ip) } } ================================================ FILE: common/net/abstactOutbount/errors.generated.go ================================================ package abstactOutbount import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/net/abstactOutbount/outbound.go ================================================ package abstactOutbount //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/transport" ) func NewOutboundListener() *OutboundListener { return &OutboundListener{ buffer: make(chan *connWithContext, 4), done: done.New(), } } type connWithContext struct { net.Conn ctx context.Context } func (c *connWithContext) GetConnectionContext() context.Context { return c.ctx } // OutboundListener is a net.Listener for listening gRPC connections. type OutboundListener struct { buffer chan *connWithContext done *done.Instance } func (l *OutboundListener) addWithContext(ctx context.Context, conn net.Conn) { select { case l.buffer <- &connWithContext{Conn: conn, ctx: ctx}: case <-l.done.Wait(): conn.Close() default: conn.Close() } } // Accept implements net.Listener. func (l *OutboundListener) Accept() (net.Conn, error) { select { case <-l.done.Wait(): return nil, newError("listen closed") case c := <-l.buffer: return c, nil } } // Close implement net.Listener. func (l *OutboundListener) Close() error { common.Must(l.done.Close()) L: for { select { case c := <-l.buffer: c.Close() default: break L } } return nil } // Addr implements net.Listener. func (l *OutboundListener) Addr() net.Addr { return &net.TCPAddr{ IP: net.IP{0, 0, 0, 0}, Port: 0, } } func NewOutbound(tag string, listener *OutboundListener) *Outbound { return &Outbound{ tag: tag, listener: listener, } } // Outbound is a outbound.Handler that handles gRPC connections. type Outbound struct { tag string listener *OutboundListener access sync.RWMutex closed bool } // Dispatch implements outbound.Handler. func (co *Outbound) Dispatch(ctx context.Context, link *transport.Link) { co.access.RLock() if co.closed { common.Interrupt(link.Reader) common.Interrupt(link.Writer) co.access.RUnlock() return } closeSignal := done.New() c := net.NewConnection(net.ConnectionInputMulti(link.Writer), net.ConnectionOutputMulti(link.Reader), net.ConnectionOnClose(closeSignal)) co.listener.addWithContext(ctx, c) co.access.RUnlock() <-closeSignal.Wait() } // Tag implements outbound.Handler. func (co *Outbound) Tag() string { return co.tag } // Start implements common.Runnable. func (co *Outbound) Start() error { co.access.Lock() co.closed = false co.access.Unlock() return nil } // Close implements common.Closable. func (co *Outbound) Close() error { co.access.Lock() defer co.access.Unlock() co.closed = true return co.listener.Close() } ================================================ FILE: common/net/address.go ================================================ package net import ( "bytes" "encoding/json" "net" "strings" "github.com/golang/protobuf/jsonpb" ) var ( // LocalHostIP is a constant value for localhost IP in IPv4. LocalHostIP = IPAddress([]byte{127, 0, 0, 1}) // AnyIP is a constant value for any IP in IPv4. AnyIP = IPAddress([]byte{0, 0, 0, 0}) // LocalHostDomain is a constant value for localhost domain. LocalHostDomain = DomainAddress("localhost") // LocalHostIPv6 is a constant value for localhost IP in IPv6. LocalHostIPv6 = IPAddress([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) // AnyIPv6 is a constant value for any IP in IPv6. AnyIPv6 = IPAddress([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) ) // AddressFamily is the type of address. type AddressFamily byte const ( // AddressFamilyIPv4 represents address as IPv4 AddressFamilyIPv4 = AddressFamily(0) // AddressFamilyIPv6 represents address as IPv6 AddressFamilyIPv6 = AddressFamily(1) // AddressFamilyDomain represents address as Domain AddressFamilyDomain = AddressFamily(2) ) // IsIPv4 returns true if current AddressFamily is IPv4. func (af AddressFamily) IsIPv4() bool { return af == AddressFamilyIPv4 } // IsIPv6 returns true if current AddressFamily is IPv6. func (af AddressFamily) IsIPv6() bool { return af == AddressFamilyIPv6 } // IsIP returns true if current AddressFamily is IPv6 or IPv4. func (af AddressFamily) IsIP() bool { return af == AddressFamilyIPv4 || af == AddressFamilyIPv6 } // IsDomain returns true if current AddressFamily is Domain. func (af AddressFamily) IsDomain() bool { return af == AddressFamilyDomain } // Address represents a network address to be communicated with. It may be an IP address or domain // address, not both. This interface doesn't resolve IP address for a given domain. type Address interface { IP() net.IP // IP of this Address Domain() string // Domain of this Address Family() AddressFamily String() string // String representation of this Address } func isAlphaNum(c byte) bool { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } // ParseAddress parses a string into an Address. The return value will be an IPAddress when // the string is in the form of IPv4 or IPv6 address, or a DomainAddress otherwise. func ParseAddress(addr string) Address { // Handle IPv6 address in form as "[2001:4860:0:2001::68]" lenAddr := len(addr) if lenAddr > 0 && addr[0] == '[' && addr[lenAddr-1] == ']' { addr = addr[1 : lenAddr-1] lenAddr -= 2 } if lenAddr > 0 && (!isAlphaNum(addr[0]) || !isAlphaNum(addr[len(addr)-1])) { addr = strings.TrimSpace(addr) } ip := net.ParseIP(addr) if ip != nil { return IPAddress(ip) } return DomainAddress(addr) } var bytes0 = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // IPAddress creates an Address with given IP. func IPAddress(ip []byte) Address { switch len(ip) { case net.IPv4len: var addr ipv4Address = [4]byte{ip[0], ip[1], ip[2], ip[3]} return addr case net.IPv6len: if bytes.Equal(ip[:10], bytes0) && ip[10] == 0xff && ip[11] == 0xff { return IPAddress(ip[12:16]) } var addr ipv6Address = [16]byte{ ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15], } return addr default: newError("invalid IP format: ", ip).AtError().WriteToLog() return nil } } // DomainAddress creates an Address with given domain. func DomainAddress(domain string) Address { return domainAddress(domain) } type ipv4Address [4]byte func (a ipv4Address) IP() net.IP { return net.IP(a[:]) } func (ipv4Address) Domain() string { panic("Calling Domain() on an IPv4Address.") } func (ipv4Address) Family() AddressFamily { return AddressFamilyIPv4 } func (a ipv4Address) String() string { return a.IP().String() } type ipv6Address [16]byte func (a ipv6Address) IP() net.IP { return net.IP(a[:]) } func (ipv6Address) Domain() string { panic("Calling Domain() on an IPv6Address.") } func (ipv6Address) Family() AddressFamily { return AddressFamilyIPv6 } func (a ipv6Address) String() string { return "[" + a.IP().String() + "]" } type domainAddress string func (domainAddress) IP() net.IP { panic("Calling IP() on a DomainAddress.") } func (a domainAddress) Domain() string { return string(a) } func (domainAddress) Family() AddressFamily { return AddressFamilyDomain } func (a domainAddress) String() string { return a.Domain() } // AsAddress translates IPOrDomain to Address. func (d *IPOrDomain) AsAddress() Address { if d == nil { return nil } switch addr := d.Address.(type) { case *IPOrDomain_Ip: return IPAddress(addr.Ip) case *IPOrDomain_Domain: return DomainAddress(addr.Domain) } panic("Common|Net: Invalid address.") } // NewIPOrDomain translates Address to IPOrDomain func NewIPOrDomain(addr Address) *IPOrDomain { switch addr.Family() { case AddressFamilyDomain: return &IPOrDomain{ Address: &IPOrDomain_Domain{ Domain: addr.Domain(), }, } case AddressFamilyIPv4, AddressFamilyIPv6: return &IPOrDomain{ Address: &IPOrDomain_Ip{ Ip: addr.IP(), }, } default: panic("Unknown Address type.") } } func (d *IPOrDomain) UnmarshalJSONPB(unmarshaler *jsonpb.Unmarshaler, bytes []byte) error { var ipOrDomain string if err := json.Unmarshal(bytes, &ipOrDomain); err != nil { return err } result := NewIPOrDomain(ParseAddress(ipOrDomain)) d.Address = result.Address return nil } func (d *IPOrDomain) MarshalJSONPB(marshaler *jsonpb.Marshaler) ([]byte, error) { ipod := d.AsAddress().String() return json.Marshal(ipod) } ================================================ FILE: common/net/address.pb.go ================================================ package net import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Address of a network host. It may be either an IP address or a domain // address. type IPOrDomain struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Address: // // *IPOrDomain_Ip // *IPOrDomain_Domain Address isIPOrDomain_Address `protobuf_oneof:"address"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *IPOrDomain) Reset() { *x = IPOrDomain{} mi := &file_common_net_address_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *IPOrDomain) String() string { return protoimpl.X.MessageStringOf(x) } func (*IPOrDomain) ProtoMessage() {} func (x *IPOrDomain) ProtoReflect() protoreflect.Message { mi := &file_common_net_address_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use IPOrDomain.ProtoReflect.Descriptor instead. func (*IPOrDomain) Descriptor() ([]byte, []int) { return file_common_net_address_proto_rawDescGZIP(), []int{0} } func (x *IPOrDomain) GetAddress() isIPOrDomain_Address { if x != nil { return x.Address } return nil } func (x *IPOrDomain) GetIp() []byte { if x != nil { if x, ok := x.Address.(*IPOrDomain_Ip); ok { return x.Ip } } return nil } func (x *IPOrDomain) GetDomain() string { if x != nil { if x, ok := x.Address.(*IPOrDomain_Domain); ok { return x.Domain } } return "" } type isIPOrDomain_Address interface { isIPOrDomain_Address() } type IPOrDomain_Ip struct { // IP address. Must by either 4 or 16 bytes. Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3,oneof"` } type IPOrDomain_Domain struct { // Domain address. Domain string `protobuf:"bytes,2,opt,name=domain,proto3,oneof"` } func (*IPOrDomain_Ip) isIPOrDomain_Address() {} func (*IPOrDomain_Domain) isIPOrDomain_Address() {} var File_common_net_address_proto protoreflect.FileDescriptor const file_common_net_address_proto_rawDesc = "" + "\n" + "\x18common/net/address.proto\x12\x15v2ray.core.common.net\"C\n" + "\n" + "IPOrDomain\x12\x10\n" + "\x02ip\x18\x01 \x01(\fH\x00R\x02ip\x12\x18\n" + "\x06domain\x18\x02 \x01(\tH\x00R\x06domainB\t\n" + "\aaddressB`\n" + "\x19com.v2ray.core.common.netP\x01Z)github.com/v2fly/v2ray-core/v5/common/net\xaa\x02\x15V2Ray.Core.Common.Netb\x06proto3" var ( file_common_net_address_proto_rawDescOnce sync.Once file_common_net_address_proto_rawDescData []byte ) func file_common_net_address_proto_rawDescGZIP() []byte { file_common_net_address_proto_rawDescOnce.Do(func() { file_common_net_address_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_net_address_proto_rawDesc), len(file_common_net_address_proto_rawDesc))) }) return file_common_net_address_proto_rawDescData } var file_common_net_address_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_common_net_address_proto_goTypes = []any{ (*IPOrDomain)(nil), // 0: v2ray.core.common.net.IPOrDomain } var file_common_net_address_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_common_net_address_proto_init() } func file_common_net_address_proto_init() { if File_common_net_address_proto != nil { return } file_common_net_address_proto_msgTypes[0].OneofWrappers = []any{ (*IPOrDomain_Ip)(nil), (*IPOrDomain_Domain)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_net_address_proto_rawDesc), len(file_common_net_address_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_net_address_proto_goTypes, DependencyIndexes: file_common_net_address_proto_depIdxs, MessageInfos: file_common_net_address_proto_msgTypes, }.Build() File_common_net_address_proto = out.File file_common_net_address_proto_goTypes = nil file_common_net_address_proto_depIdxs = nil } ================================================ FILE: common/net/address.proto ================================================ syntax = "proto3"; package v2ray.core.common.net; option csharp_namespace = "V2Ray.Core.Common.Net"; option go_package = "github.com/v2fly/v2ray-core/v5/common/net"; option java_package = "com.v2ray.core.common.net"; option java_multiple_files = true; // Address of a network host. It may be either an IP address or a domain // address. message IPOrDomain { oneof address { // IP address. Must by either 4 or 16 bytes. bytes ip = 1; // Domain address. string domain = 2; } } ================================================ FILE: common/net/address_test.go ================================================ package net_test import ( "net" "testing" "github.com/google/go-cmp/cmp" . "github.com/v2fly/v2ray-core/v5/common/net" ) func TestAddressProperty(t *testing.T) { type addrProprty struct { IP []byte Domain string Family AddressFamily String string } testCases := []struct { Input Address Output addrProprty }{ { Input: IPAddress([]byte{byte(1), byte(2), byte(3), byte(4)}), Output: addrProprty{ IP: []byte{byte(1), byte(2), byte(3), byte(4)}, Family: AddressFamilyIPv4, String: "1.2.3.4", }, }, { Input: IPAddress([]byte{ byte(1), byte(2), byte(3), byte(4), byte(1), byte(2), byte(3), byte(4), byte(1), byte(2), byte(3), byte(4), byte(1), byte(2), byte(3), byte(4), }), Output: addrProprty{ IP: []byte{ byte(1), byte(2), byte(3), byte(4), byte(1), byte(2), byte(3), byte(4), byte(1), byte(2), byte(3), byte(4), byte(1), byte(2), byte(3), byte(4), }, Family: AddressFamilyIPv6, String: "[102:304:102:304:102:304:102:304]", }, }, { Input: IPAddress([]byte{ byte(0), byte(0), byte(0), byte(0), byte(0), byte(0), byte(0), byte(0), byte(0), byte(0), byte(255), byte(255), byte(1), byte(2), byte(3), byte(4), }), Output: addrProprty{ IP: []byte{byte(1), byte(2), byte(3), byte(4)}, Family: AddressFamilyIPv4, String: "1.2.3.4", }, }, { Input: DomainAddress("v2fly.org"), Output: addrProprty{ Domain: "v2fly.org", Family: AddressFamilyDomain, String: "v2fly.org", }, }, { Input: IPAddress(net.IPv4(1, 2, 3, 4)), Output: addrProprty{ IP: []byte{byte(1), byte(2), byte(3), byte(4)}, Family: AddressFamilyIPv4, String: "1.2.3.4", }, }, { Input: ParseAddress("[2001:4860:0:2001::68]"), Output: addrProprty{ IP: []byte{0x20, 0x01, 0x48, 0x60, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68}, Family: AddressFamilyIPv6, String: "[2001:4860:0:2001::68]", }, }, { Input: ParseAddress("::0"), Output: addrProprty{ IP: AnyIPv6.IP(), Family: AddressFamilyIPv6, String: "[::]", }, }, { Input: ParseAddress("[::ffff:123.151.71.143]"), Output: addrProprty{ IP: []byte{123, 151, 71, 143}, Family: AddressFamilyIPv4, String: "123.151.71.143", }, }, { Input: NewIPOrDomain(ParseAddress("v2fly.org")).AsAddress(), Output: addrProprty{ Domain: "v2fly.org", Family: AddressFamilyDomain, String: "v2fly.org", }, }, { Input: NewIPOrDomain(ParseAddress("8.8.8.8")).AsAddress(), Output: addrProprty{ IP: []byte{8, 8, 8, 8}, Family: AddressFamilyIPv4, String: "8.8.8.8", }, }, { Input: NewIPOrDomain(ParseAddress("[2001:4860:0:2001::68]")).AsAddress(), Output: addrProprty{ IP: []byte{0x20, 0x01, 0x48, 0x60, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68}, Family: AddressFamilyIPv6, String: "[2001:4860:0:2001::68]", }, }, } for _, testCase := range testCases { actual := addrProprty{ Family: testCase.Input.Family(), String: testCase.Input.String(), } if testCase.Input.Family().IsIP() { actual.IP = testCase.Input.IP() } else { actual.Domain = testCase.Input.Domain() } if r := cmp.Diff(actual, testCase.Output); r != "" { t.Error("for input: ", testCase.Input, ":", r) } } } func TestInvalidAddressConvertion(t *testing.T) { panics := func(f func()) (ret bool) { defer func() { if r := recover(); r != nil { ret = true } }() f() return false } testCases := []func(){ func() { ParseAddress("8.8.8.8").Domain() }, func() { ParseAddress("2001:4860:0:2001::68").Domain() }, func() { ParseAddress("v2fly.org").IP() }, } for idx, testCase := range testCases { if !panics(testCase) { t.Error("case ", idx, " failed") } } } func BenchmarkParseAddressIPv4(b *testing.B) { for i := 0; i < b.N; i++ { addr := ParseAddress("8.8.8.8") if addr.Family() != AddressFamilyIPv4 { panic("not ipv4") } } } func BenchmarkParseAddressIPv6(b *testing.B) { for i := 0; i < b.N; i++ { addr := ParseAddress("2001:4860:0:2001::68") if addr.Family() != AddressFamilyIPv6 { panic("not ipv6") } } } func BenchmarkParseAddressDomain(b *testing.B) { for i := 0; i < b.N; i++ { addr := ParseAddress("v2fly.org") if addr.Family() != AddressFamilyDomain { panic("not domain") } } } ================================================ FILE: common/net/connection.go ================================================ package net import ( "io" "net" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/signal/done" ) type ConnectionOption func(*connection) func ConnectionLocalAddr(a net.Addr) ConnectionOption { return func(c *connection) { c.local = a } } func ConnectionRemoteAddr(a net.Addr) ConnectionOption { return func(c *connection) { c.remote = a } } func ConnectionInput(writer io.Writer) ConnectionOption { return func(c *connection) { c.writer = buf.NewWriter(writer) } } func ConnectionInputMulti(writer buf.Writer) ConnectionOption { return func(c *connection) { c.writer = writer } } func ConnectionOutput(reader io.Reader) ConnectionOption { return func(c *connection) { c.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)} } } func ConnectionOutputMulti(reader buf.Reader) ConnectionOption { return func(c *connection) { c.reader = &buf.BufferedReader{Reader: reader} } } func ConnectionOutputMultiUDP(reader buf.Reader) ConnectionOption { return func(c *connection) { c.reader = &buf.BufferedReader{ Reader: reader, Spliter: buf.SplitFirstBytes, } } } func ConnectionOnClose(n io.Closer) ConnectionOption { return func(c *connection) { c.onClose = n } } func NewConnection(opts ...ConnectionOption) net.Conn { c := &connection{ done: done.New(), local: &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, Port: 0, }, remote: &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, Port: 0, }, } for _, opt := range opts { opt(c) } return c } type connection struct { reader *buf.BufferedReader writer buf.Writer done *done.Instance onClose io.Closer local Addr remote Addr } func (c *connection) Read(b []byte) (int, error) { return c.reader.Read(b) } // ReadMultiBuffer implements buf.Reader. func (c *connection) ReadMultiBuffer() (buf.MultiBuffer, error) { return c.reader.ReadMultiBuffer() } // Write implements net.Conn.Write(). func (c *connection) Write(b []byte) (int, error) { if c.done.Done() { return 0, io.ErrClosedPipe } if len(b)/buf.Size+1 > 64*1024*1024 { return 0, errors.New("value too large") } l := len(b) sliceSize := l/buf.Size + 1 mb := make(buf.MultiBuffer, 0, sliceSize) mb = buf.MergeBytes(mb, b) return l, c.writer.WriteMultiBuffer(mb) } func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { if c.done.Done() { buf.ReleaseMulti(mb) return io.ErrClosedPipe } return c.writer.WriteMultiBuffer(mb) } // Close implements net.Conn.Close(). func (c *connection) Close() error { common.Must(c.done.Close()) common.Interrupt(c.reader) common.Close(c.writer) if c.onClose != nil { return c.onClose.Close() } return nil } // LocalAddr implements net.Conn.LocalAddr(). func (c *connection) LocalAddr() net.Addr { return c.local } // RemoteAddr implements net.Conn.RemoteAddr(). func (c *connection) RemoteAddr() net.Addr { return c.remote } // SetDeadline implements net.Conn.SetDeadline(). func (c *connection) SetDeadline(t time.Time) error { return nil } // SetReadDeadline implements net.Conn.SetReadDeadline(). func (c *connection) SetReadDeadline(t time.Time) error { return nil } // SetWriteDeadline implements net.Conn.SetWriteDeadline(). func (c *connection) SetWriteDeadline(t time.Time) error { return nil } ================================================ FILE: common/net/destination.go ================================================ package net import ( "net" "strings" ) // Destination represents a network destination including address and protocol (tcp / udp). type Destination struct { Address Address Port Port Network Network } // DestinationFromAddr generates a Destination from a net address. func DestinationFromAddr(addr net.Addr) Destination { switch addr := addr.(type) { case *net.TCPAddr: return TCPDestination(IPAddress(addr.IP), Port(addr.Port)) case *net.UDPAddr: return UDPDestination(IPAddress(addr.IP), Port(addr.Port)) case *net.UnixAddr: return UnixDestination(DomainAddress(addr.Name)) default: panic("Net: Unknown address type.") } } // ParseDestination converts a destination from its string presentation. func ParseDestination(dest string) (Destination, error) { d := Destination{ Address: AnyIP, Port: Port(0), } switch { case strings.HasPrefix(dest, "tcp:"): d.Network = Network_TCP dest = dest[4:] case strings.HasPrefix(dest, "udp:"): d.Network = Network_UDP dest = dest[4:] case strings.HasPrefix(dest, "unix:"): d = UnixDestination(DomainAddress(dest[5:])) return d, nil } hstr, pstr, err := SplitHostPort(dest) if err != nil { return d, err } if len(hstr) > 0 { d.Address = ParseAddress(hstr) } if len(pstr) > 0 { port, err := PortFromString(pstr) if err != nil { return d, err } d.Port = port } return d, nil } // TCPDestination creates a TCP destination with given address func TCPDestination(address Address, port Port) Destination { return Destination{ Network: Network_TCP, Address: address, Port: port, } } // UDPDestination creates a UDP destination with given address func UDPDestination(address Address, port Port) Destination { return Destination{ Network: Network_UDP, Address: address, Port: port, } } // UnixDestination creates a Unix destination with given address func UnixDestination(address Address) Destination { return Destination{ Network: Network_UNIX, Address: address, } } // NetAddr returns the network address in this Destination in string form. func (d Destination) NetAddr() string { addr := "" if d.Network == Network_TCP || d.Network == Network_UDP { addr = d.Address.String() + ":" + d.Port.String() } else if d.Network == Network_UNIX { addr = d.Address.String() } return addr } // String returns the strings form of this Destination. func (d Destination) String() string { prefix := "unknown:" switch d.Network { case Network_TCP: prefix = "tcp:" case Network_UDP: prefix = "udp:" case Network_UNIX: prefix = "unix:" } return prefix + d.NetAddr() } // IsValid returns true if this Destination is valid. func (d Destination) IsValid() bool { return d.Network != Network_Unknown } // AsDestination converts current Endpoint into Destination. func (p *Endpoint) AsDestination() Destination { return Destination{ Network: p.Network, Address: p.Address.AsAddress(), Port: Port(p.Port), } } ================================================ FILE: common/net/destination.pb.go ================================================ package net import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Endpoint of a network connection. type Endpoint struct { state protoimpl.MessageState `protogen:"open.v1"` Network Network `protobuf:"varint,1,opt,name=network,proto3,enum=v2ray.core.common.net.Network" json:"network,omitempty"` Address *IPOrDomain `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Endpoint) Reset() { *x = Endpoint{} mi := &file_common_net_destination_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Endpoint) String() string { return protoimpl.X.MessageStringOf(x) } func (*Endpoint) ProtoMessage() {} func (x *Endpoint) ProtoReflect() protoreflect.Message { mi := &file_common_net_destination_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Endpoint.ProtoReflect.Descriptor instead. func (*Endpoint) Descriptor() ([]byte, []int) { return file_common_net_destination_proto_rawDescGZIP(), []int{0} } func (x *Endpoint) GetNetwork() Network { if x != nil { return x.Network } return Network_Unknown } func (x *Endpoint) GetAddress() *IPOrDomain { if x != nil { return x.Address } return nil } func (x *Endpoint) GetPort() uint32 { if x != nil { return x.Port } return 0 } var File_common_net_destination_proto protoreflect.FileDescriptor const file_common_net_destination_proto_rawDesc = "" + "\n" + "\x1ccommon/net/destination.proto\x12\x15v2ray.core.common.net\x1a\x18common/net/network.proto\x1a\x18common/net/address.proto\"\x95\x01\n" + "\bEndpoint\x128\n" + "\anetwork\x18\x01 \x01(\x0e2\x1e.v2ray.core.common.net.NetworkR\anetwork\x12;\n" + "\aaddress\x18\x02 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x03 \x01(\rR\x04portB`\n" + "\x19com.v2ray.core.common.netP\x01Z)github.com/v2fly/v2ray-core/v5/common/net\xaa\x02\x15V2Ray.Core.Common.Netb\x06proto3" var ( file_common_net_destination_proto_rawDescOnce sync.Once file_common_net_destination_proto_rawDescData []byte ) func file_common_net_destination_proto_rawDescGZIP() []byte { file_common_net_destination_proto_rawDescOnce.Do(func() { file_common_net_destination_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_net_destination_proto_rawDesc), len(file_common_net_destination_proto_rawDesc))) }) return file_common_net_destination_proto_rawDescData } var file_common_net_destination_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_common_net_destination_proto_goTypes = []any{ (*Endpoint)(nil), // 0: v2ray.core.common.net.Endpoint (Network)(0), // 1: v2ray.core.common.net.Network (*IPOrDomain)(nil), // 2: v2ray.core.common.net.IPOrDomain } var file_common_net_destination_proto_depIdxs = []int32{ 1, // 0: v2ray.core.common.net.Endpoint.network:type_name -> v2ray.core.common.net.Network 2, // 1: v2ray.core.common.net.Endpoint.address:type_name -> v2ray.core.common.net.IPOrDomain 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_common_net_destination_proto_init() } func file_common_net_destination_proto_init() { if File_common_net_destination_proto != nil { return } file_common_net_network_proto_init() file_common_net_address_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_net_destination_proto_rawDesc), len(file_common_net_destination_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_net_destination_proto_goTypes, DependencyIndexes: file_common_net_destination_proto_depIdxs, MessageInfos: file_common_net_destination_proto_msgTypes, }.Build() File_common_net_destination_proto = out.File file_common_net_destination_proto_goTypes = nil file_common_net_destination_proto_depIdxs = nil } ================================================ FILE: common/net/destination.proto ================================================ syntax = "proto3"; package v2ray.core.common.net; option csharp_namespace = "V2Ray.Core.Common.Net"; option go_package = "github.com/v2fly/v2ray-core/v5/common/net"; option java_package = "com.v2ray.core.common.net"; option java_multiple_files = true; import "common/net/network.proto"; import "common/net/address.proto"; // Endpoint of a network connection. message Endpoint { Network network = 1; IPOrDomain address = 2; uint32 port = 3; } ================================================ FILE: common/net/destination_test.go ================================================ package net_test import ( "testing" "github.com/google/go-cmp/cmp" . "github.com/v2fly/v2ray-core/v5/common/net" ) func TestDestinationProperty(t *testing.T) { testCases := []struct { Input Destination Network Network String string NetString string }{ { Input: TCPDestination(IPAddress([]byte{1, 2, 3, 4}), 80), Network: Network_TCP, String: "tcp:1.2.3.4:80", NetString: "1.2.3.4:80", }, { Input: UDPDestination(IPAddress([]byte{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}), 53), Network: Network_UDP, String: "udp:[2001:4860:4860::8888]:53", NetString: "[2001:4860:4860::8888]:53", }, { Input: UnixDestination(DomainAddress("/tmp/test.sock")), Network: Network_UNIX, String: "unix:/tmp/test.sock", NetString: "/tmp/test.sock", }, } for _, testCase := range testCases { dest := testCase.Input if r := cmp.Diff(dest.Network, testCase.Network); r != "" { t.Error("unexpected Network in ", dest.String(), ": ", r) } if r := cmp.Diff(dest.String(), testCase.String); r != "" { t.Error(r) } if r := cmp.Diff(dest.NetAddr(), testCase.NetString); r != "" { t.Error(r) } } } func TestDestinationParse(t *testing.T) { cases := []struct { Input string Output Destination Error bool }{ { Input: "tcp:127.0.0.1:80", Output: TCPDestination(LocalHostIP, Port(80)), }, { Input: "udp:8.8.8.8:53", Output: UDPDestination(IPAddress([]byte{8, 8, 8, 8}), Port(53)), }, { Input: "unix:/tmp/test.sock", Output: UnixDestination(DomainAddress("/tmp/test.sock")), }, { Input: "8.8.8.8:53", Output: Destination{ Address: IPAddress([]byte{8, 8, 8, 8}), Port: Port(53), }, }, { Input: ":53", Output: Destination{ Address: AnyIP, Port: Port(53), }, }, { Input: "8.8.8.8", Error: true, }, { Input: "8.8.8.8:http", Error: true, }, { Input: "/tmp/test.sock", Error: true, }, } for _, testcase := range cases { d, err := ParseDestination(testcase.Input) if !testcase.Error { if err != nil { t.Error("for test case: ", testcase.Input, " expected no error, but got ", err) } if d != testcase.Output { t.Error("for test case: ", testcase.Input, " expected output: ", testcase.Output.String(), " but got ", d.String()) } } else if err == nil { t.Error("for test case: ", testcase.Input, " expected error, but got nil") } } } ================================================ FILE: common/net/errors.generated.go ================================================ package net import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/net/net.go ================================================ // Package net is a drop-in replacement to Golang's net package, with some more functionalities. package net //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: common/net/network.go ================================================ package net import ( "encoding/json" "strings" "github.com/golang/protobuf/jsonpb" ) func (n Network) SystemString() string { switch n { case Network_TCP: return "tcp" case Network_UDP: return "udp" case Network_UNIX: return "unix" default: return "unknown" } } func (nl *NetworkList) UnmarshalJSONPB(unmarshaler *jsonpb.Unmarshaler, bytes []byte) error { var networkStrList []string if err := json.Unmarshal(bytes, &networkStrList); err == nil { nl.Network = ParseNetworkStringList(networkStrList) return nil } var networkStr string if err := json.Unmarshal(bytes, &networkStr); err == nil { strList := strings.Split(networkStr, ",") nl.Network = ParseNetworkStringList(strList) return nil } return newError("unknown format of a string list: " + string(bytes)) } func (nl *NetworkList) MarshalJSONPB(marshaler *jsonpb.Marshaler) ([]byte, error) { networkStrList := make([]string, len(nl.Network)) for idx, network := range nl.Network { networkStrList[idx] = network.String() } return json.Marshal(networkStrList) } // HasNetwork returns true if the network list has a certain network. func HasNetwork(list []Network, network Network) bool { for _, value := range list { if value == network { return true } } return false } func ParseNetwork(net string) Network { switch strings.ToLower(net) { case "tcp": return Network_TCP case "udp": return Network_UDP case "unix": return Network_UNIX default: return Network_Unknown } } func ParseNetworkStringList(strList []string) []Network { list := make([]Network, len(strList)) for idx, str := range strList { list[idx] = ParseNetwork(str) } return list } ================================================ FILE: common/net/network.pb.go ================================================ package net import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Network int32 const ( Network_Unknown Network = 0 // Deprecated: Marked as deprecated in common/net/network.proto. Network_RawTCP Network = 1 Network_TCP Network = 2 Network_UDP Network = 3 Network_UNIX Network = 4 ) // Enum value maps for Network. var ( Network_name = map[int32]string{ 0: "Unknown", 1: "RawTCP", 2: "TCP", 3: "UDP", 4: "UNIX", } Network_value = map[string]int32{ "Unknown": 0, "RawTCP": 1, "TCP": 2, "UDP": 3, "UNIX": 4, } ) func (x Network) Enum() *Network { p := new(Network) *p = x return p } func (x Network) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Network) Descriptor() protoreflect.EnumDescriptor { return file_common_net_network_proto_enumTypes[0].Descriptor() } func (Network) Type() protoreflect.EnumType { return &file_common_net_network_proto_enumTypes[0] } func (x Network) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Network.Descriptor instead. func (Network) EnumDescriptor() ([]byte, []int) { return file_common_net_network_proto_rawDescGZIP(), []int{0} } // NetworkList is a list of Networks. type NetworkList struct { state protoimpl.MessageState `protogen:"open.v1"` Network []Network `protobuf:"varint,1,rep,packed,name=network,proto3,enum=v2ray.core.common.net.Network" json:"network,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetworkList) Reset() { *x = NetworkList{} mi := &file_common_net_network_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetworkList) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetworkList) ProtoMessage() {} func (x *NetworkList) ProtoReflect() protoreflect.Message { mi := &file_common_net_network_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetworkList.ProtoReflect.Descriptor instead. func (*NetworkList) Descriptor() ([]byte, []int) { return file_common_net_network_proto_rawDescGZIP(), []int{0} } func (x *NetworkList) GetNetwork() []Network { if x != nil { return x.Network } return nil } var File_common_net_network_proto protoreflect.FileDescriptor const file_common_net_network_proto_rawDesc = "" + "\n" + "\x18common/net/network.proto\x12\x15v2ray.core.common.net\"G\n" + "\vNetworkList\x128\n" + "\anetwork\x18\x01 \x03(\x0e2\x1e.v2ray.core.common.net.NetworkR\anetwork*B\n" + "\aNetwork\x12\v\n" + "\aUnknown\x10\x00\x12\x0e\n" + "\x06RawTCP\x10\x01\x1a\x02\b\x01\x12\a\n" + "\x03TCP\x10\x02\x12\a\n" + "\x03UDP\x10\x03\x12\b\n" + "\x04UNIX\x10\x04B`\n" + "\x19com.v2ray.core.common.netP\x01Z)github.com/v2fly/v2ray-core/v5/common/net\xaa\x02\x15V2Ray.Core.Common.Netb\x06proto3" var ( file_common_net_network_proto_rawDescOnce sync.Once file_common_net_network_proto_rawDescData []byte ) func file_common_net_network_proto_rawDescGZIP() []byte { file_common_net_network_proto_rawDescOnce.Do(func() { file_common_net_network_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_net_network_proto_rawDesc), len(file_common_net_network_proto_rawDesc))) }) return file_common_net_network_proto_rawDescData } var file_common_net_network_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_common_net_network_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_common_net_network_proto_goTypes = []any{ (Network)(0), // 0: v2ray.core.common.net.Network (*NetworkList)(nil), // 1: v2ray.core.common.net.NetworkList } var file_common_net_network_proto_depIdxs = []int32{ 0, // 0: v2ray.core.common.net.NetworkList.network:type_name -> v2ray.core.common.net.Network 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_common_net_network_proto_init() } func file_common_net_network_proto_init() { if File_common_net_network_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_net_network_proto_rawDesc), len(file_common_net_network_proto_rawDesc)), NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_net_network_proto_goTypes, DependencyIndexes: file_common_net_network_proto_depIdxs, EnumInfos: file_common_net_network_proto_enumTypes, MessageInfos: file_common_net_network_proto_msgTypes, }.Build() File_common_net_network_proto = out.File file_common_net_network_proto_goTypes = nil file_common_net_network_proto_depIdxs = nil } ================================================ FILE: common/net/network.proto ================================================ syntax = "proto3"; package v2ray.core.common.net; option csharp_namespace = "V2Ray.Core.Common.Net"; option go_package = "github.com/v2fly/v2ray-core/v5/common/net"; option java_package = "com.v2ray.core.common.net"; option java_multiple_files = true; enum Network { Unknown = 0; RawTCP = 1 [deprecated = true]; TCP = 2; UDP = 3; UNIX = 4; } // NetworkList is a list of Networks. message NetworkList { repeated Network network = 1; } ================================================ FILE: common/net/packetaddr/config.pb.go ================================================ package packetaddr import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type PacketAddrType int32 const ( PacketAddrType_None PacketAddrType = 0 PacketAddrType_Packet PacketAddrType = 1 ) // Enum value maps for PacketAddrType. var ( PacketAddrType_name = map[int32]string{ 0: "None", 1: "Packet", } PacketAddrType_value = map[string]int32{ "None": 0, "Packet": 1, } ) func (x PacketAddrType) Enum() *PacketAddrType { p := new(PacketAddrType) *p = x return p } func (x PacketAddrType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (PacketAddrType) Descriptor() protoreflect.EnumDescriptor { return file_common_net_packetaddr_config_proto_enumTypes[0].Descriptor() } func (PacketAddrType) Type() protoreflect.EnumType { return &file_common_net_packetaddr_config_proto_enumTypes[0] } func (x PacketAddrType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use PacketAddrType.Descriptor instead. func (PacketAddrType) EnumDescriptor() ([]byte, []int) { return file_common_net_packetaddr_config_proto_rawDescGZIP(), []int{0} } var File_common_net_packetaddr_config_proto protoreflect.FileDescriptor const file_common_net_packetaddr_config_proto_rawDesc = "" + "\n" + "\"common/net/packetaddr/config.proto\x12\x19v2ray.core.net.packetaddr*&\n" + "\x0ePacketAddrType\x12\b\n" + "\x04None\x10\x00\x12\n" + "\n" + "\x06Packet\x10\x01B\x81\x01\n" + "$com.v2ray.core.common.net.packetaddrP\x01Z4github.com/v2fly/v2ray-core/v5/common/net/packetaddr\xaa\x02 V2Ray.Core.Common.Net.Packetaddrb\x06proto3" var ( file_common_net_packetaddr_config_proto_rawDescOnce sync.Once file_common_net_packetaddr_config_proto_rawDescData []byte ) func file_common_net_packetaddr_config_proto_rawDescGZIP() []byte { file_common_net_packetaddr_config_proto_rawDescOnce.Do(func() { file_common_net_packetaddr_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_net_packetaddr_config_proto_rawDesc), len(file_common_net_packetaddr_config_proto_rawDesc))) }) return file_common_net_packetaddr_config_proto_rawDescData } var file_common_net_packetaddr_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_common_net_packetaddr_config_proto_goTypes = []any{ (PacketAddrType)(0), // 0: v2ray.core.net.packetaddr.PacketAddrType } var file_common_net_packetaddr_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_common_net_packetaddr_config_proto_init() } func file_common_net_packetaddr_config_proto_init() { if File_common_net_packetaddr_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_net_packetaddr_config_proto_rawDesc), len(file_common_net_packetaddr_config_proto_rawDesc)), NumEnums: 1, NumMessages: 0, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_net_packetaddr_config_proto_goTypes, DependencyIndexes: file_common_net_packetaddr_config_proto_depIdxs, EnumInfos: file_common_net_packetaddr_config_proto_enumTypes, }.Build() File_common_net_packetaddr_config_proto = out.File file_common_net_packetaddr_config_proto_goTypes = nil file_common_net_packetaddr_config_proto_depIdxs = nil } ================================================ FILE: common/net/packetaddr/config.proto ================================================ syntax = "proto3"; package v2ray.core.net.packetaddr; option csharp_namespace = "V2Ray.Core.Common.Net.Packetaddr"; option go_package = "github.com/v2fly/v2ray-core/v5/common/net/packetaddr"; option java_package = "com.v2ray.core.common.net.packetaddr"; option java_multiple_files = true; enum PacketAddrType { None = 0; Packet = 1; } ================================================ FILE: common/net/packetaddr/connection_adaptor.go ================================================ package packetaddr import ( "context" gonet "net" "sync" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport" ) var ( errNotPacketConn = errors.New("not a packet connection") errUnsupported = errors.New("unsupported action") ) func ToPacketAddrConn(link *transport.Link, dest net.Destination) (net.PacketConn, error) { if !dest.Address.Family().IsDomain() { return nil, errNotPacketConn } switch dest.Address.Domain() { case seqPacketMagicAddress: return &packetConnectionAdaptor{ readerAccess: &sync.Mutex{}, readerBuffer: nil, link: link, }, nil default: return nil, errNotPacketConn } } func CreatePacketAddrConn(ctx context.Context, dispatcher routing.Dispatcher, isStream bool) (net.PacketConn, error) { if isStream { return nil, errUnsupported } packetDest := net.Destination{ Address: net.DomainAddress(seqPacketMagicAddress), Port: 0, Network: net.Network_UDP, } link, err := dispatcher.Dispatch(ctx, packetDest) if err != nil { return nil, err } return &packetConnectionAdaptor{ readerAccess: &sync.Mutex{}, readerBuffer: nil, link: link, }, nil } type packetConnectionAdaptor struct { readerAccess *sync.Mutex readerBuffer buf.MultiBuffer link *transport.Link } func (c *packetConnectionAdaptor) ReadFrom(p []byte) (n int, addr gonet.Addr, err error) { c.readerAccess.Lock() defer c.readerAccess.Unlock() if c.readerBuffer.IsEmpty() { c.readerBuffer, err = c.link.Reader.ReadMultiBuffer() if err != nil { return 0, nil, err } } c.readerBuffer, n = buf.SplitFirstBytes(c.readerBuffer, p) var w *buf.Buffer w, addr, err = ExtractAddressFromPacket(buf.FromBytes(p[:n])) n = copy(p, w.Bytes()) w.Release() return } func (c *packetConnectionAdaptor) WriteTo(p []byte, addr gonet.Addr) (n int, err error) { _, ok := addr.(*gonet.UDPAddr) if !ok { // address other than UDPAddr is not supported, and will be dropped. return 0, nil } payloadLen := len(p) var buffer *buf.Buffer buffer, err = AttachAddressToPacket(buf.FromBytes(p), addr) if err != nil { return 0, err } mb := buf.MultiBuffer{buffer} err = c.link.Writer.WriteMultiBuffer(mb) if err != nil { return 0, err } return payloadLen, nil } func (c *packetConnectionAdaptor) Close() error { c.readerAccess.Lock() defer c.readerAccess.Unlock() c.readerBuffer = buf.ReleaseMulti(c.readerBuffer) return common.Interrupt(c.link) } func (c packetConnectionAdaptor) LocalAddr() gonet.Addr { return &gonet.UnixAddr{Name: "unsupported"} } func (c packetConnectionAdaptor) SetDeadline(t time.Time) error { return nil } func (c packetConnectionAdaptor) SetReadDeadline(t time.Time) error { return nil } func (c packetConnectionAdaptor) SetWriteDeadline(t time.Time) error { return nil } func ToPacketAddrConnWrapper(conn net.PacketConn, isStream bool) FusedConnection { return &packetConnWrapper{conn} } type packetConnWrapper struct { net.PacketConn } func (pc *packetConnWrapper) RemoteAddr() gonet.Addr { return nil } type FusedConnection interface { net.PacketConn net.Conn } func (pc *packetConnWrapper) Read(p []byte) (n int, err error) { recbuf := buf.StackNew() recbuf.Extend(2048) n, addr, err := pc.PacketConn.ReadFrom(recbuf.Bytes()) if err != nil { return 0, err } recbuf.Resize(0, int32(n)) result, err := AttachAddressToPacket(&recbuf, addr) if err != nil { return 0, err } n = copy(p, result.Bytes()) result.Release() return n, nil } func (pc *packetConnWrapper) Write(p []byte) (n int, err error) { data, addr, err := ExtractAddressFromPacket(buf.FromBytes(p)) if err != nil { return 0, err } _, err = pc.PacketConn.WriteTo(data.Bytes(), addr) if err != nil { return 0, err } data.Release() return len(p), nil } func (pc *packetConnWrapper) Close() error { return pc.PacketConn.Close() } func GetDestinationSubsetOf(dest net.Destination) (bool, error) { if !dest.Address.Family().IsDomain() { return false, errNotPacketConn } switch dest.Address.Domain() { case seqPacketMagicAddress: return false, nil default: return false, errNotPacketConn } } ================================================ FILE: common/net/packetaddr/packetaddr.go ================================================ package packetaddr import ( "bytes" gonet "net" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), protocol.AddressFamilyByte(0x02, net.AddressFamilyIPv6), ) // AttachAddressToPacket // relinquish ownership of data // gain ownership of the returning value func AttachAddressToPacket(data *buf.Buffer, address gonet.Addr) (*buf.Buffer, error) { packetBuf := buf.New() udpaddr := address.(*gonet.UDPAddr) port, err := net.PortFromInt(uint32(udpaddr.Port)) if err != nil { return nil, err } err = addrParser.WriteAddressPort(packetBuf, net.IPAddress(udpaddr.IP), port) if err != nil { return nil, err } _, err = packetBuf.Write(data.Bytes()) if err != nil { return nil, err } data.Release() return packetBuf, nil } // ExtractAddressFromPacket // relinquish ownership of data // gain ownership of the returning value func ExtractAddressFromPacket(data *buf.Buffer) (*buf.Buffer, gonet.Addr, error) { packetBuf := buf.StackNew() address, port, err := addrParser.ReadAddressPort(&packetBuf, bytes.NewReader(data.Bytes())) if err != nil { return nil, nil, err } if address.Family().IsDomain() { return nil, nil, errors.New("invalid address type") } addr := &gonet.UDPAddr{ IP: address.IP(), Port: int(port.Value()), Zone: "", } data.Advance(packetBuf.Len()) packetBuf.Release() return data, addr, nil } ================================================ FILE: common/net/packetaddr/packetaddr_test.go ================================================ package packetaddr import ( sysnet "net" "testing" "github.com/stretchr/testify/assert" "github.com/v2fly/v2ray-core/v5/common/buf" ) func TestPacketEncodingIPv4(t *testing.T) { packetAddress := &sysnet.UDPAddr{ IP: sysnet.IPv4(1, 2, 3, 4).To4(), Port: 1234, } var packetData [256]byte wrapped, err := AttachAddressToPacket(buf.FromBytes(packetData[:]), packetAddress) assert.NoError(t, err) packetPayload, decodedAddress, err := ExtractAddressFromPacket(wrapped) assert.NoError(t, err) assert.Equal(t, packetPayload.Bytes(), packetData[:]) assert.Equal(t, packetAddress, decodedAddress) } func TestPacketEncodingIPv6(t *testing.T) { packetAddress := &sysnet.UDPAddr{ IP: sysnet.IPv6linklocalallrouters, Port: 1234, } var packetData [256]byte wrapped, err := AttachAddressToPacket(buf.FromBytes(packetData[:]), packetAddress) assert.NoError(t, err) packetPayload, decodedAddress, err := ExtractAddressFromPacket(wrapped) assert.NoError(t, err) assert.Equal(t, packetPayload.Bytes(), packetData[:]) assert.Equal(t, packetAddress, decodedAddress) } ================================================ FILE: common/net/packetaddr/special_address.go ================================================ package packetaddr const seqPacketMagicAddress = "sp.packet-addr.v2fly.arpa" ================================================ FILE: common/net/port.go ================================================ package net import ( "encoding/binary" "strconv" ) // Port represents a network port in TCP and UDP protocol. type Port uint16 // PortFromBytes converts a byte array to a Port, assuming bytes are in big endian order. // @unsafe Caller must ensure that the byte array has at least 2 elements. func PortFromBytes(port []byte) Port { return Port(binary.BigEndian.Uint16(port)) } // PortFromInt converts an integer to a Port. // @error when the integer is not positive or larger then 65535 func PortFromInt(val uint32) (Port, error) { if val > 65535 { return Port(0), newError("invalid port range: ", val) } return Port(val), nil } // PortFromString converts a string to a Port. // @error when the string is not an integer or the integral value is a not a valid Port. func PortFromString(s string) (Port, error) { val, err := strconv.ParseUint(s, 10, 32) if err != nil { return Port(0), newError("invalid port range: ", s) } return PortFromInt(uint32(val)) } // Value return the corresponding uint16 value of a Port. func (p Port) Value() uint16 { return uint16(p) } // String returns the string presentation of a Port. func (p Port) String() string { return strconv.Itoa(int(p)) } // FromPort returns the beginning port of this PortRange. func (p *PortRange) FromPort() Port { return Port(p.From) } // ToPort returns the end port of this PortRange. func (p *PortRange) ToPort() Port { return Port(p.To) } // Contains returns true if the given port is within the range of a PortRange. func (p *PortRange) Contains(port Port) bool { return p.FromPort() <= port && port <= p.ToPort() } // SinglePortRange returns a PortRange contains a single port. func SinglePortRange(p Port) *PortRange { return &PortRange{ From: uint32(p), To: uint32(p), } } type MemoryPortRange struct { From Port To Port } func (r MemoryPortRange) Contains(port Port) bool { return r.From <= port && port <= r.To } type MemoryPortList []MemoryPortRange func PortListFromProto(l *PortList) MemoryPortList { mpl := make(MemoryPortList, 0, len(l.Range)) for _, r := range l.Range { mpl = append(mpl, MemoryPortRange{From: Port(r.From), To: Port(r.To)}) } return mpl } func (mpl MemoryPortList) Contains(port Port) bool { for _, pr := range mpl { if pr.Contains(port) { return true } } return false } ================================================ FILE: common/net/port.pb.go ================================================ package net import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // PortRange represents a range of ports. type PortRange struct { state protoimpl.MessageState `protogen:"open.v1"` // The port that this range starts from. From uint32 `protobuf:"varint,1,opt,name=From,proto3" json:"From,omitempty"` // The port that this range ends with (inclusive). To uint32 `protobuf:"varint,2,opt,name=To,proto3" json:"To,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PortRange) Reset() { *x = PortRange{} mi := &file_common_net_port_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PortRange) String() string { return protoimpl.X.MessageStringOf(x) } func (*PortRange) ProtoMessage() {} func (x *PortRange) ProtoReflect() protoreflect.Message { mi := &file_common_net_port_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PortRange.ProtoReflect.Descriptor instead. func (*PortRange) Descriptor() ([]byte, []int) { return file_common_net_port_proto_rawDescGZIP(), []int{0} } func (x *PortRange) GetFrom() uint32 { if x != nil { return x.From } return 0 } func (x *PortRange) GetTo() uint32 { if x != nil { return x.To } return 0 } // PortList is a list of ports. type PortList struct { state protoimpl.MessageState `protogen:"open.v1"` Range []*PortRange `protobuf:"bytes,1,rep,name=range,proto3" json:"range,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PortList) Reset() { *x = PortList{} mi := &file_common_net_port_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PortList) String() string { return protoimpl.X.MessageStringOf(x) } func (*PortList) ProtoMessage() {} func (x *PortList) ProtoReflect() protoreflect.Message { mi := &file_common_net_port_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PortList.ProtoReflect.Descriptor instead. func (*PortList) Descriptor() ([]byte, []int) { return file_common_net_port_proto_rawDescGZIP(), []int{1} } func (x *PortList) GetRange() []*PortRange { if x != nil { return x.Range } return nil } var File_common_net_port_proto protoreflect.FileDescriptor const file_common_net_port_proto_rawDesc = "" + "\n" + "\x15common/net/port.proto\x12\x15v2ray.core.common.net\"/\n" + "\tPortRange\x12\x12\n" + "\x04From\x18\x01 \x01(\rR\x04From\x12\x0e\n" + "\x02To\x18\x02 \x01(\rR\x02To\"B\n" + "\bPortList\x126\n" + "\x05range\x18\x01 \x03(\v2 .v2ray.core.common.net.PortRangeR\x05rangeB`\n" + "\x19com.v2ray.core.common.netP\x01Z)github.com/v2fly/v2ray-core/v5/common/net\xaa\x02\x15V2Ray.Core.Common.Netb\x06proto3" var ( file_common_net_port_proto_rawDescOnce sync.Once file_common_net_port_proto_rawDescData []byte ) func file_common_net_port_proto_rawDescGZIP() []byte { file_common_net_port_proto_rawDescOnce.Do(func() { file_common_net_port_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_net_port_proto_rawDesc), len(file_common_net_port_proto_rawDesc))) }) return file_common_net_port_proto_rawDescData } var file_common_net_port_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_common_net_port_proto_goTypes = []any{ (*PortRange)(nil), // 0: v2ray.core.common.net.PortRange (*PortList)(nil), // 1: v2ray.core.common.net.PortList } var file_common_net_port_proto_depIdxs = []int32{ 0, // 0: v2ray.core.common.net.PortList.range:type_name -> v2ray.core.common.net.PortRange 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_common_net_port_proto_init() } func file_common_net_port_proto_init() { if File_common_net_port_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_net_port_proto_rawDesc), len(file_common_net_port_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_net_port_proto_goTypes, DependencyIndexes: file_common_net_port_proto_depIdxs, MessageInfos: file_common_net_port_proto_msgTypes, }.Build() File_common_net_port_proto = out.File file_common_net_port_proto_goTypes = nil file_common_net_port_proto_depIdxs = nil } ================================================ FILE: common/net/port.proto ================================================ syntax = "proto3"; package v2ray.core.common.net; option csharp_namespace = "V2Ray.Core.Common.Net"; option go_package = "github.com/v2fly/v2ray-core/v5/common/net"; option java_package = "com.v2ray.core.common.net"; option java_multiple_files = true; // PortRange represents a range of ports. message PortRange { // The port that this range starts from. uint32 From = 1; // The port that this range ends with (inclusive). uint32 To = 2; } // PortList is a list of ports. message PortList { repeated PortRange range = 1; } ================================================ FILE: common/net/port_test.go ================================================ package net_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/net" ) func TestPortRangeContains(t *testing.T) { portRange := &PortRange{ From: 53, To: 53, } if !portRange.Contains(Port(53)) { t.Error("expected port range containing 53, but actually not") } } ================================================ FILE: common/net/system.go ================================================ package net import "net" const ( IPv4len = net.IPv4len IPv6len = net.IPv6len ) var ( CIDRMask = net.CIDRMask Dial = net.Dial DialTCP = net.DialTCP DialUDP = net.DialUDP DialUnix = net.DialUnix FileConn = net.FileConn FileListener = net.FileListener Listen = net.Listen ListenTCP = net.ListenTCP ListenUDP = net.ListenUDP ListenUnix = net.ListenUnix LookupIP = net.LookupIP ParseCIDR = net.ParseCIDR ParseIP = net.ParseIP ResolveUDPAddr = net.ResolveUDPAddr ResolveUnixAddr = net.ResolveUnixAddr SplitHostPort = net.SplitHostPort ) type ( Addr = net.Addr AddrError = net.AddrError Conn = net.Conn Dialer = net.Dialer Error = net.Error IP = net.IP IPMask = net.IPMask IPNet = net.IPNet ListenConfig = net.ListenConfig Listener = net.Listener PacketConn = net.PacketConn Resolver = net.Resolver TCPAddr = net.TCPAddr TCPConn = net.TCPConn TCPListener = net.TCPListener UDPAddr = net.UDPAddr UDPConn = net.UDPConn UnixAddr = net.UnixAddr UnixConn = net.UnixConn UnixListener = net.UnixListener ) ================================================ FILE: common/packetswitch/gvisorstack/adapter.go ================================================ package gvisorstack import ( "context" "sync" "gvisor.dev/gvisor/pkg/buffer" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/packetswitch" ) func NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(_ context.Context, mtu int, networkLayerSwitch packetswitch.NetworkLayerDevice) *NetworkLayerDeviceToGvisorLinkEndpointAdaptor { return &NetworkLayerDeviceToGvisorLinkEndpointAdaptor{ mtu: mtu, networkLayerSwitch: networkLayerSwitch, waitCh: make(chan struct{}), } } // NetworkLayerDeviceToGvisorLinkEndpointAdaptor is primarily machine generated. type NetworkLayerDeviceToGvisorLinkEndpointAdaptor struct { mtu int networkLayerSwitch packetswitch.NetworkLayerDevice mu sync.RWMutex dispatcher stack.NetworkDispatcher attached bool closed bool onClose func() waitCh chan struct{} } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) MTU() uint32 { n.mu.RLock() defer n.mu.RUnlock() return uint32(n.mtu) } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) SetMTU(mtu uint32) { n.mu.Lock() defer n.mu.Unlock() n.mtu = int(mtu) } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) MaxHeaderLength() uint16 { // No additional link-layer header. return 0 } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) LinkAddress() tcpip.LinkAddress { // Not applicable for network-layer device. return "" } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) SetLinkAddress(_ tcpip.LinkAddress) { // no-op } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) Capabilities() stack.LinkEndpointCapabilities { return stack.CapabilityNone } // networkLayerWriter adapts packets from NetworkLayerDevice into gVisor Stack. type networkLayerWriter struct { parent *NetworkLayerDeviceToGvisorLinkEndpointAdaptor } func (w *networkLayerWriter) Write(packet []byte) (int, error) { if len(packet) == 0 { return 0, nil } buf := buffer.MakeWithData(packet) pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ Payload: buf, // Do not call buf.Release here; PacketBuffer.DecRef will release internal buffer. }) // Determine network protocol by IP version. ver := packet[0] >> 4 var proto tcpip.NetworkProtocolNumber switch ver { case 4: proto = ipv4.ProtocolNumber case 6: proto = ipv6.ProtocolNumber default: // Unknown network packet, drop. pkt.DecRef() return 0, nil } w.parent.mu.RLock() d := w.parent.dispatcher w.parent.mu.RUnlock() if d == nil { // No dispatcher attached, drop. pkt.DecRef() return 0, nil } // Deliver to network layer. The dispatcher takes ownership of pkt // and is responsible for releasing it. d.DeliverNetworkPacket(proto, pkt) return len(packet), nil } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) Attach(dispatcher stack.NetworkDispatcher) { n.mu.Lock() defer n.mu.Unlock() if dispatcher == nil { // Detaching. n.dispatcher = nil n.attached = false return } n.dispatcher = dispatcher writer := &networkLayerWriter{parent: n} // Let the network layer device know where to write incoming packets. if err := n.networkLayerSwitch.OnAttach(writer); err == nil { n.attached = true } else { // OnAttach failed; keep attached false. n.attached = false } } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) IsAttached() bool { n.mu.RLock() defer n.mu.RUnlock() return n.attached } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) Wait() { // If closed, return immediately. n.mu.RLock() closed := n.closed ch := n.waitCh n.mu.RUnlock() if closed { return } // Wait until closed is signaled. <-ch } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) ARPHardwareType() header.ARPHardwareType { return header.ARPHardwareNone } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) AddHeader(_ *stack.PacketBuffer) { // No link-layer header to add. } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) ParseHeader(_ *stack.PacketBuffer) bool { // Nothing to parse; packet is a bare network packet. return true } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) Close() { n.mu.Lock() if n.closed { n.mu.Unlock() return } n.closed = true n.attached = false n.mu.Unlock() // Close underlying network device if any. _ = common.Close(n.networkLayerSwitch) // Run onClose action if set. n.mu.RLock() onc := n.onClose ch := n.waitCh n.mu.RUnlock() if onc != nil { onc() } // Signal waiters. close(ch) } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) SetOnCloseAction(f func()) { n.mu.Lock() defer n.mu.Unlock() n.onClose = f } func (n *NetworkLayerDeviceToGvisorLinkEndpointAdaptor) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) { // Defensive: if receiver is nil, treat as closed. if n == nil { return 0, &tcpip.ErrClosedForSend{} } // Convert each packet to bytes and write to networkLayerSwitch. slice := list.AsSlice() if len(slice) == 0 { return 0, nil } n.mu.RLock() dev := n.networkLayerSwitch mtu := n.mtu n.mu.RUnlock() if dev == nil { return 0, &tcpip.ErrClosedForSend{} } written := 0 for _, pkt := range slice { if pkt == nil { continue } // Get slices and copy into a contiguous buffer. slices := pkt.AsSlices() total := 0 for _, s := range slices { total += len(s) } if mtu > 0 && total > mtu { return written, &tcpip.ErrMessageTooLong{} } cp := make([]byte, total) off := 0 for _, s := range slices { copy(cp[off:], s) off += len(s) } _, err := dev.Write(cp) if err != nil { // Map writer error to tcpip error. return written, &tcpip.ErrNoBufferSpace{} } written++ } return written, nil } ================================================ FILE: common/packetswitch/gvisorstack/adapter_test.go ================================================ package gvisorstack import ( "context" "errors" "reflect" "sync" "testing" "time" "gvisor.dev/gvisor/pkg/buffer" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "github.com/v2fly/v2ray-core/v5/common/packetswitch" ) // fakeDevice implements packetswitch.NetworkLayerDevice for testing. type fakeDevice struct { mu sync.Mutex writer packetswitch.NetworkLayerPacketWriter writes [][]byte closed bool onAttach func(packetswitch.NetworkLayerPacketWriter) error } func (f *fakeDevice) OnAttach(w packetswitch.NetworkLayerPacketWriter) error { f.mu.Lock() defer f.mu.Unlock() f.writer = w if f.onAttach != nil { return f.onAttach(w) } return nil } func (f *fakeDevice) Write(packet []byte) (int, error) { f.mu.Lock() defer f.mu.Unlock() if f.closed { return 0, errors.New("closed") } // make a copy to avoid aliasing test buffer. cp := make([]byte, len(packet)) copy(cp, packet) f.writes = append(f.writes, cp) return len(packet), nil } func (f *fakeDevice) Close() error { f.mu.Lock() defer f.mu.Unlock() f.closed = true return nil } func (f *fakeDevice) getWriter() packetswitch.NetworkLayerPacketWriter { f.mu.Lock() defer f.mu.Unlock() return f.writer } func (f *fakeDevice) lastWrite() []byte { f.mu.Lock() defer f.mu.Unlock() if len(f.writes) == 0 { return nil } return f.writes[len(f.writes)-1] } // fakeDispatcher implements stack.NetworkDispatcher, capturing delivered packets. type fakeDispatcher struct { mu sync.Mutex protocols []tcpip.NetworkProtocolNumber pkts [][]byte } func (d *fakeDispatcher) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { // capture packet payload safely by copying from AsSlices slices := pkt.AsSlices() total := 0 for _, s := range slices { total += len(s) } cp := make([]byte, total) off := 0 for _, s := range slices { copy(cp[off:], s) off += len(s) } // record d.mu.Lock() d.protocols = append(d.protocols, protocol) d.pkts = append(d.pkts, cp) d.mu.Unlock() // release the packet pkt.DecRef() } func (d *fakeDispatcher) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { // not used in these tests pkt.DecRef() } func (d *fakeDispatcher) last() (tcpip.NetworkProtocolNumber, []byte) { d.mu.Lock() defer d.mu.Unlock() if len(d.protocols) == 0 { return 0, nil } return d.protocols[len(d.protocols)-1], d.pkts[len(d.pkts)-1] } func TestAttachAndInboundIPv4IPv6(t *testing.T) { dev := &fakeDevice{} a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, dev) d := &fakeDispatcher{} // Initially not attached if a.IsAttached() { t.Fatal("expected not attached") } // Attach should call device.OnAttach and store writer a.Attach(d) w := dev.getWriter() if w == nil { t.Fatal("device did not receive writer on attach") } if !a.IsAttached() { t.Fatal("expected attached after successful OnAttach") } // Send an IPv4 packet (first byte 0x45 = version 4, IHL 5) ipv4pkt := []byte{0x45, 0x00, 0x00, 0x04} if _, err := w.Write(ipv4pkt); err != nil { t.Fatalf("writer.Write failed: %v", err) } // Check dispatcher received it proto, payload := d.last() if proto != ipv4.ProtocolNumber { t.Fatalf("expected ipv4 protocol, got %v", proto) } if !reflect.DeepEqual(payload, ipv4pkt) { t.Fatalf("unexpected payload: %v", payload) } // IPv6 packet (first byte 0x60) ipv6pkt := []byte{0x60, 0x00, 0x00, 0x00} if _, err := w.Write(ipv6pkt); err != nil { t.Fatalf("writer.Write failed: %v", err) } proto2, payload2 := d.last() if proto2 != ipv6.ProtocolNumber { t.Fatalf("expected ipv6 protocol, got %v", proto2) } if !reflect.DeepEqual(payload2, ipv6pkt) { t.Fatalf("unexpected payload: %v", payload2) } } func TestInboundNonIPIsDropped(t *testing.T) { dev := &fakeDevice{} a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, dev) d := &fakeDispatcher{} a.Attach(d) w := dev.getWriter() if w == nil { t.Fatal("device did not receive writer on attach") } // Non-IP packet: first nibble 0 pkt := []byte{0x00, 0x01, 0x02} if _, err := w.Write(pkt); err != nil { t.Fatalf("writer.Write failed: %v", err) } // Dispatcher should not have any packets proto, payload := d.last() if payload != nil || proto != 0 { t.Fatalf("expected no delivery for non-ip packet, got proto=%v payload=%v", proto, payload) } } func makePacketBufferPayload(b []byte) *stack.PacketBuffer { buf := buffer.MakeWithData(b) pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ Payload: buf, }) return pkt } func TestOutboundWritePacketsOk(t *testing.T) { dev := &fakeDevice{} a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, dev) // Prepare a PacketBufferList with one packet list := stack.PacketBufferList{} payload := []byte{0x45, 0x01, 0x02, 0x03} pkt := makePacketBufferPayload(payload) list.PushBack(pkt) written, err := a.WritePackets(list) if err != nil { t.Fatalf("WritePackets returned error: %v", err) } if written != 1 { t.Fatalf("expected 1 written, got %d", written) } // Device should have received the payload lw := dev.lastWrite() if !reflect.DeepEqual(lw, payload) { t.Fatalf("device write mismatch: got %v, want %v", lw, payload) } } func TestWritePacketsMTUExceeded(t *testing.T) { dev := &fakeDevice{} // mtu set to 2 a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 2, dev) list := stack.PacketBufferList{} payload := []byte{0x45, 0x01, 0x02} // len 3 > mtu 2 pkt := makePacketBufferPayload(payload) list.PushBack(pkt) written, err := a.WritePackets(list) if err == nil { t.Fatalf("expected error due to message too long, got nil") } if _, ok := err.(*tcpip.ErrMessageTooLong); !ok { t.Fatalf("expected ErrMessageTooLong, got %T", err) } if written != 0 { t.Fatalf("expected 0 written, got %d", written) } } func TestCloseAndOnCloseActionAndWait(t *testing.T) { dev := &fakeDevice{} a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, dev) called := make(chan struct{}) a.SetOnCloseAction(func() { close(called) }) // Wait should block until Close is called. Run Wait in goroutine. done := make(chan struct{}) go func() { a.Wait() close(done) }() // Give goroutine a moment to start time.Sleep(5 * time.Millisecond) // Now close a.Close() // onClose action should be called select { case <-called: // ok case <-time.After(100 * time.Millisecond): t.Fatal("onClose was not called") } // Wait should return select { case <-done: // ok case <-time.After(100 * time.Millisecond): t.Fatal("Wait did not return after Close") } } func TestAttachOnAttachFailLeavesNotAttached(t *testing.T) { dev := &fakeDevice{} // make OnAttach return error dev.onAttach = func(w packetswitch.NetworkLayerPacketWriter) error { return errors.New("attach fail") } a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, dev) d := &fakeDispatcher{} a.Attach(d) if a.IsAttached() { t.Fatal("expected not attached when OnAttach fails") } } func TestWritePacketsWhenNoDevice(t *testing.T) { // Create adaptor with nil device a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, nil) list := stack.PacketBufferList{} payload := []byte{0x45} pkt := makePacketBufferPayload(payload) list.PushBack(pkt) written, err := a.WritePackets(list) if err == nil { t.Fatalf("expected ErrClosedForSend, got nil") } if _, ok := err.(*tcpip.ErrClosedForSend); !ok { t.Fatalf("expected ErrClosedForSend, got %T", err) } if written != 0 { t.Fatalf("expected 0 written, got %d", written) } } func TestSetMTUAndCapsAndHeaders(t *testing.T) { // Create adaptor and test MTU setter a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, nil) if a.MTU() != 1500 { t.Fatalf("initial MTU mismatch: %d", a.MTU()) } a.SetMTU(9000) if a.MTU() != 9000 { t.Fatalf("MTU not updated: %d", a.MTU()) } // Caps and headers if a.MaxHeaderLength() != 0 { t.Fatalf("MaxHeaderLength expected 0, got %d", a.MaxHeaderLength()) } if a.Capabilities() != stack.CapabilityNone { t.Fatalf("Capabilities expected CapabilityNone, got %v", a.Capabilities()) } if a.LinkAddress() != "" { t.Fatalf("LinkAddress expected empty, got %v", a.LinkAddress()) } // AddHeader/ParseHeader should not panic and parse returns true pkt := makePacketBufferPayload([]byte{0x45}) // Should be no-op a.AddHeader(pkt) if !a.ParseHeader(pkt) { t.Fatalf("ParseHeader expected true") } pkt.DecRef() } // errDevice fails after a certain number of writes to simulate partial failures. type errDevice struct { mu sync.Mutex writer packetswitch.NetworkLayerPacketWriter writes [][]byte failAfter int calls int closed bool } func (e *errDevice) OnAttach(w packetswitch.NetworkLayerPacketWriter) error { e.mu.Lock() defer e.mu.Unlock() e.writer = w return nil } func (e *errDevice) Write(packet []byte) (int, error) { e.mu.Lock() defer e.mu.Unlock() if e.closed { return 0, errors.New("closed") } e.calls++ if e.failAfter > 0 && e.calls > e.failAfter { return 0, errors.New("injected write error") } cp := make([]byte, len(packet)) copy(cp, packet) e.writes = append(e.writes, cp) return len(packet), nil } func (e *errDevice) Close() error { e.mu.Lock() defer e.mu.Unlock() e.closed = true return nil } func TestWritePacketsPartialOnError(t *testing.T) { e := &errDevice{failAfter: 1} a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, e) list := stack.PacketBufferList{} p1 := makePacketBufferPayload([]byte{0x45, 0x01}) p2 := makePacketBufferPayload([]byte{0x45, 0x02}) list.PushBack(p1) list.PushBack(p2) written, err := a.WritePackets(list) if err == nil { t.Fatalf("expected error due to injected write error") } // We mapped device write errors to ErrNoBufferSpace if _, ok := err.(*tcpip.ErrNoBufferSpace); !ok { t.Fatalf("expected ErrNoBufferSpace, got %T", err) } if written != 1 { t.Fatalf("expected 1 written, got %d", written) } } func TestMultiplePacketsWrite(t *testing.T) { dev := &fakeDevice{} a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, dev) list := stack.PacketBufferList{} p1 := makePacketBufferPayload([]byte{0x45, 0x01}) p2 := makePacketBufferPayload([]byte{0x45, 0x02}) p3 := makePacketBufferPayload([]byte{0x45, 0x03}) list.PushBack(p1) list.PushBack(p2) list.PushBack(p3) written, err := a.WritePackets(list) if err != nil { t.Fatalf("WritePackets returned error: %v", err) } if written != 3 { t.Fatalf("expected 3 written, got %d", written) } // last write should be p3 lw := dev.lastWrite() if !reflect.DeepEqual(lw, []byte{0x45, 0x03}) { t.Fatalf("unexpected last write: %v", lw) } } func TestConcurrentWritePacketsAndClose(t *testing.T) { dev := &fakeDevice{} a := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(context.Background(), 1500, dev) wg := sync.WaitGroup{} nWorkers := 5 nPerWorker := 50 wg.Add(nWorkers) for i := 0; i < nWorkers; i++ { go func(id int) { defer wg.Done() for j := 0; j < nPerWorker; j++ { list := stack.PacketBufferList{} payload := []byte{0x45, byte(id), byte(j)} pkt := makePacketBufferPayload(payload) list.PushBack(pkt) _, _ = a.WritePackets(list) } }(i) } // Close after a short delay go func() { time.Sleep(10 * time.Millisecond) a.Close() }() wg.Wait() // Wait should return quickly after Close a.Wait() } ================================================ FILE: common/packetswitch/gvisorstack/config.pb.go ================================================ package gvisorstack import ( routercommon "github.com/v2fly/v2ray-core/v5/app/router/routercommon" _ "github.com/v2fly/v2ray-core/v5/common/protoext" internet "github.com/v2fly/v2ray-core/v5/transport/internet" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type TCPListener struct { state protoimpl.MessageState `protogen:"open.v1"` Port uint32 `protobuf:"varint,1,opt,name=port,proto3" json:"port,omitempty"` Address *routercommon.CIDR `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` Tag string `protobuf:"bytes,3,opt,name=tag,proto3" json:"tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TCPListener) Reset() { *x = TCPListener{} mi := &file_common_packetswitch_gvisorstack_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TCPListener) String() string { return protoimpl.X.MessageStringOf(x) } func (*TCPListener) ProtoMessage() {} func (x *TCPListener) ProtoReflect() protoreflect.Message { mi := &file_common_packetswitch_gvisorstack_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TCPListener.ProtoReflect.Descriptor instead. func (*TCPListener) Descriptor() ([]byte, []int) { return file_common_packetswitch_gvisorstack_config_proto_rawDescGZIP(), []int{0} } func (x *TCPListener) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *TCPListener) GetAddress() *routercommon.CIDR { if x != nil { return x.Address } return nil } func (x *TCPListener) GetTag() string { if x != nil { return x.Tag } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Mtu uint32 `protobuf:"varint,2,opt,name=mtu,proto3" json:"mtu,omitempty"` UserLevel uint32 `protobuf:"varint,3,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` Ips []*routercommon.CIDR `protobuf:"bytes,6,rep,name=ips,proto3" json:"ips,omitempty"` Routes []*routercommon.CIDR `protobuf:"bytes,7,rep,name=routes,proto3" json:"routes,omitempty"` EnablePromiscuousMode bool `protobuf:"varint,8,opt,name=enable_promiscuous_mode,json=enablePromiscuousMode,proto3" json:"enable_promiscuous_mode,omitempty"` EnableSpoofing bool `protobuf:"varint,9,opt,name=enable_spoofing,json=enableSpoofing,proto3" json:"enable_spoofing,omitempty"` SocketSettings *internet.SocketConfig `protobuf:"bytes,10,opt,name=socket_settings,json=socketSettings,proto3" json:"socket_settings,omitempty"` PreferIpv6ForUdp bool `protobuf:"varint,11,opt,name=prefer_ipv6_for_udp,json=preferIpv6ForUdp,proto3" json:"prefer_ipv6_for_udp,omitempty"` DualStackUdp bool `protobuf:"varint,12,opt,name=dual_stack_udp,json=dualStackUdp,proto3" json:"dual_stack_udp,omitempty"` TcpListener []*TCPListener `protobuf:"bytes,13,rep,name=tcp_listener,json=tcpListener,proto3" json:"tcp_listener,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_common_packetswitch_gvisorstack_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_common_packetswitch_gvisorstack_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_common_packetswitch_gvisorstack_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } func (x *Config) GetUserLevel() uint32 { if x != nil { return x.UserLevel } return 0 } func (x *Config) GetIps() []*routercommon.CIDR { if x != nil { return x.Ips } return nil } func (x *Config) GetRoutes() []*routercommon.CIDR { if x != nil { return x.Routes } return nil } func (x *Config) GetEnablePromiscuousMode() bool { if x != nil { return x.EnablePromiscuousMode } return false } func (x *Config) GetEnableSpoofing() bool { if x != nil { return x.EnableSpoofing } return false } func (x *Config) GetSocketSettings() *internet.SocketConfig { if x != nil { return x.SocketSettings } return nil } func (x *Config) GetPreferIpv6ForUdp() bool { if x != nil { return x.PreferIpv6ForUdp } return false } func (x *Config) GetDualStackUdp() bool { if x != nil { return x.DualStackUdp } return false } func (x *Config) GetTcpListener() []*TCPListener { if x != nil { return x.TcpListener } return nil } var File_common_packetswitch_gvisorstack_config_proto protoreflect.FileDescriptor const file_common_packetswitch_gvisorstack_config_proto_rawDesc = "" + "\n" + ",common/packetswitch/gvisorstack/config.proto\x12*v2ray.core.common.packetswitch.gvisorstack\x1a$app/router/routercommon/common.proto\x1a\x1ftransport/internet/config.proto\x1a common/protoext/extensions.proto\"w\n" + "\vTCPListener\x12\x12\n" + "\x04port\x18\x01 \x01(\rR\x04port\x12B\n" + "\aaddress\x18\x02 \x01(\v2(.v2ray.core.app.router.routercommon.CIDRR\aaddress\x12\x10\n" + "\x03tag\x18\x03 \x01(\tR\x03tag\"\x9f\x04\n" + "\x06Config\x12\x10\n" + "\x03mtu\x18\x02 \x01(\rR\x03mtu\x12\x1d\n" + "\n" + "user_level\x18\x03 \x01(\rR\tuserLevel\x12:\n" + "\x03ips\x18\x06 \x03(\v2(.v2ray.core.app.router.routercommon.CIDRR\x03ips\x12@\n" + "\x06routes\x18\a \x03(\v2(.v2ray.core.app.router.routercommon.CIDRR\x06routes\x126\n" + "\x17enable_promiscuous_mode\x18\b \x01(\bR\x15enablePromiscuousMode\x12'\n" + "\x0fenable_spoofing\x18\t \x01(\bR\x0eenableSpoofing\x12T\n" + "\x0fsocket_settings\x18\n" + " \x01(\v2+.v2ray.core.transport.internet.SocketConfigR\x0esocketSettings\x12-\n" + "\x13prefer_ipv6_for_udp\x18\v \x01(\bR\x10preferIpv6ForUdp\x12$\n" + "\x0edual_stack_udp\x18\f \x01(\bR\fdualStackUdp\x12Z\n" + "\ftcp_listener\x18\r \x03(\v27.v2ray.core.common.packetswitch.gvisorstack.TCPListenerR\vtcpListenerB\x9f\x01\n" + ".com.v2ray.core.common.packetswitch.gvisorstackP\x01Z>github.com/v2fly/v2ray-core/v5/common/packetswitch/gvisorstack\xaa\x02*V2Ray.Core.Common.Packetswitch.Gvisorstackb\x06proto3" var ( file_common_packetswitch_gvisorstack_config_proto_rawDescOnce sync.Once file_common_packetswitch_gvisorstack_config_proto_rawDescData []byte ) func file_common_packetswitch_gvisorstack_config_proto_rawDescGZIP() []byte { file_common_packetswitch_gvisorstack_config_proto_rawDescOnce.Do(func() { file_common_packetswitch_gvisorstack_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_packetswitch_gvisorstack_config_proto_rawDesc), len(file_common_packetswitch_gvisorstack_config_proto_rawDesc))) }) return file_common_packetswitch_gvisorstack_config_proto_rawDescData } var file_common_packetswitch_gvisorstack_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_common_packetswitch_gvisorstack_config_proto_goTypes = []any{ (*TCPListener)(nil), // 0: v2ray.core.common.packetswitch.gvisorstack.TCPListener (*Config)(nil), // 1: v2ray.core.common.packetswitch.gvisorstack.Config (*routercommon.CIDR)(nil), // 2: v2ray.core.app.router.routercommon.CIDR (*internet.SocketConfig)(nil), // 3: v2ray.core.transport.internet.SocketConfig } var file_common_packetswitch_gvisorstack_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.common.packetswitch.gvisorstack.TCPListener.address:type_name -> v2ray.core.app.router.routercommon.CIDR 2, // 1: v2ray.core.common.packetswitch.gvisorstack.Config.ips:type_name -> v2ray.core.app.router.routercommon.CIDR 2, // 2: v2ray.core.common.packetswitch.gvisorstack.Config.routes:type_name -> v2ray.core.app.router.routercommon.CIDR 3, // 3: v2ray.core.common.packetswitch.gvisorstack.Config.socket_settings:type_name -> v2ray.core.transport.internet.SocketConfig 0, // 4: v2ray.core.common.packetswitch.gvisorstack.Config.tcp_listener:type_name -> v2ray.core.common.packetswitch.gvisorstack.TCPListener 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_common_packetswitch_gvisorstack_config_proto_init() } func file_common_packetswitch_gvisorstack_config_proto_init() { if File_common_packetswitch_gvisorstack_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_packetswitch_gvisorstack_config_proto_rawDesc), len(file_common_packetswitch_gvisorstack_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_packetswitch_gvisorstack_config_proto_goTypes, DependencyIndexes: file_common_packetswitch_gvisorstack_config_proto_depIdxs, MessageInfos: file_common_packetswitch_gvisorstack_config_proto_msgTypes, }.Build() File_common_packetswitch_gvisorstack_config_proto = out.File file_common_packetswitch_gvisorstack_config_proto_goTypes = nil file_common_packetswitch_gvisorstack_config_proto_depIdxs = nil } ================================================ FILE: common/packetswitch/gvisorstack/config.proto ================================================ syntax = "proto3"; package v2ray.core.common.packetswitch.gvisorstack; option csharp_namespace = "V2Ray.Core.Common.Packetswitch.Gvisorstack"; option go_package = "github.com/v2fly/v2ray-core/v5/common/packetswitch/gvisorstack"; option java_package = "com.v2ray.core.common.packetswitch.gvisorstack"; option java_multiple_files = true; import "app/router/routercommon/common.proto"; import "transport/internet/config.proto"; import "common/protoext/extensions.proto"; message TCPListener { uint32 port = 1; v2ray.core.app.router.routercommon.CIDR address = 2; string tag = 3; } message Config { uint32 mtu = 2; uint32 user_level = 3; repeated v2ray.core.app.router.routercommon.CIDR ips = 6; repeated v2ray.core.app.router.routercommon.CIDR routes = 7; bool enable_promiscuous_mode = 8; bool enable_spoofing = 9; v2ray.core.transport.internet.SocketConfig socket_settings = 10; bool prefer_ipv6_for_udp = 11; bool dual_stack_udp = 12; repeated TCPListener tcp_listener = 13; } ================================================ FILE: common/packetswitch/gvisorstack/dialer.go ================================================ package gvisorstack import ( "context" "fmt" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "github.com/v2fly/v2ray-core/v5/common/dualStack/fusedPacketConn" "github.com/v2fly/v2ray-core/v5/common/net" ) // DialTCP will create a connection to the given destination, using the stack. // Machine Generated func (w *WrappedStack) DialTCP(ctx context.Context, remoteAddress net.Destination) (net.Conn, error) { if w == nil || w.stack == nil { return nil, fmt.Errorf("gvisor stack not initialized") } if remoteAddress.Network != net.Network_TCP { return nil, fmt.Errorf("destination is not tcp: %v", remoteAddress.Network) } // Resolve address to IP if necessary. var ipBytes []byte switch remoteAddress.Address.Family() { case net.AddressFamilyIPv4: ipBytes = remoteAddress.Address.IP().To4() case net.AddressFamilyIPv6: ipBytes = remoteAddress.Address.IP().To16() case net.AddressFamilyDomain: // Do not resolve domain names here. Return explicit error. return nil, fmt.Errorf("domain address not supported for gVisor dial: %s", remoteAddress.Address.String()) default: return nil, fmt.Errorf("unsupported address family: %v", remoteAddress.Address.Family()) } if ipBytes == nil { return nil, fmt.Errorf("failed to obtain IP bytes for %v", remoteAddress.Address) } // Choose network protocol number based on IP length. netProto := ipv4.ProtocolNumber if len(ipBytes) == 16 { netProto = ipv6.ProtocolNumber } remote := tcpip.FullAddress{ Addr: tcpip.AddrFromSlice(ipBytes), Port: uint16(remoteAddress.Port), } // Use gonet dialer to create a TCP connection on the in-memory stack. conn, err := gonet.DialContextTCP(ctx, w.stack, remote, netProto) if err != nil { return nil, err } return conn, nil } // ListenUDP will create a connection to the given destination, using the stack. // Machine Generated func (w *WrappedStack) ListenUDP(ctx context.Context, localAddress net.Destination) (net.PacketConn, error) { // allow ctx to be accepted by the function signature _ = ctx if w == nil || w.stack == nil { return nil, fmt.Errorf("gvisor stack not initialized") } if localAddress.Network != net.Network_UDP { return nil, fmt.Errorf("destination is not udp: %v", localAddress.Network) } // Determine local address bytes. var ipBytes []byte specified := false if localAddress.Address == nil { // If address is nil, treat as unspecified (zero) address. specified = false } else { switch localAddress.Address.Family() { case net.AddressFamilyIPv4: specified = true ipBytes = localAddress.Address.IP().To4() case net.AddressFamilyIPv6: specified = true ipBytes = localAddress.Address.IP().To16() case net.AddressFamilyDomain: // Listening on a domain name is not supported. return nil, fmt.Errorf("listening on domain address not supported: %s", localAddress.Address.String()) default: // If unspecified (zero) address, allow kernel (stack) to choose. specified = false } } var laddr *tcpip.FullAddress if specified { if ipBytes == nil { return nil, fmt.Errorf("failed to obtain IP bytes for %v", localAddress.Address) } netProto := ipv4.ProtocolNumber if len(ipBytes) == 16 { netProto = ipv6.ProtocolNumber } l := tcpip.FullAddress{Addr: tcpip.AddrFromSlice(ipBytes), Port: uint16(localAddress.Port)} laddr = &l // Create UDP endpoint bound to local address. udpConn, err := gonet.DialUDP(w.stack, laddr, nil, netProto) if err != nil { return nil, err } return udpConn, nil } if w.config.DualStackUdp { udpConn4, err := gonet.DialUDP(w.stack, nil, nil, ipv4.ProtocolNumber) if err != nil { return nil, fmt.Errorf("failed to create IPv4 UDP conn for dual stack: %w", err) } udpConn6, err := gonet.DialUDP(w.stack, nil, nil, ipv6.ProtocolNumber) if err != nil { udpConn4.Close() return nil, fmt.Errorf("failed to create IPv6 UDP conn for dual stack: %w", err) } preferIPv6 := w.config.GetPreferIpv6ForUdp() return fusedPacketConn.NewFusedPacketConn(udpConn4, udpConn6, int(w.config.Mtu), preferIPv6), nil } // If not specified, let the stack choose the local address (pass nil laddr). // Default network selection honors PreferIpv6ForUdp if configured. defaultNet := ipv4.ProtocolNumber if w.config != nil && w.config.GetPreferIpv6ForUdp() { defaultNet = ipv6.ProtocolNumber } udpConn, err := gonet.DialUDP(w.stack, nil, nil, defaultNet) if err != nil { return nil, err } return udpConn, nil } ================================================ FILE: common/packetswitch/gvisorstack/stack.go ================================================ package gvisorstack import ( "context" "fmt" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/udp" "github.com/v2fly/v2ray-core/v5/common/packetswitch" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type WrappedStack struct { config *Config ctx context.Context stack *stack.Stack stackTCPListeners []*gonet.TCPListener } func NewStack(ctx context.Context, config *Config) (*WrappedStack, error) { return &WrappedStack{ config: config, ctx: ctx, }, nil } func (w *WrappedStack) CreateStackFromNetworkLayerDevice(packetSwitchDevice packetswitch.NetworkLayerDevice) error { // Validate if w == nil || w.config == nil { return fmt.Errorf("no config") } // Determine MTU from config (0 means unspecified) mtu := int(w.config.GetMtu()) // Create adaptor that implements stack.LinkEndpoint adaptor := NewNetworkLayerDeviceToGvisorLinkEndpointAdaptor(w.ctx, mtu, packetSwitchDevice) // Create stack using adaptor as link endpoint s, err := w.createStack(adaptor) if err != nil { // cleanup adaptor on error adaptor.Close() return fmt.Errorf("failed to create gvisor stack: %v", err) } // When the adaptor is closed, close the stack as well. adaptor.SetOnCloseAction(func() { if s != nil { s.Close() } }) w.stack = s if err := w.ApplyListeners(); err != nil { return fmt.Errorf("failed to apply listeners: %v", err) } return nil } func (w *WrappedStack) createStack(linkedEndpoint stack.LinkEndpoint) (*stack.Stack, error) { // Machine Generated if w == nil || w.config == nil { return nil, fmt.Errorf("no config") } s := stack.New(stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{ ipv4.NewProtocol, ipv6.NewProtocol, }, TransportProtocols: []stack.TransportProtocolFactory{ tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6, }, }) nicID := s.NextNICID() // Create NIC if err := s.CreateNICWithOptions(nicID, linkedEndpoint, stack.NICOptions{Disabled: false, QDisc: nil}); err != nil { return nil, fmt.Errorf("failed to create NIC: %v", err) } // Add protocol addresses for _, ip := range w.config.Ips { tcpIPAddr := tcpip.AddrFromSlice(ip.Ip) protocolAddress := tcpip.ProtocolAddress{ AddressWithPrefix: tcpip.AddressWithPrefix{ Address: tcpIPAddr, PrefixLen: int(ip.Prefix), }, } switch tcpIPAddr.Len() { case 4: protocolAddress.Protocol = ipv4.ProtocolNumber case 16: protocolAddress.Protocol = ipv6.ProtocolNumber default: return nil, fmt.Errorf("invalid IP address length: %d", tcpIPAddr.Len()) } if err := s.AddProtocolAddress(nicID, protocolAddress, stack.AddressProperties{}); err != nil { return nil, fmt.Errorf("failed to add protocol address: %v", err) } } // Set route table s.SetRouteTable(func() (table []tcpip.Route) { for _, cidrs := range w.config.Routes { subnet := tcpip.AddressWithPrefix{ Address: tcpip.AddrFromSlice(cidrs.Ip), PrefixLen: int(cidrs.Prefix), }.Subnet() route := tcpip.Route{ Destination: subnet, NIC: nicID, } table = append(table, route) } return }()) // Promiscuous & spoofing if err := s.SetPromiscuousMode(nicID, w.config.EnablePromiscuousMode); err != nil { return nil, fmt.Errorf("failed to set promiscuous mode: %v", err) } if err := s.SetSpoofing(nicID, w.config.EnableSpoofing); err != nil { return nil, fmt.Errorf("failed to set spoofing: %v", err) } // Apply socket buffer sizes if provided if w.config.SocketSettings != nil { if size := w.config.SocketSettings.TxBufSize; size != 0 { sendBufferSizeRangeOption := tcpip.TCPSendBufferSizeRangeOption{Min: tcp.MinBufferSize, Default: int(size), Max: tcp.MaxBufferSize} if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sendBufferSizeRangeOption); err != nil { return nil, fmt.Errorf("failed to set tcp send buffer size: %v", err) } } if size := w.config.SocketSettings.RxBufSize; size != 0 { receiveBufferSizeRangeOption := tcpip.TCPReceiveBufferSizeRangeOption{Min: tcp.MinBufferSize, Default: int(size), Max: tcp.MaxBufferSize} if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &receiveBufferSizeRangeOption); err != nil { return nil, fmt.Errorf("failed to set tcp receive buffer size: %v", err) } } } return s, nil } func (w *WrappedStack) Close() error { if w == nil || w.stack == nil { return nil } for _, l := range w.stackTCPListeners { l.Close() } w.stackTCPListeners = nil w.stack.Close() w.stack = nil return nil } ================================================ FILE: common/packetswitch/gvisorstack/tcp_listener.go ================================================ package gvisorstack import ( "fmt" "io" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet/tagged" ) func (w *WrappedStack) ApplyListeners() error { for _, listener := range w.config.TcpListener { listenerIP := listener.Address.Ip listenerPort := listener.GetPort() listenerTag := listener.Tag addr := tcpip.FullAddress{ Addr: tcpip.AddrFromSlice(listenerIP), Port: uint16(listenerPort), } netProto := ipv4.ProtocolNumber if len(listenerIP) == net.IPv6len { netProto = ipv6.ProtocolNumber } tcpListener, err := w.CreateStackListener(addr, netProto) if err != nil { return fmt.Errorf("failed to create TCP listener on %v:%d: %w", listenerIP, listenerPort, err) } w.stackTCPListeners = append(w.stackTCPListeners, tcpListener) go w.acceptLoop(tcpListener, listenerTag) } return nil } func (w *WrappedStack) acceptLoop(listener *gonet.TCPListener, outboundTag string) { for { conn, err := listener.Accept() if err != nil { return } go w.handleTCPConn(conn, outboundTag) } } func (w *WrappedStack) handleTCPConn(conn net.Conn, outboundTag string) { defer conn.Close() // Use the connection's local address as the destination, // representing the address the client was connecting to on the stack. tcpAddr, ok := conn.LocalAddr().(*net.TCPAddr) if !ok { return } dest := net.TCPDestination(net.IPAddress(tcpAddr.IP), net.Port(tcpAddr.Port)) outboundConn, err := tagged.Dialer(w.ctx, dest, outboundTag) if err != nil { return } defer outboundConn.Close() // Bidirectional relay done := make(chan struct{}) go func() { io.Copy(outboundConn, conn) close(done) }() io.Copy(conn, outboundConn) <-done } func (w *WrappedStack) CreateStackListener(addr tcpip.FullAddress, netProto tcpip.NetworkProtocolNumber) (*gonet.TCPListener, error) { return gonet.ListenTCP(w.stack, addr, netProto) } ================================================ FILE: common/packetswitch/interconnect/interconnect.go ================================================ package interconnect //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: common/packetswitch/interconnect/networkLayer_cable.go ================================================ package interconnect import ( "context" "errors" "sync" "github.com/v2fly/v2ray-core/v5/common/packetswitch" ) func NewNetworkLayerCable(ctx context.Context) (*NetworkLayerCable, error) { return &NetworkLayerCable{ ctx: ctx, }, nil } // NetworkLayerCable is primarily Machine Generated type NetworkLayerCable struct { lSideWriter packetswitch.NetworkLayerPacketWriter rSideWriter packetswitch.NetworkLayerPacketWriter ctx context.Context lock sync.RWMutex } // NetworkLayerCableDevice is Machine Generated type NetworkLayerCableDevice struct { cable *NetworkLayerCable isLeft bool } func (c *NetworkLayerCable) GetLSideDevice() *NetworkLayerCableDevice { return &NetworkLayerCableDevice{ cable: c, isLeft: true, } } func (c *NetworkLayerCable) GetRSideDevice() *NetworkLayerCableDevice { return &NetworkLayerCableDevice{ cable: c, isLeft: false, } } // OnAttach implements NetworkLayerPacketReader.OnAttach func (d *NetworkLayerCableDevice) OnAttach(writer packetswitch.NetworkLayerPacketWriter) error { if writer == nil { return errors.New("nil writer") } d.cable.lock.Lock() defer d.cable.lock.Unlock() if d.isLeft { if d.cable.lSideWriter != nil { return errors.New("left writer already attached") } d.cable.lSideWriter = writer } else { if d.cable.rSideWriter != nil { return errors.New("right writer already attached") } d.cable.rSideWriter = writer } return nil } // Write implements NetworkLayerPacketWriter.Write func (d *NetworkLayerCableDevice) Write(packet []byte) (int, error) { d.cable.lock.RLock() var peer packetswitch.NetworkLayerPacketWriter if d.isLeft { peer = d.cable.rSideWriter } else { peer = d.cable.lSideWriter } d.cable.lock.RUnlock() if peer == nil { return 0, errors.New("no peer attached") } return peer.Write(packet) } // Close implements common.Closable.Close func (d *NetworkLayerCableDevice) Close() error { d.cable.lock.Lock() defer d.cable.lock.Unlock() if d.isLeft { d.cable.lSideWriter = nil } else { d.cable.rSideWriter = nil } return nil } ================================================ FILE: common/packetswitch/interconnect/networkLayer_cable_test.go ================================================ package interconnect import ( "context" "fmt" "sync" "testing" "time" "github.com/v2fly/v2ray-core/v5/common/packetswitch" ) type testWriter struct { mu sync.Mutex received [][]byte ch chan []byte } func newTestWriter(bufSize int) *testWriter { w := &testWriter{ch: make(chan []byte, bufSize)} return w } func (w *testWriter) Write(p []byte) (int, error) { // copy payload cp := make([]byte, len(p)) copy(cp, p) w.mu.Lock() w.received = append(w.received, cp) w.mu.Unlock() select { case w.ch <- cp: default: } return len(p), nil } func (w *testWriter) ReceivedAll() [][]byte { w.mu.Lock() defer w.mu.Unlock() out := make([][]byte, len(w.received)) copy(out, w.received) return out } func TestCable_HappyPath(t *testing.T) { c, err := NewNetworkLayerCable(context.Background()) if err != nil { t.Fatalf("failed to create cable: %v", err) } l := c.GetLSideDevice() r := c.GetRSideDevice() wL := newTestWriter(4) wR := newTestWriter(4) if err := l.OnAttach(wL); err != nil { t.Fatalf("attach left failed: %v", err) } if err := r.OnAttach(wR); err != nil { t.Fatalf("attach right failed: %v", err) } payloadL := []byte("from-left") n, err := l.Write(payloadL) if err != nil { t.Fatalf("write left failed: %v", err) } if n != len(payloadL) { t.Fatalf("write returned wrong length: %d", n) } select { case got := <-wR.ch: if string(got) != string(payloadL) { t.Fatalf("unexpected payload at right: %s", string(got)) } case <-time.After(500 * time.Millisecond): t.Fatalf("timeout waiting for payload on right") } payloadR := []byte("from-right") n, err = r.Write(payloadR) if err != nil { t.Fatalf("write right failed: %v", err) } if n != len(payloadR) { t.Fatalf("write returned wrong length: %d", n) } select { case got := <-wL.ch: if string(got) != string(payloadR) { t.Fatalf("unexpected payload at left: %s", string(got)) } case <-time.After(500 * time.Millisecond): t.Fatalf("timeout waiting for payload on left") } } func TestCable_NoPeer(t *testing.T) { c, _ := NewNetworkLayerCable(context.Background()) l := c.GetLSideDevice() wL := newTestWriter(1) if err := l.OnAttach(wL); err != nil { t.Fatalf("attach left failed: %v", err) } if n, err := l.Write([]byte("x")); err == nil || n != 0 { t.Fatalf("expected write to fail when no peer attached got n=%d err=%v", n, err) } } func TestCable_DoubleAttachAndClose(t *testing.T) { c, _ := NewNetworkLayerCable(context.Background()) l := c.GetLSideDevice() w1 := newTestWriter(1) w2 := newTestWriter(1) if err := l.OnAttach(w1); err != nil { t.Fatalf("attach left failed: %v", err) } if err := l.OnAttach(w2); err == nil { t.Fatalf("expected second attach to fail") } r := c.GetRSideDevice() wr := newTestWriter(2) if err := r.OnAttach(wr); err != nil { t.Fatalf("attach right failed: %v", err) } // close left and ensure right cannot write if err := l.Close(); err != nil { t.Fatalf("close left failed: %v", err) } if n, err := r.Write([]byte("hello")); err == nil || n != 0 { t.Fatalf("expected write from right to fail after left closed got n=%d err=%v", n, err) } } func TestCable_ConcurrentWrites(t *testing.T) { c, _ := NewNetworkLayerCable(context.Background()) l := c.GetLSideDevice() r := c.GetRSideDevice() wL := newTestWriter(100) wR := newTestWriter(100) if err := l.OnAttach(wL); err != nil { t.Fatalf("attach left failed: %v", err) } if err := r.OnAttach(wR); err != nil { t.Fatalf("attach right failed: %v", err) } var wg sync.WaitGroup count := 200 wg.Add(count * 2) for i := 0; i < count; i++ { payloadL := []byte(fmt.Sprintf("L-%d", i%10)) payloadR := []byte(fmt.Sprintf("R-%d", i%10)) go func(p []byte) { defer wg.Done() _, _ = l.Write(p) }(payloadL) go func(p []byte) { defer wg.Done() _, _ = r.Write(p) }(payloadR) } wg.Wait() // drain channels (best-effort) timed := time.After(500 * time.Millisecond) for { select { case <-wL.ch: case <-wR.ch: case <-timed: return } } } // ensure testWriter implements the interface var _ packetswitch.NetworkLayerPacketWriter = (*testWriter)(nil) ================================================ FILE: common/packetswitch/packetswitch.go ================================================ package packetswitch import "github.com/v2fly/v2ray-core/v5/common" type NetworkLayerDevice interface { common.Closable NetworkLayerPacketWriter NetworkLayerPacketReader } type NetworkLayerPacketWriter interface { Write(packet []byte) (n int, err error) } type NetworkLayerPacketReader interface { OnAttach(writer NetworkLayerPacketWriter) error } ================================================ FILE: common/peer/latency.go ================================================ package peer import ( "sync" ) type Latency interface { Value() uint64 } type HasLatency interface { ConnectionLatency() Latency HandshakeLatency() Latency } type AverageLatency struct { access sync.Mutex value uint64 } func (al *AverageLatency) Update(newValue uint64) { al.access.Lock() defer al.access.Unlock() al.value = (al.value + newValue*2) / 3 } func (al *AverageLatency) Value() uint64 { return al.value } ================================================ FILE: common/peer/peer.go ================================================ package peer ================================================ FILE: common/platform/ctlcmd/attr_other.go ================================================ //go:build !windows // +build !windows package ctlcmd import "syscall" func getSysProcAttr() *syscall.SysProcAttr { return nil } ================================================ FILE: common/platform/filesystem/file.go ================================================ package filesystem import ( "io" "os" "path/filepath" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem/fsifce" ) var NewFileSeeker fsifce.FileSeekerFunc = func(path string) (io.ReadSeekCloser, error) { return os.Open(path) } var NewFileReader fsifce.FileReaderFunc = func(path string) (io.ReadCloser, error) { return os.Open(path) } var NewFileWriter fsifce.FileWriterFunc = func(path string) (io.WriteCloser, error) { basePath := filepath.Dir(path) if err := os.MkdirAll(basePath, 0o700); err != nil { return nil, err } return os.Create(path) } var NewFileRemover fsifce.FileRemoveFunc = os.Remove var NewFileReadDir fsifce.FileReadDirFunc = os.ReadDir func ReadFile(path string) ([]byte, error) { reader, err := NewFileReader(path) if err != nil { return nil, err } defer reader.Close() return buf.ReadAllToBytes(reader) } func WriteFile(path string, payload []byte) error { writer, err := NewFileWriter(path) if err != nil { return err } defer writer.Close() return buf.WriteAllBytes(writer, payload) } func ReadAsset(file string) ([]byte, error) { return ReadFile(platform.GetAssetLocation(file)) } func CopyFile(dst string, src string, perm os.FileMode) error { bytes, err := ReadFile(src) if err != nil { return err } f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, perm) if err != nil { return err } defer f.Close() _, err = f.Write(bytes) return err } ================================================ FILE: common/platform/filesystem/fsifce/ifce.go ================================================ package fsifce import ( "io" "io/fs" ) type FileSeekerFunc func(path string) (io.ReadSeekCloser, error) type FileReaderFunc func(path string) (io.ReadCloser, error) type FileWriterFunc func(path string) (io.WriteCloser, error) type FileReadDirFunc func(path string) ([]fs.DirEntry, error) type FileRemoveFunc func(path string) error ================================================ FILE: common/platform/others.go ================================================ //go:build !windows // +build !windows package platform import ( "path/filepath" "github.com/adrg/xdg" ) func LineSeparator() string { return "\n" } // GetAssetLocation search for `file` in certain locations func GetAssetLocation(file string) string { const name = "v2ray.location.asset" assetPath := NewEnvFlag(name).GetValue(getExecutableDir) defPath := filepath.Join(assetPath, file) relPath := filepath.Join("v2ray", file) fullPath, err := xdg.SearchDataFile(relPath) if err != nil { return defPath } return fullPath } ================================================ FILE: common/platform/platform.go ================================================ package platform import ( "os" "path/filepath" "strconv" "strings" ) type EnvFlag struct { Name string AltName string } func NewEnvFlag(name string) EnvFlag { return EnvFlag{ Name: name, AltName: NormalizeEnvName(name), } } func (f EnvFlag) GetValue(defaultValue func() string) string { if v, found := os.LookupEnv(f.Name); found { return v } if len(f.AltName) > 0 { if v, found := os.LookupEnv(f.AltName); found { return v } } return defaultValue() } func (f EnvFlag) GetValueAsInt(defaultValue int) int { useDefaultValue := false s := f.GetValue(func() string { useDefaultValue = true return "" }) if useDefaultValue { return defaultValue } v, err := strconv.ParseInt(s, 10, 32) if err != nil { return defaultValue } return int(v) } func NormalizeEnvName(name string) string { return strings.ReplaceAll(strings.ToUpper(strings.TrimSpace(name)), ".", "_") } func getExecutableDir() string { exec, err := os.Executable() if err != nil { return "" } return filepath.Dir(exec) } func getExecutableSubDir(dir string) func() string { return func() string { return filepath.Join(getExecutableDir(), dir) } } func GetPluginDirectory() string { const name = "v2ray.location.plugin" pluginDir := NewEnvFlag(name).GetValue(getExecutableSubDir("plugins")) return pluginDir } func GetConfigurationPath() string { const name = "v2ray.location.config" configPath := NewEnvFlag(name).GetValue(getExecutableDir) return filepath.Join(configPath, "config.json") } // GetConfDirPath reads "v2ray.location.confdir" func GetConfDirPath() string { const name = "v2ray.location.confdir" configPath := NewEnvFlag(name).GetValue(func() string { return "" }) return configPath } ================================================ FILE: common/platform/platform_test.go ================================================ package platform_test import ( "errors" "io/fs" "os" "path/filepath" "runtime" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/platform" ) func TestNormalizeEnvName(t *testing.T) { cases := []struct { input string output string }{ { input: "a", output: "A", }, { input: "a.a", output: "A_A", }, { input: "A.A.B", output: "A_A_B", }, } for _, test := range cases { if v := platform.NormalizeEnvName(test.input); v != test.output { t.Error("unexpected output: ", v, " want ", test.output) } } } func TestEnvFlag(t *testing.T) { if v := (platform.EnvFlag{ Name: "xxxxx.y", }.GetValueAsInt(10)); v != 10 { t.Error("env value: ", v) } } // TestWrongErrorCheckOnOSStat is a test to detect the misuse of error handling // in os.Stat, which will lead to failure to find & read geoip & geosite files. func TestWrongErrorCheckOnOSStat(t *testing.T) { theExpectedDir := filepath.Join("usr", "local", "share", "v2ray") getAssetLocation := func(file string) string { for _, p := range []string{ filepath.Join(theExpectedDir, file), } { // errors.Is(fs.ErrNotExist, err) is a mistake supposed Not to // be discovered by the Go runtime, which will lead to failure to // find & read geoip & geosite files. // The correct code is `errors.Is(err, fs.ErrNotExist)` if _, err := os.Stat(p); err != nil && errors.Is(fs.ErrNotExist, err) { //nolint: staticcheck continue } // asset found return p } return filepath.Join("the", "wrong", "path", "not-exist.txt") } notExist := getAssetLocation("not-exist.txt") if filepath.Dir(notExist) != theExpectedDir { t.Error("asset dir:", notExist, "not in", theExpectedDir) } } func TestGetAssetLocation(t *testing.T) { exec, err := os.Executable() common.Must(err) loc := platform.GetAssetLocation("t") if filepath.Dir(loc) != filepath.Dir(exec) { t.Error("asset dir: ", loc, " not in ", exec) } os.Setenv("v2ray.location.asset", "/v2ray") if runtime.GOOS == "windows" { if v := platform.GetAssetLocation("t"); v != "\\v2ray\\t" { t.Error("asset loc: ", v) } } else { if v := platform.GetAssetLocation("t"); v != "/v2ray/t" { t.Error("asset loc: ", v) } } } ================================================ FILE: common/platform/securedload/embedded.go ================================================ package securedload const allowedHashes = `SHA256 (!#project==v2fly) = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff SHA256 (!#version==embedded) = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff SHA256 (subscriptions/subscriptionsDefinition.v2flyTemplate) = 3f165dba7de0d7c506fbdff3275ea64b76f307df435316a3ea0914ee957793ab SHA256 (browserforwarder/index.html) = 34f2c573724256421ade769bda18eeac85172bf0aaed00d7b90e41e843a2caef SHA256 (browserforwarder/index.js) = cb587a075bb0addcdc0d22c9222a48d2c7004b54935b5021379d3d35dc1f2927 ` ================================================ FILE: common/platform/securedload/embeddedhash.go ================================================ package securedload import ( "bytes" "crypto/sha256" "encoding/hex" "path/filepath" "strings" "github.com/v2fly/VSign/insmgr" "github.com/v2fly/VSign/signerVerify" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" ) type EmbeddedHashProtectedLoader struct { checkedFile map[string]string } func (e EmbeddedHashProtectedLoader) VerifyAndLoad(filename string) ([]byte, error) { platformFileName := filepath.FromSlash(filename) fileContent, err := filesystem.ReadFile(platform.GetAssetLocation(platformFileName)) if err != nil { return nil, newError("Cannot find file", filename).Base(err) } fileHash := sha256.Sum256(fileContent) fileHashAsString := hex.EncodeToString(fileHash[:]) if fileNameVerified, ok := e.checkedFile[fileHashAsString]; ok { for _, filenameVerifiedIndividual := range strings.Split(fileNameVerified, ";") { if strings.HasSuffix(filenameVerifiedIndividual, filename) { return fileContent, nil } } } return nil, newError("Unrecognized file at ", filename, " can not be loaded for execution") } func NewEmbeddedHashProtectedLoader() *EmbeddedHashProtectedLoader { instructions := insmgr.ReadAllIns(bytes.NewReader([]byte(allowedHashes))) checkedFile, _, ok := signerVerify.CheckAsClient(instructions, "v2fly", true) if !ok { panic("Embedded Hash data is invalid") } return &EmbeddedHashProtectedLoader{checkedFile: checkedFile} } func init() { RegisterProtectedLoader("embedded", NewEmbeddedHashProtectedLoader()) } ================================================ FILE: common/platform/securedload/errors.generated.go ================================================ package securedload import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/platform/securedload/file.go ================================================ package securedload func GetAssetSecured(name string) ([]byte, error) { var err error for k, v := range knownProtectedLoader { loadedData, errLoad := v.VerifyAndLoad(name) if errLoad == nil { return loadedData, nil } err = newError(k, " is not loading executable file").Base(errLoad) } return nil, err } ================================================ FILE: common/platform/securedload/securedload.go ================================================ package securedload //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: common/platform/securedload/verify.go ================================================ package securedload type ProtectedLoader interface { VerifyAndLoad(filename string) ([]byte, error) } var knownProtectedLoader map[string]ProtectedLoader func RegisterProtectedLoader(name string, sv ProtectedLoader) { if knownProtectedLoader == nil { knownProtectedLoader = map[string]ProtectedLoader{} } knownProtectedLoader[name] = sv } ================================================ FILE: common/platform/windows.go ================================================ //go:build windows // +build windows package platform import "path/filepath" func LineSeparator() string { return "\r\n" } // GetAssetLocation search for `file` in the excutable dir func GetAssetLocation(file string) string { const name = "v2ray.location.asset" assetPath := NewEnvFlag(name).GetValue(getExecutableDir) return filepath.Join(assetPath, file) } ================================================ FILE: common/protocol/account.go ================================================ package protocol // Account is a user identity used for authentication. type Account interface { Equals(Account) bool } // AsAccount is an object can be converted into account. type AsAccount interface { AsAccount() (Account, error) } ================================================ FILE: common/protocol/address.go ================================================ package protocol import ( "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" ) type AddressOption func(*option) func PortThenAddress() AddressOption { return func(p *option) { p.portFirst = true } } func AddressFamilyByte(b byte, f net.AddressFamily) AddressOption { if b >= 16 { panic("address family byte too big") } return func(p *option) { p.addrTypeMap[b] = f p.addrByteMap[f] = b } } type AddressTypeParser func(byte) byte func WithAddressTypeParser(atp AddressTypeParser) AddressOption { return func(p *option) { p.typeParser = atp } } type AddressSerializer interface { ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error } const afInvalid = 255 type option struct { addrTypeMap [16]net.AddressFamily addrByteMap [16]byte portFirst bool typeParser AddressTypeParser } // NewAddressParser creates a new AddressParser func NewAddressParser(options ...AddressOption) AddressSerializer { var o option for i := range o.addrByteMap { o.addrByteMap[i] = afInvalid } for i := range o.addrTypeMap { o.addrTypeMap[i] = net.AddressFamily(afInvalid) } for _, opt := range options { opt(&o) } ap := &addressParser{ addrByteMap: o.addrByteMap, addrTypeMap: o.addrTypeMap, } if o.typeParser != nil { ap.typeParser = o.typeParser } if o.portFirst { return portFirstAddressParser{ap: ap} } return portLastAddressParser{ap: ap} } type portFirstAddressParser struct { ap *addressParser } func (p portFirstAddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) { if buffer == nil { buffer = buf.New() defer buffer.Release() } port, err := readPort(buffer, input) if err != nil { return nil, 0, err } addr, err := p.ap.readAddress(buffer, input) if err != nil { return nil, 0, err } return addr, port, nil } func (p portFirstAddressParser) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error { if err := writePort(writer, port); err != nil { return err } return p.ap.writeAddress(writer, addr) } type portLastAddressParser struct { ap *addressParser } func (p portLastAddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) { if buffer == nil { buffer = buf.New() defer buffer.Release() } addr, err := p.ap.readAddress(buffer, input) if err != nil { return nil, 0, err } port, err := readPort(buffer, input) if err != nil { return nil, 0, err } return addr, port, nil } func (p portLastAddressParser) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error { if err := p.ap.writeAddress(writer, addr); err != nil { return err } return writePort(writer, port) } func readPort(b *buf.Buffer, reader io.Reader) (net.Port, error) { if _, err := b.ReadFullFrom(reader, 2); err != nil { return 0, err } return net.PortFromBytes(b.BytesFrom(-2)), nil } func writePort(writer io.Writer, port net.Port) error { return common.Error2(serial.WriteUint16(writer, port.Value())) } func maybeIPPrefix(b byte) bool { return b == '[' || (b >= '0' && b <= '9') } func isValidDomain(d string) bool { for _, c := range d { if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '.' || c == '_') { return false } } return true } type addressParser struct { addrTypeMap [16]net.AddressFamily addrByteMap [16]byte typeParser AddressTypeParser } func (p *addressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Address, error) { if _, err := b.ReadFullFrom(reader, 1); err != nil { return nil, err } addrType := b.Byte(b.Len() - 1) if p.typeParser != nil { addrType = p.typeParser(addrType) } if addrType >= 16 { return nil, newError("unknown address type: ", addrType) } addrFamily := p.addrTypeMap[addrType] if addrFamily == net.AddressFamily(afInvalid) { return nil, newError("unknown address type: ", addrType) } switch addrFamily { case net.AddressFamilyIPv4: if _, err := b.ReadFullFrom(reader, 4); err != nil { return nil, err } return net.IPAddress(b.BytesFrom(-4)), nil case net.AddressFamilyIPv6: if _, err := b.ReadFullFrom(reader, 16); err != nil { return nil, err } return net.IPAddress(b.BytesFrom(-16)), nil case net.AddressFamilyDomain: if _, err := b.ReadFullFrom(reader, 1); err != nil { return nil, err } domainLength := int32(b.Byte(b.Len() - 1)) if _, err := b.ReadFullFrom(reader, domainLength); err != nil { return nil, err } domain := string(b.BytesFrom(-domainLength)) if maybeIPPrefix(domain[0]) { addr := net.ParseAddress(domain) if addr.Family().IsIP() { return addr, nil } } if !isValidDomain(domain) { return nil, newError("invalid domain name: ", domain) } return net.DomainAddress(domain), nil default: panic("impossible case") } } func (p *addressParser) writeAddress(writer io.Writer, address net.Address) error { tb := p.addrByteMap[address.Family()] if tb == afInvalid { return newError("unknown address family", address.Family()) } switch address.Family() { case net.AddressFamilyIPv4, net.AddressFamilyIPv6: if _, err := writer.Write([]byte{tb}); err != nil { return err } if _, err := writer.Write(address.IP()); err != nil { return err } case net.AddressFamilyDomain: domain := address.Domain() if IsDomainTooLong(domain) { return newError("Super long domain is not supported: ", domain) } if _, err := writer.Write([]byte{tb, byte(len(domain))}); err != nil { return err } if _, err := writer.Write([]byte(domain)); err != nil { return err } default: panic("Unknown family type.") } return nil } ================================================ FILE: common/protocol/address_test.go ================================================ package protocol_test import ( "bytes" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" . "github.com/v2fly/v2ray-core/v5/common/protocol" ) func TestAddressReading(t *testing.T) { data := []struct { Options []AddressOption Input []byte Address net.Address Port net.Port Error bool }{ { Options: []AddressOption{}, Input: []byte{}, Error: true, }, { Options: []AddressOption{}, Input: []byte{0, 0, 0, 0, 0}, Error: true, }, { Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)}, Input: []byte{1, 0, 0, 0, 0, 0, 53}, Address: net.IPAddress([]byte{0, 0, 0, 0}), Port: net.Port(53), }, { Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4), PortThenAddress()}, Input: []byte{0, 53, 1, 0, 0, 0, 0}, Address: net.IPAddress([]byte{0, 0, 0, 0}), Port: net.Port(53), }, { Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)}, Input: []byte{1, 0, 0, 0, 0}, Error: true, }, { Options: []AddressOption{AddressFamilyByte(0x04, net.AddressFamilyIPv6)}, Input: []byte{4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 80}, Address: net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}), Port: net.Port(80), }, { Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, Input: []byte{3, 9, 118, 50, 102, 108, 121, 46, 111, 114, 103, 0, 80}, Address: net.DomainAddress("v2fly.org"), Port: net.Port(80), }, { Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, Input: []byte{3, 9, 118, 50, 102, 108, 121, 46, 111, 114, 103, 0}, Error: true, }, { Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, Input: []byte{3, 7, 56, 46, 56, 46, 56, 46, 56, 0, 80}, Address: net.ParseAddress("8.8.8.8"), Port: net.Port(80), }, { Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, Input: []byte{3, 7, 10, 46, 56, 46, 56, 46, 56, 0, 80}, Error: true, }, { Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, Input: append(append([]byte{3, 24}, []byte("2a00:1450:4007:816::200e")...), 0, 80), Address: net.ParseAddress("2a00:1450:4007:816::200e"), Port: net.Port(80), }, } for _, tc := range data { b := buf.New() parser := NewAddressParser(tc.Options...) addr, port, err := parser.ReadAddressPort(b, bytes.NewReader(tc.Input)) b.Release() if tc.Error { if err == nil { t.Errorf("Expect error but not: %v", tc) } } else { if err != nil { t.Errorf("Expect no error but: %s %v", err.Error(), tc) } if addr != tc.Address { t.Error("Got address ", addr.String(), " want ", tc.Address.String()) } if tc.Port != port { t.Error("Got port ", port, " want ", tc.Port) } } } } func TestAddressWriting(t *testing.T) { data := []struct { Options []AddressOption Address net.Address Port net.Port Bytes []byte Error bool }{ { Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)}, Address: net.LocalHostIP, Port: net.Port(80), Bytes: []byte{1, 127, 0, 0, 1, 0, 80}, }, } for _, tc := range data { parser := NewAddressParser(tc.Options...) b := buf.New() err := parser.WriteAddressPort(b, tc.Address, tc.Port) if tc.Error { if err == nil { t.Error("Expect error but nil") } } else { common.Must(err) if diff := cmp.Diff(tc.Bytes, b.Bytes()); diff != "" { t.Error(err) } } } } func BenchmarkAddressReadingIPv4(b *testing.B) { parser := NewAddressParser(AddressFamilyByte(0x01, net.AddressFamilyIPv4)) cache := buf.New() defer cache.Release() payload := buf.New() defer payload.Release() raw := []byte{1, 0, 0, 0, 0, 0, 53} payload.Write(raw) b.ResetTimer() for i := 0; i < b.N; i++ { _, _, err := parser.ReadAddressPort(cache, payload) common.Must(err) cache.Clear() payload.Clear() payload.Extend(int32(len(raw))) } } func BenchmarkAddressReadingIPv6(b *testing.B) { parser := NewAddressParser(AddressFamilyByte(0x04, net.AddressFamilyIPv6)) cache := buf.New() defer cache.Release() payload := buf.New() defer payload.Release() raw := []byte{4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 80} payload.Write(raw) b.ResetTimer() for i := 0; i < b.N; i++ { _, _, err := parser.ReadAddressPort(cache, payload) common.Must(err) cache.Clear() payload.Clear() payload.Extend(int32(len(raw))) } } func BenchmarkAddressReadingDomain(b *testing.B) { parser := NewAddressParser(AddressFamilyByte(0x03, net.AddressFamilyDomain)) cache := buf.New() defer cache.Release() payload := buf.New() defer payload.Release() raw := []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 80} payload.Write(raw) b.ResetTimer() for i := 0; i < b.N; i++ { _, _, err := parser.ReadAddressPort(cache, payload) common.Must(err) cache.Clear() payload.Clear() payload.Extend(int32(len(raw))) } } func BenchmarkAddressWritingIPv4(b *testing.B) { parser := NewAddressParser(AddressFamilyByte(0x01, net.AddressFamilyIPv4)) writer := buf.New() defer writer.Release() b.ResetTimer() for i := 0; i < b.N; i++ { common.Must(parser.WriteAddressPort(writer, net.LocalHostIP, net.Port(80))) writer.Clear() } } func BenchmarkAddressWritingIPv6(b *testing.B) { parser := NewAddressParser(AddressFamilyByte(0x04, net.AddressFamilyIPv6)) writer := buf.New() defer writer.Release() b.ResetTimer() for i := 0; i < b.N; i++ { common.Must(parser.WriteAddressPort(writer, net.LocalHostIPv6, net.Port(80))) writer.Clear() } } func BenchmarkAddressWritingDomain(b *testing.B) { parser := NewAddressParser(AddressFamilyByte(0x02, net.AddressFamilyDomain)) writer := buf.New() defer writer.Release() b.ResetTimer() for i := 0; i < b.N; i++ { common.Must(parser.WriteAddressPort(writer, net.DomainAddress("www.v2fly.org"), net.Port(80))) writer.Clear() } } ================================================ FILE: common/protocol/bittorrent/bittorrent.go ================================================ package bittorrent import ( "encoding/binary" "errors" "math" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" ) type SniffHeader struct{} func (h *SniffHeader) Protocol() string { return "bittorrent" } func (h *SniffHeader) Domain() string { return "" } var errNotBittorrent = errors.New("not bittorrent header") func SniffBittorrent(b []byte) (*SniffHeader, error) { if len(b) < 20 { return nil, common.ErrNoClue } if b[0] == 19 && string(b[1:20]) == "BitTorrent protocol" { return &SniffHeader{}, nil } return nil, errNotBittorrent } func SniffUTP(b []byte) (*SniffHeader, error) { if len(b) < 20 { return nil, common.ErrNoClue } buffer := buf.FromBytes(b) var typeAndVersion uint8 if binary.Read(buffer, binary.BigEndian, &typeAndVersion) != nil { return nil, common.ErrNoClue } else if b[0]>>4&0xF > 4 || b[0]&0xF != 1 { return nil, errNotBittorrent } var extension uint8 if binary.Read(buffer, binary.BigEndian, &extension) != nil { return nil, common.ErrNoClue } else if extension != 0 && extension != 1 { return nil, errNotBittorrent } for extension != 0 { if extension != 1 { return nil, errNotBittorrent } if binary.Read(buffer, binary.BigEndian, &extension) != nil { return nil, common.ErrNoClue } var length uint8 if err := binary.Read(buffer, binary.BigEndian, &length); err != nil { return nil, common.ErrNoClue } if common.Error2(buffer.ReadBytes(int32(length))) != nil { return nil, common.ErrNoClue } } if common.Error2(buffer.ReadBytes(2)) != nil { return nil, common.ErrNoClue } var timestamp uint32 if err := binary.Read(buffer, binary.BigEndian, ×tamp); err != nil { return nil, common.ErrNoClue } if math.Abs(float64(time.Now().UnixMicro()-int64(timestamp))) > float64(24*time.Hour) { return nil, errNotBittorrent } return &SniffHeader{}, nil } ================================================ FILE: common/protocol/context.go ================================================ package protocol import ( "context" ) type key int const ( requestKey key = iota ) func ContextWithRequestHeader(ctx context.Context, request *RequestHeader) context.Context { return context.WithValue(ctx, requestKey, request) } func RequestHeaderFromContext(ctx context.Context) *RequestHeader { request := ctx.Value(requestKey) if request == nil { return nil } return request.(*RequestHeader) } ================================================ FILE: common/protocol/dns/errors.generated.go ================================================ package dns import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/protocol/dns/io.go ================================================ package dns import ( "encoding/binary" "sync" "golang.org/x/net/dns/dnsmessage" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/serial" ) func PackMessage(msg *dnsmessage.Message) (*buf.Buffer, error) { buffer := buf.New() rawBytes := buffer.Extend(buf.Size) packed, err := msg.AppendPack(rawBytes[:0]) if err != nil { buffer.Release() return nil, err } buffer.Resize(0, int32(len(packed))) return buffer, nil } type MessageReader interface { ReadMessage() (*buf.Buffer, error) } type UDPReader struct { buf.Reader access sync.Mutex cache buf.MultiBuffer } func (r *UDPReader) readCache() *buf.Buffer { r.access.Lock() defer r.access.Unlock() mb, b := buf.SplitFirst(r.cache) r.cache = mb return b } func (r *UDPReader) refill() error { mb, err := r.Reader.ReadMultiBuffer() if err != nil { return err } r.access.Lock() r.cache = mb r.access.Unlock() return nil } // ReadMessage implements MessageReader. func (r *UDPReader) ReadMessage() (*buf.Buffer, error) { for { b := r.readCache() if b != nil { return b, nil } if err := r.refill(); err != nil { return nil, err } } } // Close implements common.Closable. func (r *UDPReader) Close() error { defer func() { r.access.Lock() buf.ReleaseMulti(r.cache) r.cache = nil r.access.Unlock() }() return common.Close(r.Reader) } type TCPReader struct { reader *buf.BufferedReader } func NewTCPReader(reader buf.Reader) *TCPReader { return &TCPReader{ reader: &buf.BufferedReader{ Reader: reader, }, } } func (r *TCPReader) ReadMessage() (*buf.Buffer, error) { size, err := serial.ReadUint16(r.reader) if err != nil { return nil, err } if size > buf.Size { return nil, newError("message size too large: ", size) } b := buf.New() if _, err := b.ReadFullFrom(r.reader, int32(size)); err != nil { return nil, err } return b, nil } func (r *TCPReader) Interrupt() { common.Interrupt(r.reader) } func (r *TCPReader) Close() error { return common.Close(r.reader) } type MessageWriter interface { WriteMessage(msg *buf.Buffer) error } type UDPWriter struct { buf.Writer } func (w *UDPWriter) WriteMessage(b *buf.Buffer) error { return w.WriteMultiBuffer(buf.MultiBuffer{b}) } type TCPWriter struct { buf.Writer } func (w *TCPWriter) WriteMessage(b *buf.Buffer) error { if b.IsEmpty() { return nil } mb := make(buf.MultiBuffer, 0, 2) size := buf.New() binary.BigEndian.PutUint16(size.Extend(2), uint16(b.Len())) mb = append(mb, size, b) return w.WriteMultiBuffer(mb) } ================================================ FILE: common/protocol/errors.generated.go ================================================ package protocol import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/protocol/headers.go ================================================ package protocol import ( "runtime" "golang.org/x/sys/cpu" "github.com/v2fly/v2ray-core/v5/common/bitmask" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/uuid" ) // RequestCommand is a custom command in a proxy request. type RequestCommand byte const ( RequestCommandTCP = RequestCommand(0x01) RequestCommandUDP = RequestCommand(0x02) RequestCommandMux = RequestCommand(0x03) ) func (c RequestCommand) TransferType() TransferType { switch c { case RequestCommandTCP, RequestCommandMux: return TransferTypeStream case RequestCommandUDP: return TransferTypePacket default: return TransferTypeStream } } const ( // RequestOptionChunkStream indicates request payload is chunked. Each chunk consists of length, authentication and payload. RequestOptionChunkStream bitmask.Byte = 0x01 // RequestOptionConnectionReuse indicates client side expects to reuse the connection. RequestOptionConnectionReuse bitmask.Byte = 0x02 RequestOptionChunkMasking bitmask.Byte = 0x04 RequestOptionGlobalPadding bitmask.Byte = 0x08 RequestOptionAuthenticatedLength bitmask.Byte = 0x10 ) type RequestHeader struct { Version byte Command RequestCommand Option bitmask.Byte Security SecurityType Port net.Port Address net.Address User *MemoryUser } func (h *RequestHeader) Destination() net.Destination { if h.Command == RequestCommandUDP { return net.UDPDestination(h.Address, h.Port) } return net.TCPDestination(h.Address, h.Port) } const ( ResponseOptionConnectionReuse bitmask.Byte = 0x01 ) type ResponseCommand interface{} type ResponseHeader struct { Option bitmask.Byte Command ResponseCommand } type CommandSwitchAccount struct { Host net.Address Port net.Port ID uuid.UUID Level uint32 AlterIds uint16 ValidMin byte } var ( hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL // Keep in sync with crypto/aes/cipher_s390x.go. hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || runtime.GOARCH == "arm64" && hasGCMAsmARM64 || runtime.GOARCH == "s390x" && hasGCMAsmS390X ) func (sc *SecurityConfig) GetSecurityType() SecurityType { if sc == nil || sc.Type == SecurityType_AUTO { if hasAESGCMHardwareSupport { return SecurityType_AES128_GCM } return SecurityType_CHACHA20_POLY1305 } return sc.Type } func IsDomainTooLong(domain string) bool { return len(domain) > 255 } ================================================ FILE: common/protocol/headers.pb.go ================================================ package protocol import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type SecurityType int32 const ( SecurityType_UNKNOWN SecurityType = 0 SecurityType_LEGACY SecurityType = 1 SecurityType_AUTO SecurityType = 2 SecurityType_AES128_GCM SecurityType = 3 SecurityType_CHACHA20_POLY1305 SecurityType = 4 SecurityType_NONE SecurityType = 5 SecurityType_ZERO SecurityType = 6 ) // Enum value maps for SecurityType. var ( SecurityType_name = map[int32]string{ 0: "UNKNOWN", 1: "LEGACY", 2: "AUTO", 3: "AES128_GCM", 4: "CHACHA20_POLY1305", 5: "NONE", 6: "ZERO", } SecurityType_value = map[string]int32{ "UNKNOWN": 0, "LEGACY": 1, "AUTO": 2, "AES128_GCM": 3, "CHACHA20_POLY1305": 4, "NONE": 5, "ZERO": 6, } ) func (x SecurityType) Enum() *SecurityType { p := new(SecurityType) *p = x return p } func (x SecurityType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (SecurityType) Descriptor() protoreflect.EnumDescriptor { return file_common_protocol_headers_proto_enumTypes[0].Descriptor() } func (SecurityType) Type() protoreflect.EnumType { return &file_common_protocol_headers_proto_enumTypes[0] } func (x SecurityType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use SecurityType.Descriptor instead. func (SecurityType) EnumDescriptor() ([]byte, []int) { return file_common_protocol_headers_proto_rawDescGZIP(), []int{0} } type SecurityConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Type SecurityType `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.common.protocol.SecurityType" json:"type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SecurityConfig) Reset() { *x = SecurityConfig{} mi := &file_common_protocol_headers_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SecurityConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecurityConfig) ProtoMessage() {} func (x *SecurityConfig) ProtoReflect() protoreflect.Message { mi := &file_common_protocol_headers_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecurityConfig.ProtoReflect.Descriptor instead. func (*SecurityConfig) Descriptor() ([]byte, []int) { return file_common_protocol_headers_proto_rawDescGZIP(), []int{0} } func (x *SecurityConfig) GetType() SecurityType { if x != nil { return x.Type } return SecurityType_UNKNOWN } var File_common_protocol_headers_proto protoreflect.FileDescriptor const file_common_protocol_headers_proto_rawDesc = "" + "\n" + "\x1dcommon/protocol/headers.proto\x12\x1av2ray.core.common.protocol\"N\n" + "\x0eSecurityConfig\x12<\n" + "\x04type\x18\x01 \x01(\x0e2(.v2ray.core.common.protocol.SecurityTypeR\x04type*l\n" + "\fSecurityType\x12\v\n" + "\aUNKNOWN\x10\x00\x12\n" + "\n" + "\x06LEGACY\x10\x01\x12\b\n" + "\x04AUTO\x10\x02\x12\x0e\n" + "\n" + "AES128_GCM\x10\x03\x12\x15\n" + "\x11CHACHA20_POLY1305\x10\x04\x12\b\n" + "\x04NONE\x10\x05\x12\b\n" + "\x04ZERO\x10\x06Bo\n" + "\x1ecom.v2ray.core.common.protocolP\x01Z.github.com/v2fly/v2ray-core/v5/common/protocol\xaa\x02\x1aV2Ray.Core.Common.Protocolb\x06proto3" var ( file_common_protocol_headers_proto_rawDescOnce sync.Once file_common_protocol_headers_proto_rawDescData []byte ) func file_common_protocol_headers_proto_rawDescGZIP() []byte { file_common_protocol_headers_proto_rawDescOnce.Do(func() { file_common_protocol_headers_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_protocol_headers_proto_rawDesc), len(file_common_protocol_headers_proto_rawDesc))) }) return file_common_protocol_headers_proto_rawDescData } var file_common_protocol_headers_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_common_protocol_headers_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_common_protocol_headers_proto_goTypes = []any{ (SecurityType)(0), // 0: v2ray.core.common.protocol.SecurityType (*SecurityConfig)(nil), // 1: v2ray.core.common.protocol.SecurityConfig } var file_common_protocol_headers_proto_depIdxs = []int32{ 0, // 0: v2ray.core.common.protocol.SecurityConfig.type:type_name -> v2ray.core.common.protocol.SecurityType 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_common_protocol_headers_proto_init() } func file_common_protocol_headers_proto_init() { if File_common_protocol_headers_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_protocol_headers_proto_rawDesc), len(file_common_protocol_headers_proto_rawDesc)), NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_protocol_headers_proto_goTypes, DependencyIndexes: file_common_protocol_headers_proto_depIdxs, EnumInfos: file_common_protocol_headers_proto_enumTypes, MessageInfos: file_common_protocol_headers_proto_msgTypes, }.Build() File_common_protocol_headers_proto = out.File file_common_protocol_headers_proto_goTypes = nil file_common_protocol_headers_proto_depIdxs = nil } ================================================ FILE: common/protocol/headers.proto ================================================ syntax = "proto3"; package v2ray.core.common.protocol; option csharp_namespace = "V2Ray.Core.Common.Protocol"; option go_package = "github.com/v2fly/v2ray-core/v5/common/protocol"; option java_package = "com.v2ray.core.common.protocol"; option java_multiple_files = true; enum SecurityType { UNKNOWN = 0; LEGACY = 1; AUTO = 2; AES128_GCM = 3; CHACHA20_POLY1305 = 4; NONE = 5; ZERO = 6; } message SecurityConfig { SecurityType type = 1; } ================================================ FILE: common/protocol/http/headers.go ================================================ package http import ( "net/http" "strconv" "strings" "github.com/v2fly/v2ray-core/v5/common/net" ) // ParseXForwardedFor parses X-Forwarded-For header in http headers, and return the IP list in it. func ParseXForwardedFor(header http.Header) []net.Address { xff := header.Get("X-Forwarded-For") if xff == "" { return nil } list := strings.Split(xff, ",") addrs := make([]net.Address, 0, len(list)) for _, proxy := range list { addrs = append(addrs, net.ParseAddress(proxy)) } return addrs } // RemoveHopByHopHeaders remove hop by hop headers in http header list. func RemoveHopByHopHeaders(header http.Header) { // Strip hop-by-hop header based on RFC: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1 // https://www.mnot.net/blog/2011/07/11/what_proxies_must_do header.Del("Proxy-Connection") header.Del("Proxy-Authenticate") header.Del("Proxy-Authorization") header.Del("TE") header.Del("Trailers") header.Del("Transfer-Encoding") header.Del("Upgrade") header.Del("Keep-Alive") connections := header.Get("Connection") header.Del("Connection") if connections == "" { return } for _, h := range strings.Split(connections, ",") { header.Del(strings.TrimSpace(h)) } } // ParseHost splits host and port from a raw string. Default port is used when raw string doesn't contain port. func ParseHost(rawHost string, defaultPort net.Port) (net.Destination, error) { port := defaultPort host, rawPort, err := net.SplitHostPort(rawHost) if err != nil { if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") { host = rawHost } else { return net.Destination{}, err } } else if len(rawPort) > 0 { intPort, err := strconv.ParseUint(rawPort, 0, 16) if err != nil { return net.Destination{}, err } port = net.Port(intPort) } return net.TCPDestination(net.ParseAddress(host), port), nil } ================================================ FILE: common/protocol/http/headers_test.go ================================================ package http_test import ( "bufio" "net/http" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" . "github.com/v2fly/v2ray-core/v5/common/protocol/http" ) func TestParseXForwardedFor(t *testing.T) { header := http.Header{} header.Add("X-Forwarded-For", "129.78.138.66, 129.78.64.103") addrs := ParseXForwardedFor(header) if r := cmp.Diff(addrs, []net.Address{net.ParseAddress("129.78.138.66"), net.ParseAddress("129.78.64.103")}); r != "" { t.Error(r) } } func TestHopByHopHeadersRemoving(t *testing.T) { rawRequest := `GET /pkg/net/http/ HTTP/1.1 Host: golang.org Connection: keep-alive,Foo, Bar Foo: foo Bar: bar Proxy-Connection: keep-alive Proxy-Authenticate: abc Accept-Encoding: gzip Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7 Cache-Control: no-cache Accept-Language: de,en;q=0.7,en-us;q=0.3 ` b := bufio.NewReader(strings.NewReader(rawRequest)) req, err := http.ReadRequest(b) common.Must(err) headers := []struct { Key string Value string }{ { Key: "Foo", Value: "foo", }, { Key: "Bar", Value: "bar", }, { Key: "Connection", Value: "keep-alive,Foo, Bar", }, { Key: "Proxy-Connection", Value: "keep-alive", }, { Key: "Proxy-Authenticate", Value: "abc", }, } for _, header := range headers { if v := req.Header.Get(header.Key); v != header.Value { t.Error("header ", header.Key, " = ", v, " want ", header.Value) } } RemoveHopByHopHeaders(req.Header) for _, header := range []string{"Connection", "Foo", "Bar", "Proxy-Connection", "Proxy-Authenticate"} { if v := req.Header.Get(header); v != "" { t.Error("header ", header, " = ", v) } } } func TestParseHost(t *testing.T) { testCases := []struct { RawHost string DefaultPort net.Port Destination net.Destination Error bool }{ { RawHost: "v2fly.org:80", DefaultPort: 443, Destination: net.TCPDestination(net.DomainAddress("v2fly.org"), 80), }, { RawHost: "tls.v2fly.org", DefaultPort: 443, Destination: net.TCPDestination(net.DomainAddress("tls.v2fly.org"), 443), }, { RawHost: "[2401:1bc0:51f0:ec08::1]:80", DefaultPort: 443, Destination: net.TCPDestination(net.ParseAddress("[2401:1bc0:51f0:ec08::1]"), 80), }, } for _, testCase := range testCases { dest, err := ParseHost(testCase.RawHost, testCase.DefaultPort) if testCase.Error { if err == nil { t.Error("for test case: ", testCase.RawHost, " expected error, but actually nil") } } else { if dest != testCase.Destination { t.Error("for test case: ", testCase.RawHost, " expected host: ", testCase.Destination.String(), " but got ", dest.String()) } } } } ================================================ FILE: common/protocol/http/sniff.go ================================================ package http import ( "bytes" "errors" "strings" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" ) type version byte type SniffHeader struct { host string } func (h *SniffHeader) Protocol() string { return "http1" } func (h *SniffHeader) Domain() string { return h.host } var ( // refer to https://pkg.go.dev/net/http@master#pkg-constants methods = [...]string{"get", "post", "head", "put", "delete", "options", "connect", "patch", "trace"} errNotHTTPMethod = errors.New("not an HTTP method") ) func beginWithHTTPMethod(b []byte) error { for _, m := range &methods { if len(b) >= len(m) && strings.EqualFold(string(b[:len(m)]), m) { return nil } if len(b) < len(m) { return common.ErrNoClue } } return errNotHTTPMethod } func SniffHTTP(b []byte) (*SniffHeader, error) { if err := beginWithHTTPMethod(b); err != nil { return nil, err } sh := &SniffHeader{} headers := bytes.Split(b, []byte{'\n'}) for i := 1; i < len(headers); i++ { header := headers[i] if len(header) == 0 { break } parts := bytes.SplitN(header, []byte{':'}, 2) if len(parts) != 2 { continue } key := strings.ToLower(string(parts[0])) if key == "host" { rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1]))) dest, err := ParseHost(rawHost, net.Port(80)) if err != nil { return nil, err } sh.host = dest.Address.String() } } if len(sh.host) > 0 { return sh, nil } return nil, common.ErrNoClue } ================================================ FILE: common/protocol/http/sniff_test.go ================================================ package http_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/protocol/http" ) func TestHTTPHeaders(t *testing.T) { cases := []struct { input string domain string err bool }{ { input: `GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1 Host: net.tutsplus.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120 Pragma: no-cache Cache-Control: no-cache`, domain: "net.tutsplus.com", }, { input: `POST /foo.php HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://localhost/test.php Content-Type: application/x-www-form-urlencoded Content-Length: 43 first_name=John&last_name=Doe&action=Submit`, domain: "localhost", }, { input: `X /foo.php HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://localhost/test.php Content-Type: application/x-www-form-urlencoded Content-Length: 43 first_name=John&last_name=Doe&action=Submit`, domain: "", err: true, }, { input: `GET /foo.php HTTP/1.1 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://localhost/test.php Content-Type: application/x-www-form-urlencoded Content-Length: 43 Host: localhost first_name=John&last_name=Doe&action=Submit`, domain: "", err: true, }, { input: `GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1`, domain: "", err: true, }, } for _, test := range cases { header, err := SniffHTTP([]byte(test.input)) if test.err { if err == nil { t.Errorf("Expect error but nil, in test: %v", test) } } else { if err != nil { t.Errorf("Expect no error but actually %s in test %v", err.Error(), test) } if header.Domain() != test.domain { t.Error("expected domain ", test.domain, " but got ", header.Domain()) } } } } ================================================ FILE: common/protocol/id.go ================================================ package protocol import ( "crypto/hmac" "crypto/md5" "hash" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/uuid" ) const ( IDBytesLen = 16 ) type IDHash func(key []byte) hash.Hash func DefaultIDHash(key []byte) hash.Hash { return hmac.New(md5.New, key) } // The ID of en entity, in the form of a UUID. type ID struct { uuid uuid.UUID cmdKey [IDBytesLen]byte } // Equals returns true if this ID equals to the other one. func (id *ID) Equals(another *ID) bool { return id.uuid.Equals(&(another.uuid)) } func (id *ID) Bytes() []byte { return id.uuid.Bytes() } func (id *ID) String() string { return id.uuid.String() } func (id *ID) UUID() uuid.UUID { return id.uuid } func (id ID) CmdKey() []byte { return id.cmdKey[:] } // NewID returns an ID with given UUID. func NewID(uuid uuid.UUID) *ID { id := &ID{uuid: uuid} md5hash := md5.New() common.Must2(md5hash.Write(uuid.Bytes())) common.Must2(md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21"))) md5hash.Sum(id.cmdKey[:0]) return id } func nextID(u *uuid.UUID) uuid.UUID { md5hash := md5.New() common.Must2(md5hash.Write(u.Bytes())) common.Must2(md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81"))) var newid uuid.UUID for { md5hash.Sum(newid[:0]) if !newid.Equals(u) { return newid } common.Must2(md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2"))) } } func NewAlterIDs(primary *ID, alterIDCount uint16) []*ID { alterIDs := make([]*ID, alterIDCount) prevID := primary.UUID() for idx := range alterIDs { newid := nextID(&prevID) alterIDs[idx] = NewID(newid) prevID = newid } return alterIDs } ================================================ FILE: common/protocol/id_test.go ================================================ package protocol_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/uuid" ) func TestIdEquals(t *testing.T) { id1 := NewID(uuid.New()) id2 := NewID(id1.UUID()) if !id1.Equals(id2) { t.Error("expected id1 to equal id2, but actually not") } if id1.String() != id2.String() { t.Error(id1.String(), " != ", id2.String()) } } ================================================ FILE: common/protocol/payload.go ================================================ package protocol type TransferType byte const ( TransferTypeStream TransferType = 0 TransferTypePacket TransferType = 1 ) type AddressType byte const ( AddressTypeIPv4 AddressType = 1 AddressTypeDomain AddressType = 2 AddressTypeIPv6 AddressType = 3 ) ================================================ FILE: common/protocol/protocol.go ================================================ package protocol import ( "errors" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen var ErrProtoNeedMoreData = errors.New("protocol matches, but need more data to complete sniffing") ================================================ FILE: common/protocol/quic/cipher_suite.go ================================================ package quic import ( "crypto" "crypto/cipher" _ "crypto/tls" _ "unsafe" ) // copied from github.com/quic-go/quic-go/internal/qtls/cipher_suite_go121.go type cipherSuiteTLS13 struct { ID uint16 KeyLen int AEAD func(key, fixedNonce []byte) cipher.AEAD Hash crypto.Hash } // github.com/quic-go/quic-go/internal/handshake/cipher_suite.go describes these cipher suite implementations are copied from the standard library crypto/tls package. // So we can user go:linkname to implement the same feature. //go:linkname aeadAESGCMTLS13 crypto/tls.aeadAESGCMTLS13 func aeadAESGCMTLS13(key, nonceMask []byte) cipher.AEAD ================================================ FILE: common/protocol/quic/sniff.go ================================================ package quic import ( "crypto" "crypto/aes" "crypto/tls" "encoding/binary" "io" "github.com/quic-go/quic-go/quicvarint" "golang.org/x/crypto/hkdf" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/protocol" ptls "github.com/v2fly/v2ray-core/v5/common/protocol/tls" ) type SniffHeader struct { domain string } func (s SniffHeader) Protocol() string { return "quic" } func (s SniffHeader) Domain() string { return s.domain } const ( versionDraft29 uint32 = 0xff00001d version1 uint32 = 0x1 ) var ( quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99} quicSalt = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} initialSuite = &cipherSuiteTLS13{ ID: tls.TLS_AES_128_GCM_SHA256, KeyLen: 16, AEAD: aeadAESGCMTLS13, Hash: crypto.SHA256, } errNotQuic = errors.New("not quic") errNotQuicInitial = errors.New("not initial packet") ) func SniffQUIC(b []byte) (*SniffHeader, error) { if len(b) == 0 { return nil, common.ErrNoClue } // Crypto data separated across packets cryptoLen := 0 cryptoDataBuf := buf.NewWithSize(32767) defer cryptoDataBuf.Release() cache := buf.New() defer cache.Release() // Parse QUIC packets for len(b) > 0 { buffer := buf.FromBytes(b) typeByte, err := buffer.ReadByte() if err != nil { return nil, errNotQuic } isLongHeader := typeByte&0x80 > 0 if !isLongHeader || typeByte&0x40 == 0 { return nil, errNotQuicInitial } vb, err := buffer.ReadBytes(4) if err != nil { return nil, errNotQuic } versionNumber := binary.BigEndian.Uint32(vb) if versionNumber != 0 && typeByte&0x40 == 0 { return nil, errNotQuic } else if versionNumber != versionDraft29 && versionNumber != version1 { return nil, errNotQuic } packetType := (typeByte & 0x30) >> 4 isQuicInitial := packetType == 0x0 var destConnID []byte if l, err := buffer.ReadByte(); err != nil { return nil, errNotQuic } else if destConnID, err = buffer.ReadBytes(int32(l)); err != nil { return nil, errNotQuic } if l, err := buffer.ReadByte(); err != nil { return nil, errNotQuic } else if common.Error2(buffer.ReadBytes(int32(l))) != nil { return nil, errNotQuic } if isQuicInitial { // Only initial packets have token, see https://datatracker.ietf.org/doc/html/rfc9000#section-17.2.2 tokenLen, err := quicvarint.Read(buffer) if err != nil || tokenLen > uint64(len(b)) { return nil, errNotQuic } if _, err = buffer.ReadBytes(int32(tokenLen)); err != nil { return nil, errNotQuic } } packetLen, err := quicvarint.Read(buffer) if err != nil { return nil, errNotQuic } // packet is impossible to shorter than this if packetLen < 4 { return nil, errNotQuic } hdrLen := len(b) - int(buffer.Len()) if len(b) < hdrLen+int(packetLen) { return nil, common.ErrNoClue // Not enough data to read as a QUIC packet. QUIC is UDP-based, so this is unlikely to happen. } restPayload := b[hdrLen+int(packetLen):] if !isQuicInitial { // Skip this packet if it's not initial packet b = restPayload continue } origPNBytes := make([]byte, 4) copy(origPNBytes, b[hdrLen:hdrLen+4]) var salt []byte if versionNumber == version1 { salt = quicSalt } else { salt = quicSaltOld } initialSecret := hkdf.Extract(crypto.SHA256.New, destConnID, salt) secret := hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size()) hpKey := hkdfExpandLabel(initialSuite.Hash, secret, []byte{}, "quic hp", initialSuite.KeyLen) block, err := aes.NewCipher(hpKey) if err != nil { return nil, err } cache.Clear() mask := cache.Extend(int32(block.BlockSize())) block.Encrypt(mask, b[hdrLen+4:hdrLen+4+16]) b[0] ^= mask[0] & 0xf for i := range b[hdrLen : hdrLen+4] { b[hdrLen+i] ^= mask[i+1] } packetNumberLength := b[0]&0x3 + 1 if packetNumberLength != 1 { return nil, errNotQuicInitial } var packetNumber uint32 { n, err := buffer.ReadByte() if err != nil { return nil, err } packetNumber = uint32(n) } extHdrLen := hdrLen + int(packetNumberLength) copy(b[extHdrLen:hdrLen+4], origPNBytes[packetNumberLength:]) data := b[extHdrLen : int(packetLen)+hdrLen] key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16) iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12) cipher := aeadAESGCMTLS13(key, iv) nonce := cache.Extend(int32(cipher.NonceSize())) binary.BigEndian.PutUint64(nonce[len(nonce)-8:], uint64(packetNumber)) decrypted, err := cipher.Open(b[extHdrLen:extHdrLen], nonce, data, b[:extHdrLen]) if err != nil { return nil, err } buffer = buf.FromBytes(decrypted) for i := 0; !buffer.IsEmpty(); i++ { frameType := byte(0x0) // Default to PADDING frame for frameType == 0x0 && !buffer.IsEmpty() { frameType, _ = buffer.ReadByte() } switch frameType { case 0x00: // PADDING frame case 0x01: // PING frame case 0x02, 0x03: // ACK frame if _, err = quicvarint.Read(buffer); err != nil { // Field: Largest Acknowledged return nil, io.ErrUnexpectedEOF } if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Delay return nil, io.ErrUnexpectedEOF } ackRangeCount, err := quicvarint.Read(buffer) // Field: ACK Range Count if err != nil { return nil, io.ErrUnexpectedEOF } if _, err = quicvarint.Read(buffer); err != nil { // Field: First ACK Range return nil, io.ErrUnexpectedEOF } for i := 0; i < int(ackRangeCount); i++ { // Field: ACK Range if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> Gap return nil, io.ErrUnexpectedEOF } if _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> ACK Range Length return nil, io.ErrUnexpectedEOF } } if frameType == 0x03 { if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT0 Count return nil, io.ErrUnexpectedEOF } if _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT1 Count return nil, io.ErrUnexpectedEOF } if _, err = quicvarint.Read(buffer); err != nil { //nolint:misspell // Field: ECN Counts -> ECT-CE Count return nil, io.ErrUnexpectedEOF } } case 0x06: // CRYPTO frame, we will use this frame offset, err := quicvarint.Read(buffer) // Field: Offset if err != nil { return nil, io.ErrUnexpectedEOF } length, err := quicvarint.Read(buffer) // Field: Length if err != nil || length > uint64(buffer.Len()) { return nil, io.ErrUnexpectedEOF } if cryptoLen < int(offset+length) { cryptoLen = int(offset + length) if cryptoDataBuf.Cap() < int32(cryptoLen) { return nil, io.ErrShortBuffer } if cryptoDataBuf.Len() != int32(cryptoLen) { cryptoDataBuf.Extend(int32(cryptoLen) - cryptoDataBuf.Len()) } } if _, err := buffer.Read(cryptoDataBuf.BytesRange(int32(offset), int32(offset+length))); err != nil { // Field: Crypto Data return nil, io.ErrUnexpectedEOF } case 0x1c: // CONNECTION_CLOSE frame, only 0x1c is permitted in initial packet if _, err = quicvarint.Read(buffer); err != nil { // Field: Error Code return nil, io.ErrUnexpectedEOF } if _, err = quicvarint.Read(buffer); err != nil { // Field: Frame Type return nil, io.ErrUnexpectedEOF } length, err := quicvarint.Read(buffer) // Field: Reason Phrase Length if err != nil { return nil, io.ErrUnexpectedEOF } if _, err := buffer.ReadBytes(int32(length)); err != nil { // Field: Reason Phrase return nil, io.ErrUnexpectedEOF } default: // Only above frame types are permitted in initial packet. // See https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2.2-8 return nil, errNotQuicInitial } } tlsHdr := &ptls.SniffHeader{} err = ptls.ReadClientHello(cryptoDataBuf.BytesRange(0, int32(cryptoLen)), tlsHdr) if err != nil { // The crypto data may have not been fully recovered in current packets, // So we continue to sniff rest packets. b = restPayload continue } return &SniffHeader{domain: tlsHdr.Domain()}, nil } // All payload is parsed as valid QUIC packets, but we need more packets for crypto data to read client hello. return nil, protocol.ErrProtoNeedMoreData } func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte { b := make([]byte, 3, 3+6+len(label)+1+len(context)) binary.BigEndian.PutUint16(b, uint16(length)) b[2] = uint8(6 + len(label)) b = append(b, []byte("tls13 ")...) b = append(b, []byte(label)...) b = b[:3+6+len(label)+1] b[3+6+len(label)] = uint8(len(context)) b = append(b, context...) out := make([]byte, length) n, err := hkdf.Expand(hash.New, secret, b).Read(out) if err != nil || n != length { panic("quic: HKDF-Expand-Label invocation failed unexpectedly") } return out } ================================================ FILE: common/protocol/quic/sniff_test.go ================================================ package quic_test import ( "encoding/hex" "errors" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/protocol/quic" ) func TestSniffQUIC(t *testing.T) { pkt, err := hex.DecodeString("cd0000000108f1fb7bcc78aa5e7203a8f86400421531fe825b19541876db6c55c38890cd73149d267a084afee6087304095417a3033df6a81bbb71d8512e7a3e16df1e277cae5df3182cb214b8fe982ba3fdffbaa9ffec474547d55945f0fddbeadfb0b5243890b2fa3da45169e2bd34ec04b2e29382f48d612b28432a559757504d158e9e505407a77dd34f4b60b8d3b555ee85aacd6648686802f4de25e7216b19e54c5f78e8a5963380c742d861306db4c16e4f7fc94957aa50b9578a0b61f1e406b2ad5f0cd3cd271c4d99476409797b0c3cb3efec256118912d4b7e4fd79d9cb9016b6e5eaa4f5e57b637b217755daf8968a4092bed0ed5413f5d04904b3a61e4064f9211b2629e5b52a89c7b19f37a713e41e27743ea6dfa736dfa1bb0a4b2bc8c8dc632c6ce963493a20c550e6fdb2475213665e9a85cfc394da9cec0cf41f0c8abed3fc83be5245b2b5aa5e825d29349f721d30774ef5bf965b540f3d8d98febe20956b1fc8fa047e10e7d2f921c9c6622389e02322e80621a1cf5264e245b7276966eb02932584e3f7038bd36aa908766ad3fb98344025dec18670d6db43a1c5daac00937fce7b7c7d61ff4e6efd01a2bdee0ee183108b926393df4f3d74bbcbb015f240e7e346b7d01c41111a401225ce3b095ab4623a5836169bf9599eeca79d1d2e9b2202b5960a09211e978058d6fc0484eff3e91ce4649a5e3ba15b906d334cf66e28d9ff575406e1ae1ac2febafd72870b6f5d58fc5fb949cb1f40feb7c1d9ce5e71b") common.Must(err) quicHdr, err := quic.SniffQUIC(pkt) if err != nil || quicHdr.Domain() != "www.google.com" { t.Error("failed") } } func TestSniffQUICComplex(t *testing.T) { tests := []struct { name string hexData string domain string wantErr bool needsMoreData bool }{ { name: "EmptyPacket", hexData: "0000000000000000000000000000000000000000000000000000000000000000", domain: "", wantErr: true, needsMoreData: false, }, { name: "NTP Packet Client", hexData: "23000000000000000000000000000000000000000000000000000000000000000000000000000000acb84a797d4044c9", domain: "", wantErr: true, needsMoreData: false, }, { name: "NTP Packet Server", hexData: "240106ec000000000000000e47505373ea4dcaef2f4b4c31acb84a797d4044c9eb58b8693dd70c27eb58b8693dd7dde2", domain: "", wantErr: true, needsMoreData: false, }, { name: "DNS Packet Client", hexData: "4500004a8e2d40003f1146392a2a2d03080808081eea00350036a8175ad4010000010000000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000010001", domain: "", wantErr: true, needsMoreData: false, }, { name: "DNS Packet Client", hexData: "4500004a667a40003f116dec2a2a2d030808080866980035003605d9b524010000010000000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000410001", domain: "", wantErr: true, needsMoreData: false, }, { name: "DNS Packet Server", hexData: "b524818000010006000100000675706461746504636f64650c76697375616c73747564696f03636f6d0000410001c00c00050001000000ec00301e7673636f64652d7570646174652d67366763623667676474686b63746439037a303107617a7572656664036e657400c03a000500010000000b002311737461722d617a75726566642d70726f640e747261666669636d616e61676572c065c076000500010000003c002c0473686564086475616c2d6c6f770b732d706172742d3030313706742d3030303908742d6d7365646765c065c0a5000500010000006c001411617a75726566642d742d66622d70726f64c088c0dd000500010000003c0026046475616c0b732d706172742d3030313706742d303030390b66622d742d6d7365646765c065c0fd00050001000000300002c102c1150006000100000030002d036e7331c115066d736e687374096d6963726f736f6674c0257848b78d00000708000003840024ea000000003c", domain: "", wantErr: true, needsMoreData: false, }, { name: "DNS Packet Server", hexData: "5ad4818000010007000000000675706461746504636f64650c76697375616c73747564696f03636f6d0000010001c00c000500010000008400301e7673636f64652d7570646174652d67366763623667676474686b63746439037a303107617a7572656664036e657400c03a000500010000001e002311737461722d617a75726566642d70726f640e747261666669636d616e61676572c065c076000500010000003c002c0473686564086475616c2d6c6f770b732d706172742d3030313706742d3030303908742d6d7365646765c065c0a50005000100000010001411617a75726566642d742d66622d70726f64c088c0dd000500010000003c0026046475616c0b732d706172742d3030313706742d303030390b66622d742d6d7365646765c065c0fd00050001000000100002c102c102000100010000001000040d6bfd2d", domain: "", wantErr: true, needsMoreData: false, }, { name: "QUIC, NonHandshake Packet", hexData: "548439ba3a0cffd27dabe08ebf9e603dd4801781e133b1a0276d29a047c3b8856adcced0067c4b11a08985bf93c05863305bd4b43ee9168cd5fdae0c392ff74ae06ce13e8d97dabec81ee927a844fa840f781edf9deb22f3162bf77009b3f5800c5e45539ac104368e7df8ba", domain: "", wantErr: true, needsMoreData: false, }, { name: "QUIC, NonHandshake Packet", hexData: "53f4144825dab3ba251b83d0089e910210bec1a6507cca92ad9ff539cc21f6c75e3551ca44003d9a", domain: "", wantErr: true, needsMoreData: false, }, { name: "QUIC, NonHandshake Packet", hexData: "528dc5524c03e7517949422cc3f6ffbfff74b2ec30a87654a71a", domain: "", wantErr: true, needsMoreData: false, }, { name: "QUIC Chromebook Handshake[1]; packet 1", hexData: "cb00000001088ca3be26059ca269000044d088950f316207d551c91c88d791557c440a19184322536d2c900034358c1b3964f2d2935337b8d044d35bf62b4eea9ceaac64121aa634c7cd28630722d169fa0f215b940d47d7996ca56f0d463dbf97a4a1b5818c5297a26fe58f5553dfb513ad589750a61682f229996555c7121c8bf48b06b68ab06427b01af485d832f9894099a20d3baadcff7b1cf07e2c059d3e7ba88d4ad35ef0ffea1fdc6ac3db271dfcca892a41ab25284936225c9bc593ce242b11b8abed4a8df902987eef0c6d90669e3606f47dd6ad05f44ba3a0cd356854261bbb1e2d8f6b83cc57cfa57eda3e5d7181b6ec418f6eeca81c259a33e4b0a913de720f2f8782764766ac9602a7f52a1082ec3da30dbefcf38c781a3e033810c4f2babf9b72adf7164159d98142181492e4468c0e10ab29013bf238e7360e09767ca49d59a9eb18f06a372bad711fefa90295f8e0839b1080570648212b321e5bd6f614bf0d3dc2817628b0c052a32820c16cb7f531c49244c48eb1429625246f9c164ae4ee1e83eaa8ff0eef1acf5a3d8ca88f1e4597db5ba5c0cb23d6100dd53da4f439ae64c4d3d43d1fbb5677f4fdc3bd2c2948dfc7e0be1a33c842033da15529cfd3cae00da68343d835db867f746854804410ba68f0dd7711b0fe55817b83f6ce1a12ad38acf2a3156f819f0dc68ea799c05583d9728f2856577811b260dba40d6c5e82c9e558c5b8f3f4599caf05ea591118e0b80ad621e0a76e4926047593a896752cb168420cb1b02d4211de5e5b7c891f319b5c0cf687e1d261a01f2acbade6bd73cd1ade0a02e240e9351384e1a6868c21a4878f39f0fa94ee1e36c5a46449241a3fe0147ff50176787eca7f3a936c901aeef56770bff74feecb985e6670d20dfd8ed17952dca5a5292213345c61db09bb5bcf5bf74565f61f9dccab51a289c3160ffe4a9b29cc76ea46778d9317a890efea2ad905f4219463a3baca3c02f5c3682634be7c2e86e366272a8263fec8e871644a79299d4aa74f1b1414b2f963cce6e059978faf813625af7869c1dec92035478c0e46dc66d938d4131aca27a59b2103b8cefa8e08aeb44b53b205b932902aea8d519faaaa12e354a6f532b4f716d7929e655dc2e98b494a99153854af5732a2659f2c21e4069896a1835ad05c5e53781cab16599cf4af47c196deeff9115c80d13f93aeb28b08023e6a1d3cf7da2a4457a9e443176bcdfef8f8de630c02bd0efdc5ddda56ad8f6b47edbda6353205e6e655f690092a48deb7f8a5254a7d778e07216cd97dfefcf740c1acd2977ef0fa17f798ea9752bae46e3aa3ec9b13f4c95c20a7839b8409000fa1f17e8dc46cc05c41bff696ee03c0371cae8638e8018ff4ebedd9f27d56443e534a72dd3d18a64790b676ddd060376759fa4a12ffc17f4be83492126ec1dc0fcd4aefef73a0b9c443ec3532b9a66b1a60daacf45e6557115edc0cc4d08758754a44beffedaa0d1265e50beed1a01752904ee3f7e706ed290b1a79071b142105b7c02e692ff318710e3ce9c3b9ec557cdecef173796417341ada414faa06b52adf645db454b56468ccf0da50a942ebc09487797cb45a085ec1e2e06fcd1f5b72eac291955a62e5aa379a374aea3a0dec3e4e0ba1dde350a94c72dbea7505922e26e99d62f751c2b301413a73fb6b20a36052151473ebecd04d0a771ec326957bc28c2020fdf6f01d9abed69b3c3e73168b404a1748b15310b167396da01c7d", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[1]; packet 1 - 2", hexData: "cb00000001088ca3be26059ca269000044d088950f316207d551c91c88d791557c440a19184322536d2c900034358c1b3964f2d2935337b8d044d35bf62b4eea9ceaac64121aa634c7cd28630722d169fa0f215b940d47d7996ca56f0d463dbf97a4a1b5818c5297a26fe58f5553dfb513ad589750a61682f229996555c7121c8bf48b06b68ab06427b01af485d832f9894099a20d3baadcff7b1cf07e2c059d3e7ba88d4ad35ef0ffea1fdc6ac3db271dfcca892a41ab25284936225c9bc593ce242b11b8abed4a8df902987eef0c6d90669e3606f47dd6ad05f44ba3a0cd356854261bbb1e2d8f6b83cc57cfa57eda3e5d7181b6ec418f6eeca81c259a33e4b0a913de720f2f8782764766ac9602a7f52a1082ec3da30dbefcf38c781a3e033810c4f2babf9b72adf7164159d98142181492e4468c0e10ab29013bf238e7360e09767ca49d59a9eb18f06a372bad711fefa90295f8e0839b1080570648212b321e5bd6f614bf0d3dc2817628b0c052a32820c16cb7f531c49244c48eb1429625246f9c164ae4ee1e83eaa8ff0eef1acf5a3d8ca88f1e4597db5ba5c0cb23d6100dd53da4f439ae64c4d3d43d1fbb5677f4fdc3bd2c2948dfc7e0be1a33c842033da15529cfd3cae00da68343d835db867f746854804410ba68f0dd7711b0fe55817b83f6ce1a12ad38acf2a3156f819f0dc68ea799c05583d9728f2856577811b260dba40d6c5e82c9e558c5b8f3f4599caf05ea591118e0b80ad621e0a76e4926047593a896752cb168420cb1b02d4211de5e5b7c891f319b5c0cf687e1d261a01f2acbade6bd73cd1ade0a02e240e9351384e1a6868c21a4878f39f0fa94ee1e36c5a46449241a3fe0147ff50176787eca7f3a936c901aeef56770bff74feecb985e6670d20dfd8ed17952dca5a5292213345c61db09bb5bcf5bf74565f61f9dccab51a289c3160ffe4a9b29cc76ea46778d9317a890efea2ad905f4219463a3baca3c02f5c3682634be7c2e86e366272a8263fec8e871644a79299d4aa74f1b1414b2f963cce6e059978faf813625af7869c1dec92035478c0e46dc66d938d4131aca27a59b2103b8cefa8e08aeb44b53b205b932902aea8d519faaaa12e354a6f532b4f716d7929e655dc2e98b494a99153854af5732a2659f2c21e4069896a1835ad05c5e53781cab16599cf4af47c196deeff9115c80d13f93aeb28b08023e6a1d3cf7da2a4457a9e443176bcdfef8f8de630c02bd0efdc5ddda56ad8f6b47edbda6353205e6e655f690092a48deb7f8a5254a7d778e07216cd97dfefcf740c1acd2977ef0fa17f798ea9752bae46e3aa3ec9b13f4c95c20a7839b8409000fa1f17e8dc46cc05c41bff696ee03c0371cae8638e8018ff4ebedd9f27d56443e534a72dd3d18a64790b676ddd060376759fa4a12ffc17f4be83492126ec1dc0fcd4aefef73a0b9c443ec3532b9a66b1a60daacf45e6557115edc0cc4d08758754a44beffedaa0d1265e50beed1a01752904ee3f7e706ed290b1a79071b142105b7c02e692ff318710e3ce9c3b9ec557cdecef173796417341ada414faa06b52adf645db454b56468ccf0da50a942ebc09487797cb45a085ec1e2e06fcd1f5b72eac291955a62e5aa379a374aea3a0dec3e4e0ba1dde350a94c72dbea7505922e26e99d62f751c2b301413a73fb6b20a36052151473ebecd04d0a771ec326957bc28c2020fdf6f01d9abed69b3c3e73168b404a1748b15310b167396da01c7dc700000001088ca3be26059ca269000044d00a7e7a252620d0fdfb63c0c193d6a9fe6a36aa9ce1b29dfa5f11f2567850b88384a2cc682eca2e292749365b833e5f7540019cd4f3143ed078aec07990b0d6ece18310403e73e1fe2975a8f9cb05796fa6196faaba3ee12a22b63a28a624cf4f7bedd44de000dc5ea698c65664df995b7d5fade0aab1cf0ecc5afd5ecb8fb80deecae3a8c97c20171f00ac3b5dc9a9027ca9c25571c72bb32070f6e3fb583560b0da6041b72e0a9601b8ad17d3c45e9dcc059f9f4758e8c35a839a9f6f4c501cb64e32e886fc733bc51069fbe4406f04d908285974c387d5b3e5f0f674941d05993bf8bda0d5ffd8c4fb528e150ff4bf37e38bd9c6346816fe360d4a206da81e815c1f7905184b6146b33427c6e38f1179981c18b82a3544442dd997c182d956037ae8f106eaf67ba133e7f15f1550b257d431f01ba0472659c6a5c2e6ff5e4ce9e692f4ef9fb169a75df4eb13f0b20e1994f3f8687bdca300c7e749af7b7a3b6597a6b950fe378a68c77766fdabe95248ed41d37805756b7ffa9cee0898bd661f6657cbf1af9aa8c7e437d432ca854c95307e6a7dfb6504ee3f7852fb3c246d168a03810b6c3d4e3d40bdee3def579effb66563f5bac98cfa1b071cd6f33e425e016bb3514a183b72cb3a393e9e519ba60e2177c98f530835e3b6eab78cdcb8abdbc769bc07e10c8e38bea710d5de1bdb2fa8d0d9b19e8cc31d16725a696e55342c89b667497e3d7f90e48f8503d8ead2a32a1930c3b24a4a9dcf2d8ec781705dd97d7df6e26828712fe42114419d5b8346bd86c239bd02f34e55f71400cb10c1fac7d8efa1a2ab258c17ace4288c8576ab92447b648fd15f4e038ec1c81a135e3bbb6f581a994c6a4902aeb1b5588cb1b5b53c8540296d96b6d2eccd67bae9609233f36304b5186d4698b88bb3ce8b1191a62b990436cf10718fd5759cb2281ac122f49ccbef8a3206348c1a930e7fc4bb498a11d89374e1480c7b8725b5f65e8c8d6f58da17f9134abce77eb9a6fcda514e7d3ab2e3610f86945f0dca519a3844da1b3a4b0e03c80528a2f79be478d07ff26166e30294bf0e69bf07a5bbd6d879adf6d618a1ec8365023408980bf67f0525a2fdee97fccc38fe104d4f58ed15e3671dfedf684856a27fbe286adba40ff0336def93f0174e9e35d341f5de73190d330d72227db9a866b69418e17e8e19ec884c1ffe2f0ad6deec37c9d49d536d0242fab282b0cf86cc9b15341757e0d361bddcbe5cbb062b3148d7c3c62af5c5dd5922a49920f351647030f62ed16929a404aa514fcbc38e67ba4f275e02a04c486b1a8e5b5efda197fd63e6f41fdeffa652c690dd6b00ca65df3688672ead9744f7d631e42e3b42f3ed1bff51b30f89211a7467cde65eab3659af7690cf307420a5823f31999d8f63c6c6ba0296ed4a46d5df6404f8db33e7252cc6bfcf7f55fee1f1e3b0573b6c6615793ff0691b7cfd23c195f66eb333d7efb0cfb74cf159787f87ad01fc131c6763bb1117bbfb8c2e8197ffba6b8c747565b1332bdbd6553b840939c2f98aa8eb1c549491c640e012fc549852fa7a93f81e5db152c761fc7d01bce0325619965c09f6730a162e7be53af7d9ce4b5ac0f4eb487361d2ac231d4ce92e5d9a084bc7b609ccf60056ecc82cd0c06a088cfbcf7d764b3109331c42f989da82b05cfe4c134a6784e664fa67a89c0624e3cc73ccfdea3f292db28f7c7b1b109f680f6b537f135c62f764", domain: "dns.google", wantErr: false, needsMoreData: false, }, { name: "QUIC Chromebook Handshake[2]; packet 1", hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaa", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[2]; packet 1-2", hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaac40000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e4489522d29bb5c84749f83c8e1edfd9da8d1738164a8a9c59e37a5c9994d90bb982dcfa69b20f868960dc139618f1adc2546d34340ae13d826260c54a456bbf7469ee37b1be1d7177004468d7e92cac62a0b165d6a114ad479861dd58959e094b5a6250359301d4a614d529660760e3d1cdec9bf444a3761309bab40e4a977bc749e0dae431952f5f7e6b1ebc1383d343359a387da4301f7fa4b400475e9b82367e56278376dd1c80349f083988945a13649008109cc12a3acf569ffcc5481fbcd86b544e7dc8434e9dd42bd8e5716a844d37879568db046857389d36cc7550c75f94e314db6749aa987f0fc730fae0fcf465d01c2fb745269dfc10132ddb5404dda2f9455780f5818730834aa9db4740793359884b9927b0bd1a5ca96052b4f17397d8b78aa891401bb8bed6726ea2229d919798c50e24d5f40576ac204847be9244aadbe5c773684c37475036541d209c177d4e9c22a1253292ce4ffb886b925b6cf83cc251976a68887eda2777590f51804b790b51eee77e717b7ef0eea71634594df36e6ae9e7574d65c51ac3196f0b2a3b0f023c81f05f7807f958dda03418ed49e14b645e814b9aa55b37c809be3172ca21fe4c7a78e17e9ece8def2dd2949310ecaa41b1b477f4e85db5288aa144e333f47ef291d0e822941181c13859d9fd6d640904ee764c9276125228c932dff3fb12f564f039b52f5ba1ab4d119641df8fe13f784802b99347f0046da63f471e34b1d12d3111cffe7b5d90cf5999879f6f23e7785f09cb10df32821bb68dd8fdcfcdbedd63f2428b2292b9f0e76ff36403c9644fd43e01112ee6218d0ec1c86f6d147e4b802293e906750c7046f53bf05a144e321d3b45e08e4064fd3828fdd1b5d1ceed74081f61319dc0ad9a6e8a3b9cc802e952d24e2271712e2c2cda7daca2f835e6c804feeef8d918404cc82a1aa9534bddff68a472b208a0d0a7fd68a08fbc411132af47a6b67a32617b7b9991524c21599e8e3cb9395cdab87a3f5bf5d1833a9c7ea021b29cf428c877c6b21d62f99340ac7f85ae721acc10968e7d79f111ca40c75e14060d07cffa046d71151a0b00eab657300344b04bd1a8871650c34ceda8610d7c1ba8d37673da6aaa580400e0230c69fba8ba21927de2f5897656144694550d1df3d268804adc707e7b236501734aeabb2e61cb08012bd96eca5a486d7a55f996992c36233815abd71c30e263ba0c5d9456fe0828df16f6af7929390bb143c426d9dfeaf4bb373554479ebe609b36b4bc3dd08ce216b9cdc5726edb458c5e4036d0edc688d3e39d20f8254b5d1f174518f15b344efc27fc56572c0159aa593d5b46bc33818f986e3df8caebd4c7b702ef50dd582714a2b94ecd1c4e90af37d388445c478a32ff6e8f5852ee115966b708eed04da322b98813a69423e95f90b89ce85518e39bcef36fdd5bd312b2c6c5ee85962675274c18f39ee35155517f70fd74b31bb2de6b5108d369252e6fb289e453833132ef7960da1cc0934790c039b9a1b0c74f23eb3b61fe9b4d0ea67de757b93af451eef303b1373199af446a0fa98d5991bbd4771ee63317e6da86efbe213dfff595c41b98e0e89f4f2df110104e760feebf4cb3361171c9fceb1e1c809a268", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[2]; packet 1-3", hexData: "cf0000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e448967af34752fd95835e3caba1e022d6d164f3f53f1bd7f60d560a8684079e90626aa1a4d3fe728158f7e1055ff76d1566072113982b193fb932265381e4de7afb35caa4ec56f31595a33fa2eb0bc84feb9f273224050938825fd21aa7317042ad00785ffd36151aee566a5dfe17d72591af1235059171568e5af0d13fc56e7897c3d632be753d8dea184c3d96d92bc56978cc669d94dd4c5e8dc3dcba7f0a39368fb1e87981e54bba7b86fbd8e8023a94d84f0290f402a5244cb4b0eeaaa57610ea59711a43932c521f10edb4560375693cbea60240389b8cebfd94035cabe4fc96ce8a726b979775e06c3bb0e3c4c866fe82e89fb725499e711e39310b93c785b313459f22d4ba37f90b19447165c2584269d98bf47d1f7ca89585797e4d6f1a4a1db7d2b0ae91a93fb15c3bb0ab953c3656b3b2ca20833d15e95329baff6d2ade1b0921b5ed3ae96648bf123b5265e27b049e9a8674455ff5f763f039568026e4fbe9882fef761c573d8f12e342c274a8dd3ad9854a688ce57cdddb52c758161ae3a59f67fc0d5b85f12e27617e7f4366e97a61fcda084e620dde35686f01dac49ce4bd76b986e3223c215919a1b228beeb74b7fcf32827d55be8f1b3b5fed24df2db023faecbb313b18a151cc4af8199d4bb08f8127b8207a0286d52758eaca87fd476ece0e3b17bcd8afb0289e8fd33c4455d4db6f058826c301ea303bfe2c0a6651a8fb6a2e1897852d758076adb04ad907077c5d5f94089da78d8923a34f1022ed672f378fe0dd81a709b372c0a2042a42e683c051c653e42b43c4a0ea8e961074d2901d4157ac9878b13a207b05ec471cff10d922b74d05623513cd6a4ea192ad21d4089de269633d4d2d1388d98d7c8a9e29848d5558b8aa2b73b437446a640230e6adb7f4b317ee5d66681c4aae11f69b1e5f96cb32ca6331405426cb706167d86f6f8fd588a72d7b2a6906798b81f174d808e1e3fc461e598e797c41bced26b87d09282d7b6d95076c285462e0c420a6f0e171ffe2791b5d221c03520409fe36622ff77796d9b7ef82babb25313acda9c621b22bf45ed909f9365b508860645af4c3aca78e6abca2d3a65c9159fbcd577438505d3f65a57c9412c12c069ad4d6db450beb08603abef621a9e029593fb5881dbd524ea2953b4acaaf59269b584c754e88c033247bb7c032e548d34fd9b2678e62fdf953dabf2be21c3e2d7b18ec7e3aedaf2cd082e19a369c1bcd4ca67e3d464e2200ecc3df98b0aa7f349415d68bcab0441ac3366607eff024bb786aec031a4619f8a24f554fe93c8520a03affcf11e40b6d5002f98c1708cac6c56e77eccba85ea6600d1391cfd202cc7914bfbaa3303266d1a820bf2dc84d2dfcdc4cdb79e6de3fbe3c02b288dcf955652f674f3f59b50849ea7dbf755bdafa27fba3db1267fb1354d8bf25a60cacb900b4d7ba913f9ba5f6b00559ad58b2f34a658ff7ef7f7d1ceeffd9c8325f271e6b5ba44d89685b744306963aa5e05ac0e8b00ada772dd5ae5ffb7043109afea86593743564c7acb4c8e7ef0e57d081eb1b9c0916078b113ece8a6036264a9b9781183c035342d50c7b069f3a01a40230e37ed8efde073c07d0e68066541d78c2f3cbe1e603cfcaaac40000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e4489522d29bb5c84749f83c8e1edfd9da8d1738164a8a9c59e37a5c9994d90bb982dcfa69b20f868960dc139618f1adc2546d34340ae13d826260c54a456bbf7469ee37b1be1d7177004468d7e92cac62a0b165d6a114ad479861dd58959e094b5a6250359301d4a614d529660760e3d1cdec9bf444a3761309bab40e4a977bc749e0dae431952f5f7e6b1ebc1383d343359a387da4301f7fa4b400475e9b82367e56278376dd1c80349f083988945a13649008109cc12a3acf569ffcc5481fbcd86b544e7dc8434e9dd42bd8e5716a844d37879568db046857389d36cc7550c75f94e314db6749aa987f0fc730fae0fcf465d01c2fb745269dfc10132ddb5404dda2f9455780f5818730834aa9db4740793359884b9927b0bd1a5ca96052b4f17397d8b78aa891401bb8bed6726ea2229d919798c50e24d5f40576ac204847be9244aadbe5c773684c37475036541d209c177d4e9c22a1253292ce4ffb886b925b6cf83cc251976a68887eda2777590f51804b790b51eee77e717b7ef0eea71634594df36e6ae9e7574d65c51ac3196f0b2a3b0f023c81f05f7807f958dda03418ed49e14b645e814b9aa55b37c809be3172ca21fe4c7a78e17e9ece8def2dd2949310ecaa41b1b477f4e85db5288aa144e333f47ef291d0e822941181c13859d9fd6d640904ee764c9276125228c932dff3fb12f564f039b52f5ba1ab4d119641df8fe13f784802b99347f0046da63f471e34b1d12d3111cffe7b5d90cf5999879f6f23e7785f09cb10df32821bb68dd8fdcfcdbedd63f2428b2292b9f0e76ff36403c9644fd43e01112ee6218d0ec1c86f6d147e4b802293e906750c7046f53bf05a144e321d3b45e08e4064fd3828fdd1b5d1ceed74081f61319dc0ad9a6e8a3b9cc802e952d24e2271712e2c2cda7daca2f835e6c804feeef8d918404cc82a1aa9534bddff68a472b208a0d0a7fd68a08fbc411132af47a6b67a32617b7b9991524c21599e8e3cb9395cdab87a3f5bf5d1833a9c7ea021b29cf428c877c6b21d62f99340ac7f85ae721acc10968e7d79f111ca40c75e14060d07cffa046d71151a0b00eab657300344b04bd1a8871650c34ceda8610d7c1ba8d37673da6aaa580400e0230c69fba8ba21927de2f5897656144694550d1df3d268804adc707e7b236501734aeabb2e61cb08012bd96eca5a486d7a55f996992c36233815abd71c30e263ba0c5d9456fe0828df16f6af7929390bb143c426d9dfeaf4bb373554479ebe609b36b4bc3dd08ce216b9cdc5726edb458c5e4036d0edc688d3e39d20f8254b5d1f174518f15b344efc27fc56572c0159aa593d5b46bc33818f986e3df8caebd4c7b702ef50dd582714a2b94ecd1c4e90af37d388445c478a32ff6e8f5852ee115966b708eed04da322b98813a69423e95f90b89ce85518e39bcef36fdd5bd312b2c6c5ee85962675274c18f39ee35155517f70fd74b31bb2de6b5108d369252e6fb289e453833132ef7960da1cc0934790c039b9a1b0c74f23eb3b61fe9b4d0ea67de757b93af451eef303b1373199af446a0fa98d5991bbd4771ee63317e6da86efbe213dfff595c41b98e0e89f4f2df110104e760feebf4cb3361171c9fceb1e1c809a268c60000000108452af27900a1723300404600ca8530029a6bc59ff3e85beb5fc838ac3147ba5c2f6421ddcffdd85167d8de70eff2b1fa016dad4918337dec0feb660edb98e078dea51fa914055984b7957bd732fa4c831e44892ff5e6b16d8a259a9128c2c0c3c525462781a344c3df7f19a747e0e79ca8714995c867fc697a3cb87b35e769465a8e966bcb35b7e897ad036aa23a6c021e2445a0eb79962151cd20dbb43ae1231847de01caf4e5589dfebf026e95f7d1d742e140d9dda849396a70cc0798f1eef06fd5f4cfbc9a190ddf04cc332c5b7b15e53af311190ced92a1291c12b8799f2b50e076539a8370ee667e1791a78f38e565a48acbaa1c78ba941dba8b0d040f8fb8bbcc9f6bf5705efa613a24b12d6ac9cebb4f3fac1b09a07b49d8a3a62808eb0a324629f13a012e6ad0feb11ad97c1572983c713b62f27584809ba43e64e4af9845af807c0783104838f4e2ac33fa848866f3cc64a7b6203a5c09e8ad231f0f06ae2fb7b39a64cedd823b0ff297ad9be1ccac436777ccb3e22ef6b9c12e6d5e34926f50e8ca8c8c0532c810b074d001c11791a01bf25786b57a5da54065dcee4962822e929f47ee44d3b8c83d45a8b7a936dc2a6fa396e4194fa032d1627eca59f69857fc40dab5835d3613dade1c74b09c345bd32c509e9545d2330b157a7acb76409f3ac8eaa22802414f38c5422fe4c5189caaf5c1b93ce7c0892f0cfc477490d335aa78961d632a973cf106bd974c2714176fb0f98cf12f2887a0d7bd491756dd374331eb3e6adb9f2bd0d6b273403fd14b314eb27ebbb6f6e78ce310437004b757c048149cf04429ae4a6d6e65c9b3e0b9c9c4d4ef52007eaaad9670320f10cd5317b3d3edc374d45c98b217dd28fb3c2c2fb6e74a3aced143e3242084b192ba6df24e69fdb883e850714fe27a45f43883486a986574fd1fc10f259fe90786441554514c8dade1f3b86fdaf5f54ab655e2d803c98aa56073b00c32148a1ed367dff3a2bd934ecba55141389990b661bbd9ce1ef1def13747d45500daf92cec9e60908274703e761cd46affd46622f2a2192a79425ebf51c875fc7ba3598e15e0ba2465fc3e87c8a5da1915d3b8abe4b16d21259f311183eee1e7d2b808a91a7c89b284df0eb6a2a79c610bbe47722b3e04d5a6c0e574816a94d97349b6976010eb8c7debf42210982f78de482b7cd068051bf57908dbf46b5ceaf64f5fb33ede4412c1ce81eb1dfb4e99e10dd9b57ebc6e62ecbf4ee2db04d9e48c62bd45f8fa51704d414296a2d51d25ced6a192034a44c67e09d8985b573f98e03fa36dd8dcce2c04b4d5b1f276b6a642aadbdcafcf09de1d234bf8bbbf64aeadf01519ddafb419b3e62d204e04c3d7ebaf54b09e387ac3e9c4781c11625a2f44fddb7a1886f21929bd01c283f64903b6ccbb463984dfadc00f6af2a421517da023fd319f528195cac5fe907624b70c0172479d07d78e266dbf20ab8fc302228f279ffdba7395a839c4a9d7a4e001a260e1702393968f1e9722f023b204cf09cfee9a7bba045e4a2a449ee9fbb5c36e93028cfc87a2e34914b1b4f01beeded175ac0fa73fea9292f2bc3b1247164d8e05cddc3981bdc24e5f596571c418f6fa00fd9d4d0898cbf0d2f5413bed5f100f1854903017b6bc88bd7e303b5e0e2417bbcc984731128eda550d31f9af0e6e743eb6916466bbd435617d56fa60b05cd7dca66a9f6f4be23d3c5ff5d900822c6d1d8d71b0bab24f57d9682381a87c", domain: "signaler-pa.clients6.google.com", wantErr: false, needsMoreData: false, }, { name: "QUIC Chromebook Handshake[3]; packet 1", hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948f", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[3]; packet 1-2", hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948fc90000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd448983eee52163a177650f57b2cd8404bc619b9b59e796f9808bcd549ae6ae30d448c90f2783978bf9314a8038f45c0da5983163bd26f38f559f59447e8cf004f93b6b5c8af7b09603db021d4bdfa641bd83926eae1709a7a427add14df90cb258c6d4663d4d29709da89c90613d2ff9334637d53ca89407804eb863f78e110b866af2734c980705d9f969730a41132e788fc9e426d0f68ed24157aaff0383438d2715262e9b8b03cff850ba88127a05a8b68ac9a5ae5b098bb9ba5eaadd71ae846b3c0f68db728361eb8c8ed899c77725afbdfabf93812c49cbf4ee64047a96ea71258dbe5be3f988029d005fa2d9fc6e1e53fbeb6888074521b972e2ac71b4f22b754fc743e0de21af1e2ab416b2481e03227a1c7d7ea6cac5bc37ee3597d3bf11bf13a688dfa3d9aeff1eb1a7fdfcf8b6c722a4853f7c2b2d31e0b2b691f4273d4793fbb7a00f27a25577bfcba95e60699c9d2a926e71d64f535b633f2fd03320b28fe86c6619c54b34e6caf8f5a71b8a144c9236bf07edaacb486ff8ac63173af099efe7c9d006a5bb756449fb32b1fbff2e315fd5e96b586bb922a9795e29ffe6ead037c556e1bf30e24afb344cf873201007096b6f687f157588e236b71ade4d9245d8f065f2e23b36fad798d0f5504ddf25b828698d0cbdc28478b20d692d2ab605797a67232b0795927d886de798f00b4e7c69517d62b748e62e01d53dd1e77ac9a1605c0408713ff309ad53ff8f2bef17f9074f01134374068bf1f5dc07125180b5ea6902ec2d55c7d6d5f7ed4ef8732f9d34b4627678611fc9579e4321cea012c4e457dee6a11c41bdd1eb965056e885757af389079a558434eb3d59ae56a232302759431172ecc88de1c5400265f0f47e21396e3c38e0ba022c3e55ee4b85527cf49dece94445adc740cd26c18004a1cb984cc1732a138844da1ab003f89c589b6f3cc10c99a1b0d87be763f83e1b12c6fa6938ebc55d2ba33c25ca816dde207f7186f0c70b56b33feb538eb31175fdcfb036e365087f1b630628affdbbdee20d1976cb009f32db5f35aadb8117aa02ff2da9bdeaffbcc8bf3412efefeb00365e5f1ea577afd6e1c3585c67ffe1fa120382aa54028dcae9bbd624432a6256687d05483f2611f1ddd14b40f66fdf547e7eba904a79bd27733c9a8fbfb01154dda3457c4eacff8116941777ec570ff040e217d648ea5076588a6417462481eba68ebc59af04ba49b92f70b68a007977fde48b94b0af35475ea19cbec92df6449b065880bf03452cb3b3582f3d1a010e585be6506f3e067226471a94ce46c515f20502b3866553c10f037d9be89ad5858d6b2d2d94c70159247f66958d0e841d1c5b4254809d52475fdf96d087c3c6647b86006147a9ebb3f52ea6f4b89d886725b9e9243efd95e434bd8dd785143c57c06863b68df8f832987eb0c730c8b96634c1f888da2ef420cb0ebacf81f4b25c65962ae40c09ac4b0b2d440e3bdaa7309d87a1fa6af1c2e13e7a63c253fae027ceb2067cef8421b62d205f5d37c7204eaf594b1b43f9d9b67509a6709df48769ab9e1078f9e59d7656ec2132b5ebccf297e757a052835fffe94ae073131ac49c4f4374a1904cd4bf3041b236b73ea19eaa583db577fe35", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[3]; packet 1-3", hexData: "cb0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489d0a84868f32ecd4bd0d4c95a8a08d60d8303ef02323bdbcd96a33940824d25b9594bd8ae5716b9d043ab27ba03a2593c4d149b923711dd98e45456db19c8ea71e982562ae787c1b6cbe3ca1b03df2df62aa8e3127c5f68bdf80ed90588e7ec1f41f5a6281b87a12348cb17a04e9eaa461fef2e0e3ae70ebdc7bffa15c6b61ab270173e46dec0fd081f935a5fe6338d3b9cd38fcb52cb159edb66d2927238294313990da25c22f40d40e3cd72e76bbd066de731cf8fb6b4b7bc7639efb788c0b108dbf8280845a2cc62fbaf5fb8e1ecbe5ba7791aab94786c1c71c9058d0153b34a3f5bc8903e0d120f353defbe973cd33568bd03609dcdab8af1e8563897f5dd0251c6e6514bf40bd447d376fed21b2c54ebf74680df241bdc2ea5579bc0736cf3257c20d275746e8e6853aa89dcda8c2dbe523438ab92ca1ed1ab4f109e4ea84de57dfb6c544d695a5b710fe2d432f2b58644f8aeb965752d3a1d1a3057c2229192f89b254f5d292c22f1060642729df3667ef39e27691c82da9be847a59a17ba7345d23a37e31ec135633cc5ea84c752f56d4ec75878a2920b93e9b4e091e0114552712e1e50ade42e26ac0266b84043a493e1ce2e80cd57422de16a88deceaa55385dc2a977ffc9063e7c427200b6d8511ef9004f89412587bd6d0057898f5ae284db78b0ec861fed36dfb7c7a9679ad0480eefe71985ba6f731bd0e816a901e0c017dd0cb7fc8a4606dec2091a51aab16d6f9bbdecf3fea177671e68250a84fe19de8df78d711e22b81372bc22ae21ac7208ed41201f6e26cd6748e9d6e2f4884f5acba736b2432536718891638d43991bd97c232829e26be6e6bb303d44849b245ef758eb2813bc87cf21a30f132360111e3015de5d1e4f0c5a98aff159c29f6debed7c2f18f455dfc7f33995a90b7625688507ecef1e7db48e7030ea6c4fa835bbc1dfbea6c0a6c704d658d4866a42b9860b1c8b5b64cb669e102c81e369b5f07b8fa08816a566a99f4d2910f6e8d751d52f1e2889f0ec9acfcb4627e0da5c35452be05c7766eddf3c42ceb6a312044075a4231b4203718c886498a313f3ba12e44e368b04ec3ea6e72d6fed9b6b334cbc0ba89f0aa9a129b1bad5b0ad8690291a344967f58e52415859852c6ca3ea24bc93ec1041fd1dc8a6a181326d3026098db0cddec90b3cd6df1e7638a3703f70c9a3baff8f005b90f362459a275a8b39daa78ff24613434594f96b8023a41a17d815e5c0319a39e07d32841339f14f404030b4a22551b86ba94832a1c49053d63140b503f2f64354ce10abe6c08f6cdaf6d8dc361c3c9d1a8077ad34dccc699b6fe07c16f8f7743d04003d672f82e643b3f1d5e263495504e11e6b2e676c11b3d0033d5f837e6bfd01602584ff181e3cb86f081015a9311eed546b42a8280680aa538353949f89674c554b43241e36536430ae9e0190729ce902e8f06a952d23b62816deb3b62b45375033ede2d8065a8e7b38f5aee0a5c66eb2f21f33fa6795d4b086e6f6ac941ba0c883ccf6e54e52164384045e0b0d74a9361f224303c841ec907be250725ab06cf79dd8bff8f46c08963a409b9b71b5c634c987c5e163f73fc32553be1231c72444c5e2a91189824034a784948fc90000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd448983eee52163a177650f57b2cd8404bc619b9b59e796f9808bcd549ae6ae30d448c90f2783978bf9314a8038f45c0da5983163bd26f38f559f59447e8cf004f93b6b5c8af7b09603db021d4bdfa641bd83926eae1709a7a427add14df90cb258c6d4663d4d29709da89c90613d2ff9334637d53ca89407804eb863f78e110b866af2734c980705d9f969730a41132e788fc9e426d0f68ed24157aaff0383438d2715262e9b8b03cff850ba88127a05a8b68ac9a5ae5b098bb9ba5eaadd71ae846b3c0f68db728361eb8c8ed899c77725afbdfabf93812c49cbf4ee64047a96ea71258dbe5be3f988029d005fa2d9fc6e1e53fbeb6888074521b972e2ac71b4f22b754fc743e0de21af1e2ab416b2481e03227a1c7d7ea6cac5bc37ee3597d3bf11bf13a688dfa3d9aeff1eb1a7fdfcf8b6c722a4853f7c2b2d31e0b2b691f4273d4793fbb7a00f27a25577bfcba95e60699c9d2a926e71d64f535b633f2fd03320b28fe86c6619c54b34e6caf8f5a71b8a144c9236bf07edaacb486ff8ac63173af099efe7c9d006a5bb756449fb32b1fbff2e315fd5e96b586bb922a9795e29ffe6ead037c556e1bf30e24afb344cf873201007096b6f687f157588e236b71ade4d9245d8f065f2e23b36fad798d0f5504ddf25b828698d0cbdc28478b20d692d2ab605797a67232b0795927d886de798f00b4e7c69517d62b748e62e01d53dd1e77ac9a1605c0408713ff309ad53ff8f2bef17f9074f01134374068bf1f5dc07125180b5ea6902ec2d55c7d6d5f7ed4ef8732f9d34b4627678611fc9579e4321cea012c4e457dee6a11c41bdd1eb965056e885757af389079a558434eb3d59ae56a232302759431172ecc88de1c5400265f0f47e21396e3c38e0ba022c3e55ee4b85527cf49dece94445adc740cd26c18004a1cb984cc1732a138844da1ab003f89c589b6f3cc10c99a1b0d87be763f83e1b12c6fa6938ebc55d2ba33c25ca816dde207f7186f0c70b56b33feb538eb31175fdcfb036e365087f1b630628affdbbdee20d1976cb009f32db5f35aadb8117aa02ff2da9bdeaffbcc8bf3412efefeb00365e5f1ea577afd6e1c3585c67ffe1fa120382aa54028dcae9bbd624432a6256687d05483f2611f1ddd14b40f66fdf547e7eba904a79bd27733c9a8fbfb01154dda3457c4eacff8116941777ec570ff040e217d648ea5076588a6417462481eba68ebc59af04ba49b92f70b68a007977fde48b94b0af35475ea19cbec92df6449b065880bf03452cb3b3582f3d1a010e585be6506f3e067226471a94ce46c515f20502b3866553c10f037d9be89ad5858d6b2d2d94c70159247f66958d0e841d1c5b4254809d52475fdf96d087c3c6647b86006147a9ebb3f52ea6f4b89d886725b9e9243efd95e434bd8dd785143c57c06863b68df8f832987eb0c730c8b96634c1f888da2ef420cb0ebacf81f4b25c65962ae40c09ac4b0b2d440e3bdaa7309d87a1fa6af1c2e13e7a63c253fae027ceb2067cef8421b62d205f5d37c7204eaf594b1b43f9d9b67509a6709df48769ab9e1078f9e59d7656ec2132b5ebccf297e757a052835fffe94ae073131ac49c4f4374a1904cd4bf3041b236b73ea19eaa583db577fe35ca0000000108676ef9ec3514fd5f004046001ae0c831dde6ac72f1337c9ca111f5b32afdf102d75c017d3bccb6fa89902b750b00c51bb226afa517754b72e962ff007c1c8c8749a21be1d8d94f7bf73437c010cd60e0bd4489ea77dbb530c7ba127c66c3d7bbc00c336fd4e09e1775c646dffaa8696f7b8b00bf91261fc5164d57a4b9652b7cff4e301d32224b4e48cbfca535b2070ac46181615358d87e244ba6e369f6719bd5a551ac05dc78c222fd0969d0d943cbfaa3570ec25ab2768e9679d1cd1a3528659d010c409a0719526c44e4d9915dc5b0618ebc9e35f06b31bfd8e01fad99dabe32f6bfa00b3a5db5a01920d6685c34efb958729ffc5acfe46b3605715149b65b2f638007885a0866bbdde6765992b9acce2f527de906443f8643845489f1224fd3bbbb3fa78ca4848fe0167ec7cff8a05a17eb7c7a05a80c3106647e5d9aae350f33d10f3a60ab1c705858323a8f610d98cc68ef3cea66eedbb788b9a3da873bfd44ed632aa952ed7bb2004f4502260cef0596ec6e82e7683bdd2cb1f63b01b3f928ffa86b89cbeee922f1fd192fea0bdd17cf62d14f06f9e27bf5cafec90ab26f103e1dcb96ae4335e444b1fbad294cc395c5dc3a1c0c1fb4078d362eb229c42bc42ff53115c51f137d75596b3e6d3d28974720d6935430054c6b630bade51ad508d31fdfd572bd37f70e3dc06021d2b0ccf91a7975aa501e152d62980f02ce0ee94b547a2fbede47cf1f5c0a541ccc8992dd006f77437ce6a6b1f4f91833914a1cc51acf9336a620c4a22073966cde3ecac3224941dec004e741e05c11b43796dc531ce33e7c9a4fa68fa689880842e37a3a04fb75f3fcee86813388df74d443d1c35d7adea290effa98309b22ceca9bc252ccb4c443733db691adf0af559a5b7565043f84e91c5ed9f79ebf49f0bd60b68a7b8730032574e8e21548204c75321a374bbbf822efb1281ddf32feeced4bfe22bcf7c1a309954c1e356175a8a1a1a074a22f4561acd872d813c88ea9f0f22ac8d7b7b2088bc8565e1c56dfbc84f57aa38c2600ee20e8736076a91ee73f3137e8da3fe3871587b8bdb0a08af40babbe5493f036b45eea837dc15f761d9475d27a512a2d9dfc1ccdb81e2b581f91a5d7fe67cf6955427315a4e9c158806e651e4acff40051cd8a44b0108876c82f7b4d69033bfd8216234de545bf8fb58e489bc74d366db5e48711ba7f317dcdd1708ed5de97468a6026e15bc68ab11efc90f5465b4466bf384a8cc95f9c7fff91d776cccceeae5badebc31c3516c93a7b4682212e5a4902a9fd0327234749d83c141db2eee9688a76f4361f8b6213c88ebde69ebb84488c9ab8f42737da123eaf39373ac687df65f817939296f5477f92ad3fda0effbdd5d0594eb59d80265eef6cfcaf81b386c9d03c205c1b6714bf31be15e8f871b4791aec10884938285d6b8c18a0dfe750b753de88a2d2d855b9d1a0068ac4d2ce3a259bfdd30414380bf8b287abf2a28de442552f1d70a0aeb0867d9c7ed4e9717565ebc6aca21d85d2845faacfd8fadb1a76d9a2cd619413e631666009085bc7dad7492654ec20431e37ddd55588d2cd4d256021547cac768dfe3f7dcc9a18f0c72a743de799e98398b9bb2f216aea727d240b2f52ee269f4df4b8d7bdb439f074e3ef179ae2ae44daba64864fe427574b659a5e79defaf43e45e1357e1ff48e28ba6384c559cd036c9229151f917865b5575cda11e1ee0690bfbdb628b9a17e9c37190102", domain: "signaler-pa.clients6.google.com", wantErr: false, needsMoreData: false, }, { name: "QUIC Chromebook Handshake[4]; packet 1", hexData: "cc0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944894c6153ddd936992f160ae349757507f79fd0485766987d986518d9f19270a0021c52bff14e594c074f3c5664cf3de3b761cd36c16acf68565aaaeebf196da581533f19a464b22404b13b46ae3e4819bd4a7a85db6ddf379bb84f8dffcbb412c9ce405d8b4c98d303cb13df2e80a88ca0f09e2e2489c8d0b6d5ba9262b85869f8f989e9b82c4a270592894fda96bd27ce03e0cb4d4a4e130d9655e6da02f4348c949bc9b2aa609fdb4b9a0a3e45be25b18fbde569bb996d6419e98f2d9b7a0ca63f52f054b50771dc7c580596d86b4a94642be9a9b78a01ad2deae4607d8e0e25641aa8ed46b20fe027f4b9f77d1736aa0fec4f837cf5d878b3dcefdf3c6e82eb943dd022e98d623403950e6ea3addc0f93f92a422ed686b7beee437a4040f4a440dfd071d8c09f1344c28545b4488765a455e33e13d17434b9642dedadcb6a13ec35d51c3e7a03ae9a76cca6b1cba8312b7d8bae703e0a378300a17b7483d07893ddd941dd3e545a66faa0fabb4dc967c807665ebf4562fede176719d1a126f228acc0902e7235972a6db4eedd6547c38705629ee2574c5d2dd6c76c5c82e741f33291506e66a8df65ef6d1e7e6628fe4a4f5e141d482fc5f9d26609e64da8061eef5c0fca421d5334b199ca8270612074a1f9fbaad8b98ff7b81f8871f4ada6976f254e47c51e03d4c628beb3471ba375642ae0b41d65dec1419cd31f20ec779f5666717c1e7b4240fdcfa46a774234961083e1915e938ae0d41a66868b91949d856065c4e6813e0cbd9680a916b5eb78655dea9ad0f9c0ad0f5c244a72fcb8b589321519e34f0e6e25e6bb43abfa84a7241bc02555fa9060b9ef55f1e3a9dc3a575e16b23a36aabbd3ddeaf8f2516224179a4039a891e7f29631c2a08745bb184c66ffe98bc960e6c08a14524ae34444433591cdf7adc14419310b74594305f67c3087a2c21733e6e0be748e7af6fb6717946c313fbc0935ad3559e2d6323979cdc3bd48753b5a438605e15832efb8a0c4144060f41ed27a82dd067f2caaea3830abc97d9e080b3fd762aecbd58e8b2b17dae553dacdf3ee44198d2f19c0522b6a1b17923a210cf24902c5590afe808fd22e54e586399665d588a7febd0b402a4e6283679e1f95a2d4d7d3945e2bb8f44225ad8aa07cd07d3323ce94f39ae4c9466c05ceeb0a30981cea022d1bcab8a4b0c8e42e08211ee727728c74d7945f2350a149eb9cb7eb3a280954b64e612b53b19016a4c07427945345ffb86982c113ed797172ded4428d6b95b9ce64b48e98ab96421a179983c4a74b986f3f52d9a2d7fac8ea0955835d241bf4817a42950e2b298e51de20c026df81fdb0d28c68841bf62dfcfb4684def62c13ceddce9a25b446043056006ec8582aea14eee602eb2963f575dedfd2313d7d561c6ceac0d08c94645a222b25b7493542fee52c316f06f583612ab2ec3d420a01a61fe80b099386c2fe647292769d4571239592fe7e27f4324456ef894643f72ac450628cdcc9f376607b85f369a092c64d7d5a0559193e29cbc48e9ed77fa3fa05776d6169fbdbafa507db1e1d33a4550003a3a1a794b266e886f483eba76a8629d17d9596574068ffce61a52b209c21c77f5e7337e5541755c9f1c6b4", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[4]; packet 1-2", hexData: "cc0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944894c6153ddd936992f160ae349757507f79fd0485766987d986518d9f19270a0021c52bff14e594c074f3c5664cf3de3b761cd36c16acf68565aaaeebf196da581533f19a464b22404b13b46ae3e4819bd4a7a85db6ddf379bb84f8dffcbb412c9ce405d8b4c98d303cb13df2e80a88ca0f09e2e2489c8d0b6d5ba9262b85869f8f989e9b82c4a270592894fda96bd27ce03e0cb4d4a4e130d9655e6da02f4348c949bc9b2aa609fdb4b9a0a3e45be25b18fbde569bb996d6419e98f2d9b7a0ca63f52f054b50771dc7c580596d86b4a94642be9a9b78a01ad2deae4607d8e0e25641aa8ed46b20fe027f4b9f77d1736aa0fec4f837cf5d878b3dcefdf3c6e82eb943dd022e98d623403950e6ea3addc0f93f92a422ed686b7beee437a4040f4a440dfd071d8c09f1344c28545b4488765a455e33e13d17434b9642dedadcb6a13ec35d51c3e7a03ae9a76cca6b1cba8312b7d8bae703e0a378300a17b7483d07893ddd941dd3e545a66faa0fabb4dc967c807665ebf4562fede176719d1a126f228acc0902e7235972a6db4eedd6547c38705629ee2574c5d2dd6c76c5c82e741f33291506e66a8df65ef6d1e7e6628fe4a4f5e141d482fc5f9d26609e64da8061eef5c0fca421d5334b199ca8270612074a1f9fbaad8b98ff7b81f8871f4ada6976f254e47c51e03d4c628beb3471ba375642ae0b41d65dec1419cd31f20ec779f5666717c1e7b4240fdcfa46a774234961083e1915e938ae0d41a66868b91949d856065c4e6813e0cbd9680a916b5eb78655dea9ad0f9c0ad0f5c244a72fcb8b589321519e34f0e6e25e6bb43abfa84a7241bc02555fa9060b9ef55f1e3a9dc3a575e16b23a36aabbd3ddeaf8f2516224179a4039a891e7f29631c2a08745bb184c66ffe98bc960e6c08a14524ae34444433591cdf7adc14419310b74594305f67c3087a2c21733e6e0be748e7af6fb6717946c313fbc0935ad3559e2d6323979cdc3bd48753b5a438605e15832efb8a0c4144060f41ed27a82dd067f2caaea3830abc97d9e080b3fd762aecbd58e8b2b17dae553dacdf3ee44198d2f19c0522b6a1b17923a210cf24902c5590afe808fd22e54e586399665d588a7febd0b402a4e6283679e1f95a2d4d7d3945e2bb8f44225ad8aa07cd07d3323ce94f39ae4c9466c05ceeb0a30981cea022d1bcab8a4b0c8e42e08211ee727728c74d7945f2350a149eb9cb7eb3a280954b64e612b53b19016a4c07427945345ffb86982c113ed797172ded4428d6b95b9ce64b48e98ab96421a179983c4a74b986f3f52d9a2d7fac8ea0955835d241bf4817a42950e2b298e51de20c026df81fdb0d28c68841bf62dfcfb4684def62c13ceddce9a25b446043056006ec8582aea14eee602eb2963f575dedfd2313d7d561c6ceac0d08c94645a222b25b7493542fee52c316f06f583612ab2ec3d420a01a61fe80b099386c2fe647292769d4571239592fe7e27f4324456ef894643f72ac450628cdcc9f376607b85f369a092c64d7d5a0559193e29cbc48e9ed77fa3fa05776d6169fbdbafa507db1e1d33a4550003a3a1a794b266e886f483eba76a8629d17d9596574068ffce61a52b209c21c77f5e7337e5541755c9f1c6b4cd0000000108a3e7f3133d37e2970040460094b49b4808cfaa190ce163e91b9d2c0105f36a2c93f670114f7b60598f03d6c596ceb19f410e9660903f590e3f25cf619e5c171001990b1d1b97f789595d3039e666345cc944892d4ba45357f2ca515d03b90820bb91c531a4be27266fda6022856da650cfb9c34139e8a3180e93cb73a6864471f849bdfa26c03e30c0e4d00309207cd46fb48887f60d7c51b208c247d1b311b35da70dd682cb1f7ae6a64215e5fefe25249daf308083837a3898e6052ebcf6cef3cb8e987ee1eb5ea797642d76391ae363b8eb2409d7486dd4a67c9e9b755376ca61009cb853835850e4fc1844f8e9eebca73e89317003482f70c4795ce9e2724c6d62172e010233e7bf203dde6eea9976f29896df562e8640a4ed88b5b3dff50296d0db43885f162c588d72de357c2ee049d9532642576de64d4e13cce77208e0aa9cf9838166f3375a968a5a6a01cf066ea0ca27fe4471cf0bf7eb36227867928076985588d05692d3f81d9a1158d150b2701399ee0a32693aaac43c27b76c657343b2a307e7018c2e9fe6317ec09f9afb762075430140b15016ae44acffc7467f4b1cec619942e916047c2db27f89742e53856d8c7c098beaba710340674a3f8455ab38fa2a4156fa3a45dffca1e20ec86ae792988dcb52bbf2ac97ea878e80511d3e4e70ece4b2816401ad450b9d13a1fecd7a5a363dd120285a972d52c06b632362cb8f897f799fb8342850b6670eb5083347347bd48b559f118839aa627598379963ecf18c2a900399ed936ab77ebdf95bdc5eaa75a903005e38dd99362b3d99f07ee2aea1a0ada77ec5ba76a7da2a80b672f4709bd32fad36787e37467e75fe594a24b402a6fa7e858c2abe9cffd9e885cc7091c035e4354779fc113bc084b5f6fcbd7bc618e9cc15205538cf781c96b658565cd8e39d95001d085d52f87a2970eb8f72149f4061d629ca0a928442b586aef9105326e13c015ce2b987c442b574180550302c48c2cf763b45492e25adee42d23bb2608e284caea4b3a28b77a20768dee6c0002b16e5d714eca22ca0c58195e03951b9564079ffa61c81afb2c5783ea2b8c0605d3994cfaed3c33af2279469c771269174fc5879b67617f571eb376161241ca5a89332074635413661b7fc5f925d86afb56b296dedce33d2b5011fc3b85cfb769c4a1cf5d6217ba3db367ead2159a310cd398b31df83f48f722a1df6c4b6604b2e288aa57cbc9afcf42764ccbc718a3ca42895b8e8287d632d2dd47d933a7472fd7be596c4241220917e636a44a139ec16600d74104fa6055a77c34bdbe14fb1ee56b4084d1d2dc1d56f8d636a8393385fcb4916670eaf8553b2126b12c8f187f58f00ba9bdbe8f06662688b9b8c7327b2d2c2f1443c8b87930d0948c3db62c8becb5b38cc7e484be184400f8c6293568abf1714e2832ad2c0aeb88dde64686fa7b4cd8c452b7365e70221e62dd971db057d4f8eed86e6802bbe8f56f75a8fe65d8216c81265e514042dca73f20a50373fb32e9bd62b741021337a3200b5f0a1b1329e99c57c75a60850f6c2f82cbeb9001edb54d985b7d5cd5957b31f79ee77dd8e1ce5e70d50806273885886b93a12c3dc0f211383d180a475d65bb54b0c5f761f456bfc7d045acd2c7ac648f3bf27bdb52218be48cdef7aa1ddc93e3ce11067ef4819796a4e2a30459c879841789b358b9430368b0910d6e6eaa14894f36b12fe9dd2bd1a20ddb6b12ab8fcec7d0629c9a7", domain: "play.google.com", wantErr: false, needsMoreData: false, }, { name: "QUIC Chromebook Handshake[5]; packet 1", hexData: "c200000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac824704489c14d28b48268df096f2db0c040c0ffdfb1817c4e1dd32005e87a74104515069ccd1b12f6fa3f657891bca8f0d27f4bb42b23c8310897ac09241f511888ac2e431e3ba8d9114b3f8bb1470324c8a0815dd1589686d058d03fa2669ad7367dc8877abce4700ae999112eb7cba755b20b7db66918021a36a66c60ecaaeb3e0859e3f50d14c1f8c3f56966043f437bfc16098deeb02e2236cc42d2f2802f4082bec30afc347a75f78c5e7337c52502e8e8d945bb4d792ef69419e57ee4e8f3465de4d0a49955787dff0756f1bcd98268919c112ae5e87ec333d590988e111724627143bf5a0aa69c77ee322b1240a65718e0033c4b70c6a5dc2d3248aef71598d73effa0eb397b79279ce846d9c2556fca28595eb3b196f40f052f0127711abbbc334571b8de67f7d39bd9437f9196bfe0650c4972462473776eccae42418b16edd5f45414e1a5e13300ae6705a49ef68e1748e876ce04fe611c973f94fcbe18b0c1a0ae506ce6eac268021e9744899c33ffe44dabdf1ea5413c20ab1013326255ae6df2d6861a7e914001e231ce4065e0d89fb439afb928d660cea26f99eafe65c9f89c5639f3ba8ffdcdf7a777d7859ca948f123a5c4f1491e22803754373ab0f1b605ac09ca6018c6acca7f52aad786ce96d9ae28d09a77dd88d77f23e2e33475973a23dea914fb049662800cfbed416b61835bd3bc22dace817b3b271c816ccf9603de2209067def5b8747112c49659cf6c5281402bed38a4176c0331e0841592c503d342188ac97266866a102864f76b0ff2adf781e0af84ae725fb6db418545dbb70adde5d4ea6d87f8c9b64b579d4f969830056dc0007cdad648035ccdf2c24264905422dfc1a85ac7f519f475351e046a27268312d4ab6a66ea55be01226fd5dfce06f804cafe85ad997946db3d029ef28ebdd36103c8fc05b522c1d4debe034523595cef576c771f0848a221d76c63dd044ea61a3adcbe5d4f0ebcd0bb631f4db1d88a609a1c2a6cc3de29ccd8a40e8a770d806abdae300cc178a32b4ba979652d0b8849dff252571f0b09935744c5b33628a190ae2eb43543ed8dee8539fd464abfcd3cf826aa18c4e84cd11975c5e3bfd0827f7cd211a2084c8626ed4e32bb9877f4801dabe695132b835e335563cb2f4c3e9377cac57f7766a10620f1c57c9f485d66918613daf8b257035ec91481d0076899c45abdbec38b63745428dee481c1cba81b0ab6e7efd6b0c431e018b6503cb13d4df18dbbff195bad1063d59ca5066e0733d3f499109111d22304066e7656e755518ceb6d862095ae54e532fad82f6c21c0c4d776dfecc516b00fa59e117e79481d3fd386c9b0c4d6fa4ec295a5b5b67207c3db20a206e0e31dad2c8dafb38e35dde331730bea33af32893653763e82565689009265db76b3b142d60f302a9545d82900aa6a017643c5c60583aefae43066ea3908e299612b078d06f102280dce4429461a42d80acdb6279c7aa190f9a8f61b485ffa430cbef199e1732da3d9b96cfac7ecc3b4959b86260eba763abb74c249180f67997967e716c95788bb6c04f182f40b795a929370c7fc72f1785f94cd36e3ddaddc15c7adb226cc131abc1863352caceb541d3924094db9c8a1c21c21640", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[5]; packet 1-2", hexData: "c200000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac824704489c14d28b48268df096f2db0c040c0ffdfb1817c4e1dd32005e87a74104515069ccd1b12f6fa3f657891bca8f0d27f4bb42b23c8310897ac09241f511888ac2e431e3ba8d9114b3f8bb1470324c8a0815dd1589686d058d03fa2669ad7367dc8877abce4700ae999112eb7cba755b20b7db66918021a36a66c60ecaaeb3e0859e3f50d14c1f8c3f56966043f437bfc16098deeb02e2236cc42d2f2802f4082bec30afc347a75f78c5e7337c52502e8e8d945bb4d792ef69419e57ee4e8f3465de4d0a49955787dff0756f1bcd98268919c112ae5e87ec333d590988e111724627143bf5a0aa69c77ee322b1240a65718e0033c4b70c6a5dc2d3248aef71598d73effa0eb397b79279ce846d9c2556fca28595eb3b196f40f052f0127711abbbc334571b8de67f7d39bd9437f9196bfe0650c4972462473776eccae42418b16edd5f45414e1a5e13300ae6705a49ef68e1748e876ce04fe611c973f94fcbe18b0c1a0ae506ce6eac268021e9744899c33ffe44dabdf1ea5413c20ab1013326255ae6df2d6861a7e914001e231ce4065e0d89fb439afb928d660cea26f99eafe65c9f89c5639f3ba8ffdcdf7a777d7859ca948f123a5c4f1491e22803754373ab0f1b605ac09ca6018c6acca7f52aad786ce96d9ae28d09a77dd88d77f23e2e33475973a23dea914fb049662800cfbed416b61835bd3bc22dace817b3b271c816ccf9603de2209067def5b8747112c49659cf6c5281402bed38a4176c0331e0841592c503d342188ac97266866a102864f76b0ff2adf781e0af84ae725fb6db418545dbb70adde5d4ea6d87f8c9b64b579d4f969830056dc0007cdad648035ccdf2c24264905422dfc1a85ac7f519f475351e046a27268312d4ab6a66ea55be01226fd5dfce06f804cafe85ad997946db3d029ef28ebdd36103c8fc05b522c1d4debe034523595cef576c771f0848a221d76c63dd044ea61a3adcbe5d4f0ebcd0bb631f4db1d88a609a1c2a6cc3de29ccd8a40e8a770d806abdae300cc178a32b4ba979652d0b8849dff252571f0b09935744c5b33628a190ae2eb43543ed8dee8539fd464abfcd3cf826aa18c4e84cd11975c5e3bfd0827f7cd211a2084c8626ed4e32bb9877f4801dabe695132b835e335563cb2f4c3e9377cac57f7766a10620f1c57c9f485d66918613daf8b257035ec91481d0076899c45abdbec38b63745428dee481c1cba81b0ab6e7efd6b0c431e018b6503cb13d4df18dbbff195bad1063d59ca5066e0733d3f499109111d22304066e7656e755518ceb6d862095ae54e532fad82f6c21c0c4d776dfecc516b00fa59e117e79481d3fd386c9b0c4d6fa4ec295a5b5b67207c3db20a206e0e31dad2c8dafb38e35dde331730bea33af32893653763e82565689009265db76b3b142d60f302a9545d82900aa6a017643c5c60583aefae43066ea3908e299612b078d06f102280dce4429461a42d80acdb6279c7aa190f9a8f61b485ffa430cbef199e1732da3d9b96cfac7ecc3b4959b86260eba763abb74c249180f67997967e716c95788bb6c04f182f40b795a929370c7fc72f1785f94cd36e3ddaddc15c7adb226cc131abc1863352caceb541d3924094db9c8a1c21c21640c900000001085b15f32cf8fb5a3f00404600f909e38ed514455146c4ab8e53dd225a775dab2a06eaf73c2ec6b36c95c40873910eaa7e424e1b1fcb15892cf9e37a0ad2a1b67e9e8c6ec0c3a99e3d8775afe1e0aac8247044898c283466f3b200164ad9b30e17b425e07f6722df94b9a77dd555fbf25e5b0bae4fef254daf03f156b78afd967614c78208deaadef3552040c055487804c047604c5d6846e67d33e5fb5f743a81f688220d6f4c87091a860885af95d2db27e9c5dd2361f8196f5c1ade8fb37e159980547c38c6a6ddd0e055fbbd52bd7615615ffca15a0c144899d25f21156c53cb3922d2ccb83073e26074025a3c39f64a67dc02044ce0d630d9120041b2233bd26282bd2d7d1a81d486b64cab6dba7fb4375e200f53523f714a066b96769f9b1dc7c14353fc1faa51c0aeb99507ff3ae90ad6f4bfbc9b9ea00a87f8bf8e213a84a9efe2ce624e629261d93c83642df97fe146bdef478cc92bba387c9b524ac83cde55ad8f4a4d8fb3c09c8245a50ac16ebb67d651110a10ad1f7dc74ed32b9d644bc4b229c56942072aae5c311059165ef6839e7cfa0717c7032b667b8618527722fdc10a4c0ea900e9b414521b89ca83f253ea414a410a8b0b13da25c4b618f2ccd79e5d2a7579b4c431107d56ab8df16125bc25673181a6bd5abb09941515806fda32659e5281457d8c093314da0087169781b306f348d3994d84bdef936bb11355c41ce02c359174dc2a366509c130c96ddf5f156e0eb7912ac56611cf4f59ff3a8785034e81738a53ebc7fb60aaed709d78980d39822aae7e9a9d985aee6c11252a5e984ff1d167b9d4dc5d02ca8ba1167422dfc0f68b3b5902f3fd032204a5020b14a288ecd845816575fca7ef13c85765385e9964a9f0f6e2e5c4e600b190b275cd91ee8a38ecf73d35bad7371c15fdb73059afb3178786e76750845232ef787767c6985ea9b9ed9ff73430b7afb8e0b66aa210ab1943b606021bb8f50c55534e1014003d947533c1c0bd0c16dfaabf4914497583b818e643a2d8bc32d33a07133b4eecf8bcbd802dfaae894ebd473338e074af1b3672c6f03c55e6c3885848a4379cdb5873d9ff3015f9ddf8d954b3cb4eb9f56f2e8ccb519dcba72c5832146011068d3074520370fc8008b62f3fdd65978865df1a85e58fb62315dc617a3367e6c9564fbf74f5d5015932d435f3aaaaed37bc7d14d974c0e15d899c684f17db3b4790bdeba076624f4f45a0cc1c6c078a527695110842e837ded310f8c6ddf69a18e455f09130d4b4136ca6d02eee0f103b9923adefe8cb36b8fd554a27fa7300c4a6173835ca2c4d38cc11e3849ebbf73c475b7d0713caed6e70f55b34ccffe03978f231d5f92bc1e2da7ce589e2cc2cd76183394469fa537cb927db14a383dc589cf10819eb90dbdb7981cd1eeb3a284c9ce55ed859fbfbe73ca22328073b534bc81d319b5fdb97e66d532b5ffc19516b4c70ecb68f963b053a82915f3f74c8809f082bbc89d97dff9e8c82a65ed4be8e2975b82f0b7c73f34797c61db4ea8ee273116564288563a71747cf31568317363dffdff1d9e159b2f9bcd3fe5be96a9c50ac885493cc16234c58cdf471381664c1e3b54d147ea5c779c9c9b06d5c493231d9c03b50c6cba315a5ee766a6e8578105306e0911270d347e8ade2b354037f507e4993708191d32b51a79f023f4fccabb7d2ac262004745cc528b8a6dc1d8849e9d4a0152ab326b1eab8a505eef65076aca156f7b9", domain: "optimizationguide-pa.googleapis.com", wantErr: false, needsMoreData: false, }, { name: "QUIC Chromebook Handshake[6]; packet 1", hexData: "c500000001087e13dee82c592724000044d0a17c2a188dadf8c82a018cabf25d25211d9987189b2900ad36a8fa366990353230bc8d41ec5543e91e119a0fd35f72d320d670074d02f412a5418c9a3e25489db73b1f5e5ddb8fc8d6c51fd6a12dcade71e1fc8e89d2663e206185247847d218d695793a8a1054afd18d42a4feaa9bb5fae7651eab0bc5e9aa91539c311cbfd17f46d8894ac45eade657bd456ee87b4475f5e84a2c07571b082ecfdafd1a660075692ae22048d163067a60e06cf879eab20a464c6e44b242da94cc6b92e064ee6a52cb34c115056925d4615e51157da4e88f019ae144040827cf2cd2806b3f4018cde5f42e041ef55eab35fe65ff79dba1ddad5aba6221e93d4555510176463888666b1d1f53a2f3c71e8872b05717002717edd3a9c5c651be47a663ee877962623ee92310b2b1dbcd75ec2f31d9e2f0c87affe3ee083aaa0868c03c9b4e78660d8afa4e936f81dfcb2fc33dbc1ed3828195c8de3130a2757641652de23d270c8a25976546c46460d9635f499a650475a4288d1c62241f65ddd346e2bde3f4fb5fec76646f51d829b55e16a0975494c11101abb504f5398a761d59355a4843e14bfeced024239ca06717d27e932df5e050d7c54c9b3cc937f6b0731f0b1dc312b14cf4388bbc0c601abf8ca16ed7d9b597dd414156d75b486ac256f8989a072fe680961ac7446201aca359337ef0e58ffa2aa12bb3c9bd1a9f6237b5c37880cd450d8a95db7c1463c53ac4bca174f278be1a99fb75ded9fe282e0e44a03b51ad943cf8970e4ca7f8567d9a3883d07bc0ff6c11639ebc7614fa88496488cb2a49a158d0f008a6a03caf97d77c3d03a98c5a611b04f865d134e258f4094bd220e00bde789c3b3158e622d509073179c0725c7a14dde1cd76b4c6b61fc7ba87f05742f081a5c42dbe83445132b8ad1539ee36e221a9dc700ccab55e34688b082a1a8edc48335760f3d5fb7e92b548e7a09d11f15b97bda08ebb5ea355e274587720d7c1189e9ffbb00dba3c13da94d646e7742e3641d59aee50a5bd400cd992c5614325d8f1ff3529637c3f60adc1682fa6b5e26f3d52de73236680abde87ead74368e05c600a2fd291a592be00fba64eea78c9e1f5e55e61f691b3eb0ef17b7bd11f06427c89c4c930d18c0b28221a6c918322cae56397c7e2978bf253e7f2d987f4cbe66c44a96b45cddb9d5f6dae47bbc9e1f9dcbe6280e50c5dfc91efb469f408b28fed49c583800009dc8bfb4bc42174175df987a3be833582abd9aa09ba0425973de2ea9a4149a81ae1863e0c9f1b1075c26bf965dcbec2bea47ff6042495ed715b65fdd3266800994463c95960dfb6ceadfa07d58910d329fa7ef7a8f14da4a6d3b09faa5b17cbac8481ea46cbfcfb54f660929e268bcf2f86cd88a1b065dcc27f18110db9efb6fcf1eec62874ed3657b1d43419f39e785c510d239c021b7e97d258d789c90d39b434f1667495bd4f5dc5e0eb97df376d801cced0da3a85aab6ca12893d8622314b5d530f28ead33075891d0ee553d5bedcffb20fce9933ab5e117df816c96398f6a60c9e6f5b9182fd7d58869de01635b2c178ae7738beb81e318934ecd752393129fda6833718d6984d8e1a8d7bf52e9d93ad0902c0fbe3e66ae8305e43363d8996626646a684bebfb1809ac9823be750e84308ef573243b884d09ef294094ef256cfc13cb0fedb1e095ff71687c09a767bff308e562e1a6ce9964014a7afc8db2481d8d07486", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[6]; packet 1-2", hexData: "c500000001087e13dee82c592724000044d0a17c2a188dadf8c82a018cabf25d25211d9987189b2900ad36a8fa366990353230bc8d41ec5543e91e119a0fd35f72d320d670074d02f412a5418c9a3e25489db73b1f5e5ddb8fc8d6c51fd6a12dcade71e1fc8e89d2663e206185247847d218d695793a8a1054afd18d42a4feaa9bb5fae7651eab0bc5e9aa91539c311cbfd17f46d8894ac45eade657bd456ee87b4475f5e84a2c07571b082ecfdafd1a660075692ae22048d163067a60e06cf879eab20a464c6e44b242da94cc6b92e064ee6a52cb34c115056925d4615e51157da4e88f019ae144040827cf2cd2806b3f4018cde5f42e041ef55eab35fe65ff79dba1ddad5aba6221e93d4555510176463888666b1d1f53a2f3c71e8872b05717002717edd3a9c5c651be47a663ee877962623ee92310b2b1dbcd75ec2f31d9e2f0c87affe3ee083aaa0868c03c9b4e78660d8afa4e936f81dfcb2fc33dbc1ed3828195c8de3130a2757641652de23d270c8a25976546c46460d9635f499a650475a4288d1c62241f65ddd346e2bde3f4fb5fec76646f51d829b55e16a0975494c11101abb504f5398a761d59355a4843e14bfeced024239ca06717d27e932df5e050d7c54c9b3cc937f6b0731f0b1dc312b14cf4388bbc0c601abf8ca16ed7d9b597dd414156d75b486ac256f8989a072fe680961ac7446201aca359337ef0e58ffa2aa12bb3c9bd1a9f6237b5c37880cd450d8a95db7c1463c53ac4bca174f278be1a99fb75ded9fe282e0e44a03b51ad943cf8970e4ca7f8567d9a3883d07bc0ff6c11639ebc7614fa88496488cb2a49a158d0f008a6a03caf97d77c3d03a98c5a611b04f865d134e258f4094bd220e00bde789c3b3158e622d509073179c0725c7a14dde1cd76b4c6b61fc7ba87f05742f081a5c42dbe83445132b8ad1539ee36e221a9dc700ccab55e34688b082a1a8edc48335760f3d5fb7e92b548e7a09d11f15b97bda08ebb5ea355e274587720d7c1189e9ffbb00dba3c13da94d646e7742e3641d59aee50a5bd400cd992c5614325d8f1ff3529637c3f60adc1682fa6b5e26f3d52de73236680abde87ead74368e05c600a2fd291a592be00fba64eea78c9e1f5e55e61f691b3eb0ef17b7bd11f06427c89c4c930d18c0b28221a6c918322cae56397c7e2978bf253e7f2d987f4cbe66c44a96b45cddb9d5f6dae47bbc9e1f9dcbe6280e50c5dfc91efb469f408b28fed49c583800009dc8bfb4bc42174175df987a3be833582abd9aa09ba0425973de2ea9a4149a81ae1863e0c9f1b1075c26bf965dcbec2bea47ff6042495ed715b65fdd3266800994463c95960dfb6ceadfa07d58910d329fa7ef7a8f14da4a6d3b09faa5b17cbac8481ea46cbfcfb54f660929e268bcf2f86cd88a1b065dcc27f18110db9efb6fcf1eec62874ed3657b1d43419f39e785c510d239c021b7e97d258d789c90d39b434f1667495bd4f5dc5e0eb97df376d801cced0da3a85aab6ca12893d8622314b5d530f28ead33075891d0ee553d5bedcffb20fce9933ab5e117df816c96398f6a60c9e6f5b9182fd7d58869de01635b2c178ae7738beb81e318934ecd752393129fda6833718d6984d8e1a8d7bf52e9d93ad0902c0fbe3e66ae8305e43363d8996626646a684bebfb1809ac9823be750e84308ef573243b884d09ef294094ef256cfc13cb0fedb1e095ff71687c09a767bff308e562e1a6ce9964014a7afc8db2481d8d07486cd00000001087e13dee82c592724000044d0b2c403d66eaa310a954540668e9edc4b17a321446ac931672ca3d9097b2854efdfa7a8f610be76592b56ef9154b9ab42f4dafc575a9b9572433fa2bba180194a32a72a92a42560ae840508317cf34015b1d7d88a633588ce4333cd12bec1f9d53fe905130eb136852d4f405ef66254a0807640fe415e6d667b9f5e2148826d5a6c36b3c44b822cfb7a1e76954610a522e1bc9703e155f8f1e0c705e411d09c5dec1183c61608174767408558da03290f5536682373a09c762deb829dfe3ee061ac9f509a2b3d20dbb77f262e9ba8a5ff50f895165742c90c4ff48eb0438d5ff8491700f97fbfced20e8e95ab9b67078ece6664527eb8a4e78944c07c6bc5c48776e141418c3b5a0e58d24114b7d65a83619525f5a10be53a55f6e3e65080cf42109aa2ec166fbb9079444ea6c809b5062227fed7cc81609a6d7ea471bc82cd0759a354aa896c7a8242c3d8c845aa52225bbaf7546a0de6510189cd2852f7b70a29687eee6a3a99a3a288f3c3672a72dba2843fcee320e18ea906adf1629cc7f8220b6be890a8d37f4093022717d8c6d76080d07798ba74e9cda1a4c26189c367a6d12da14621b93fbfd2d2365465350b5864e4981955750c6d4038b74644827cd60c8cad0d6a18869f99d61b214becedd8f71a476c2a1a0ea8fade7b15de6ee522b6b90d0cc4e6d40eb85d9b5097e2969e85eae6710285016ae47fbc858d63e13d882721aaef3cbe0a6e4c4910e8b3e465fa66a738d8979a444b4a4804baf5b4f9a5fc8438f4991c32d280c5d273d0a1e0d9eea30d003162cd313780a5b000f70fb7797e6111b354c3deac241cb816328b77d71584852966392cab9b1a3d64ead878a2cfac085452397ba17b5cddf96415c31846a80fa9a5a21fd5f9963324ad75505d4b5c70903ec3b5f91c3460d88a7d1c6b111a5206d11accac8a2115e1ce8e834b01f48e041dcd71585c6fcdd3a2e83ec2ff1a2b85e75134ec966afec023d375a2a8bcd54ef0c42d50e20c6cd9b9fdab63785e62c5a4ceb8451c560b647a6cd87698e56e0be4e2990337f45f1cb4c73f0dbf98da1158d75df6eb60ab7a61d836c7b7f3e5c809b850a5cc5369ddd4e455da5e88179e932311873d177febf6226a378c72324ea710e10ef74f6462e7dba25b7df336cb2947df749ad0b8455f3c5c9ba5c0c8eb1e665fe50451c928ce87cc81a0ae2102e7a4fc297392818db885943112fae3c4547ea48c89ef9cd44c2edba4856dbc72b956e4fc6a875bdac57b1cd2378ddb9322e5a1d1844977ff2a6e94d8a00f4ef0a7ef7949f0840c6cc781252830c70d37df9846a69eb95bc733ec420b7b3724572521496da27bba1fcb73c28eed1eefb094283ab01284ebd82004e0be9977f23fbf135b8162ac41a2bf9e1b941b05cb52c80279775070cf2f851bb0168235c97de47880d2d51f1d5ba8327af33f16f49d44326b0d08d20ca7880ff88a14d3201452d7fd452b24fc3b69b89c5149795bc4d0bf51b6ace2c9146048e5ca0259b4955a069a16d5a73e72248cec44b2ca542dcd326aca3ba53bd48368719f44d53eddbace11ff28c11c7fd90c38966752532d3f81325d1682839b2da3d69acc85275c71ef3b10c4d983cfb0cb464f554d9360b0d6ec8fd25b85824350866207eef9e2c66e43f5d08e86aa6186b36d9ece72f56e06ac8e51a1b53fab4cc496e69307e9810a9b5cf960f306ec54131dd8e917a5861ba92f6885e41993", domain: "lh3.google.com", wantErr: false, needsMoreData: false, }, { name: "QUIC Chromebook Handshake[7]; packet 1", hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8b", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[7]; packet 1-2", hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8bc4000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544898bcc839c7364f9518287bbf01c06e9fd67f74096cab78bc885ad264ca9151a48420ea11d5b9e3bc899cc0d663509c926bee5ef2a389a9945f5919677472b3245a2a68ab91654ba5f75026ae738a9e10b6b7439503961bedbfa2494099a9bba3b9595f70cacd8950abd5d3b8a85ac61e6482847e0ac71cbd63df462f4978afc7084ec5c9e65f3b57ad22f802d699c94f260212a61db6e6958ed60356bf991d3af9582cd1724a90f08e869ebd7f29fc3fb9e1198819a967096de1bdeea692fade6229b41c5efe6cdae313014db328e99bae1f323584a308f575b923a11185634b24c8cc1d608303c8ca5c1ae3d54b8b7ecb689d5e0e22323c5e47d8f9093080bcf1553f7fa238e156658ef50281f633f184e47992645c82ab9985b9b13f97f0bbd9db94285c131fff5870ea38691ade2e017ee6417a118a9b31f5b85ba2c76163f8668d90d448976d9d37238605d36b0b7e7abfb0e9c2866bb11b128bb70f003aeb77f439c6148d28719a4f336f4d622140faff82595233cff94906695e402b9115ba7b99f7d832f55d8de481af808ed9d48a493c3301d2b633cdcdc7a64acefaa9408b99a52768edeb9824cdb49257c6d3ea3f23763f70b74fc46252a97f7b3e173a93e81cca50fb12e8b0ea1083f9262e1607156296b5ff85e1531335441c90b442aa5dc616ed7b520bc040502280a0bdb011d6e9dac154587f033b3e9434cef567b724bf5203c315ef5e283028ec72ff33736499b8d326cd643e3a37d53e70b9a2384ac6037f488075eef8b2e507774d3d450ea4b1b65c1587eb2a60c6aad0ae20745a2c50f8acd8bc1e25740b4e7ab4b03a59731ccc487831b46ca0211264f4fd4a498e145aca0d7339663d29a7f32a14fc2b62a32f6354abf2d7de5ed9d7e0a1d6dbbfe14af25ca8c927dc8eeb2618f308eac81405552f1bafcaaede51f560fe5eb6aa78b8650ae97040b46469503c85994ebcf7ffa0222905657111325f861d380935c2a5b9ad26094e85b92327f79f66b4a2f8cd674344f931f0e056ba58a084a1026bc422b80ec11b17de1ca965e8ac9ede1464588303986ba8fabb395b55de5580131ae3bf61822e4e2817dacf765e034542e435c4e9ebc58f21cfa7901dfbcc2b2be98cc55fa6d9e0030e17a32abf84d551a74f7391dd204847c25ee6382f6374b8ecc5b29b598b9ba562e0a6f25ebb932570ae8ab7ef7d06e444fdf79845e88b3132ffb6e1f330e3424272da082486aba357bd254ef0738bb7f5c9db73d2a9ba0c9afeb34e09ff0e20bd44ba1a46ad3081db2f750d00b647756dec1bb41032e1aaf56f58d7046102aef6ae40517f9cbc148692401ad06ea4ff6ed3e5c8a0cff8f9466a3530a99cfb9b5a857a967675df0cfbe3b798fdc2d929fa4c86e3b3e3c45b75fa5db6c15953f25bcd025d7efc3172b2206a200128f6d8caca97154b2608511b35b0cba9a550d8f791552faed64036cfb8498dc60492207ab81c3f3edeafd9d3acf5bca2f2735117a273c70345d3b7e289b9948edda3d5ebb8adde7de2451fe2d942d92ab383dc9a9adb6fb9e8cbc0b73d852f176e0265a6107e6a59746e2f2e7203fd445e5fe278d02cd5dac1b7988e205c37bc5f35dc27572743810453b9b27e55c", domain: "", wantErr: true, needsMoreData: true, }, { name: "QUIC Chromebook Handshake[7]; packet 1-3", hexData: "c9000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544896cd650da62160bdb601194f82a98c06df2546b79b5bab9503eafdbfbcfd1128204b52c9c5dde4d661cb84f2e015b7de9e10109874176362b7f06d4b49d850f2dcc8485aacb82fed8017fe454a1c31cfb82d392d6894081fa3ab1e5f2b71df79bf5a52a68a90d4752cf441220acbb274e3473f65c1a6e4cea3295da23e4a07818d5ba80b0668638c6ecb0b3ee12ecb8bed4ca32e27f4eee7376010b3905b8f7055728b8b1eda2e2707338b86f9a6115a23b29a010769d9a0e19d2bd06b81a507c4775d7215030862a026f168664255912c321669a5eff84ded4fec82af394cee909110758e5ad4de945236001bc0627d8e88ead3c3a6790d7e50887ced96f8380a80b11477147abc24dc4963c1907a07fec13e3cd8d5bab83cabd7cf8bb66af4911dfcdaf0f13aade34865b0740bebe4b4a482f1e3105bb9c3e5fece975187fa4eae5cb8fc868376b8b8329f89e407618aa5265f8ce73437e9f2714d752dad78025a38eaca1ebd6617a9a8a0bdefa8b3d3daad006978ba85d17ce3269da06be92eec61e4c5c99712517f01588b0b7deb4d7a75cadd5643daebc731f4a7bfaf17e8f002721b054a5d6d6ca7a3430c9480daff6adfd0a5480968b4fe0ee616d2e1f2f01268dcda2ee523fa593a0c83cbfc051dd7d503d48b152eb53894f79d4f6d38f05af35b287d16df579575b755f36e87b6df082108fda812195d5f60d2ceb59a54f09bb270d660c1d923348c7fdad97dc081e749a393af83bbcdd3c290efa19bce1a0f68dab0061df9dbee778dffb7754db10cbcc354a4b66c614e29d216a3a45a38594d337f775cb88c940924734ad52f8856606147b14ae2cf2990e1c401d79d27e4d6723afa4047454580698b9108b952b78b6bfd31db3b376f0879b2a0d8949b8443ffb9f7eb84b8024c620680481944312e86d183735effadab2d9f144a72d170fb035ed230f1451c94cb3f39bc28929e7480b22bbf6c906070eda03884cdd7ada9b10b7af27315c3bde8b4a273d46d1f764669aca152120da3e0a4fcf1be84b4f3cdfefe235a16598022cc74ad8f32e78aa06eb74ef61cccfc82ed35bcb70f008368ccdc17a6f64a316fcc9f006f307fa7a1a50f28c343c4c93a39df73a0d0a6dfc6f8ab10e966f738bee331e5a29d91ed993fd2638f0ec9d0ce539552b0a312fb85ed5e9f392fbc76a6164298b9de2c47dcb21a895957e92bc1270dfef3f00f44cc42c5f5005132dd030f9aec804045731c6a3eed3776beebe9451488932a1172b979f371aa370308037e57513a8fc9dd03d63fc2e5f7dcd683de26e116ea11a1d3b5e61fb5bbddc98e4ccd20be9ee71c02cacc95cbb17dd404558f586d4f0334bd12fc0a584d29eef3b4c2ce3f87babc462b6d24ca10aa8f1eb1abbd29d11ce3f1c92426c4950e53ba6c914cb4bf0bb1b44b25452cafbf246b76ce17f829bef3178174fbac4f932e6ac18e579fbbf8790611187c6f01de70fa82a21e979c90eed3c7f7b7e416491a000b5f2216e54858fa61893391b115573b2b960a0f7dc1e2a703a6f38485589c9133b4509fb54b4a602cc7d3341298e8e5da88d5e06aad28738650c08d8c71239735375e5bca7ea91dd78d748d65af598192317fd69e03daeecc99b8bc4000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544898bcc839c7364f9518287bbf01c06e9fd67f74096cab78bc885ad264ca9151a48420ea11d5b9e3bc899cc0d663509c926bee5ef2a389a9945f5919677472b3245a2a68ab91654ba5f75026ae738a9e10b6b7439503961bedbfa2494099a9bba3b9595f70cacd8950abd5d3b8a85ac61e6482847e0ac71cbd63df462f4978afc7084ec5c9e65f3b57ad22f802d699c94f260212a61db6e6958ed60356bf991d3af9582cd1724a90f08e869ebd7f29fc3fb9e1198819a967096de1bdeea692fade6229b41c5efe6cdae313014db328e99bae1f323584a308f575b923a11185634b24c8cc1d608303c8ca5c1ae3d54b8b7ecb689d5e0e22323c5e47d8f9093080bcf1553f7fa238e156658ef50281f633f184e47992645c82ab9985b9b13f97f0bbd9db94285c131fff5870ea38691ade2e017ee6417a118a9b31f5b85ba2c76163f8668d90d448976d9d37238605d36b0b7e7abfb0e9c2866bb11b128bb70f003aeb77f439c6148d28719a4f336f4d622140faff82595233cff94906695e402b9115ba7b99f7d832f55d8de481af808ed9d48a493c3301d2b633cdcdc7a64acefaa9408b99a52768edeb9824cdb49257c6d3ea3f23763f70b74fc46252a97f7b3e173a93e81cca50fb12e8b0ea1083f9262e1607156296b5ff85e1531335441c90b442aa5dc616ed7b520bc040502280a0bdb011d6e9dac154587f033b3e9434cef567b724bf5203c315ef5e283028ec72ff33736499b8d326cd643e3a37d53e70b9a2384ac6037f488075eef8b2e507774d3d450ea4b1b65c1587eb2a60c6aad0ae20745a2c50f8acd8bc1e25740b4e7ab4b03a59731ccc487831b46ca0211264f4fd4a498e145aca0d7339663d29a7f32a14fc2b62a32f6354abf2d7de5ed9d7e0a1d6dbbfe14af25ca8c927dc8eeb2618f308eac81405552f1bafcaaede51f560fe5eb6aa78b8650ae97040b46469503c85994ebcf7ffa0222905657111325f861d380935c2a5b9ad26094e85b92327f79f66b4a2f8cd674344f931f0e056ba58a084a1026bc422b80ec11b17de1ca965e8ac9ede1464588303986ba8fabb395b55de5580131ae3bf61822e4e2817dacf765e034542e435c4e9ebc58f21cfa7901dfbcc2b2be98cc55fa6d9e0030e17a32abf84d551a74f7391dd204847c25ee6382f6374b8ecc5b29b598b9ba562e0a6f25ebb932570ae8ab7ef7d06e444fdf79845e88b3132ffb6e1f330e3424272da082486aba357bd254ef0738bb7f5c9db73d2a9ba0c9afeb34e09ff0e20bd44ba1a46ad3081db2f750d00b647756dec1bb41032e1aaf56f58d7046102aef6ae40517f9cbc148692401ad06ea4ff6ed3e5c8a0cff8f9466a3530a99cfb9b5a857a967675df0cfbe3b798fdc2d929fa4c86e3b3e3c45b75fa5db6c15953f25bcd025d7efc3172b2206a200128f6d8caca97154b2608511b35b0cba9a550d8f791552faed64036cfb8498dc60492207ab81c3f3edeafd9d3acf5bca2f2735117a273c70345d3b7e289b9948edda3d5ebb8adde7de2451fe2d942d92ab383dc9a9adb6fb9e8cbc0b73d852f176e0265a6107e6a59746e2f2e7203fd445e5fe278d02cd5dac1b7988e205c37bc5f35dc27572743810453b9b27e55cc5000000010852e6d0c4f5f177b700404600eae9a1449fd711efdca846f59b6c9975a2189c1430f740e0e0eb4d50be086b798a47e037ee6afe9785b45df6302cfb87d39b9c03d0712f11793adab33e74904249722c4a1544894fb2a6f735bea394066ea4ab5d3ef6f419c4bb099bcea8fd7e36d8ec14fae027d0c84ba8c38a5ea1e2e9ff24ec334dee8085d1fa9d88df2c5ce3dddd981f7b2a68011f2d4034f8e4c6f43e645fc71e19927aec56045f0ef72d9da1942dbe3e42248c2f695525c8d8fcf6dbc970c2eb5d607e07dce5c8c66d4de07de6b2c35bd3bfa12cd4e4fcd4cdda3e0b2ff7575d406db96502f1ec4d9b5748215fe2a4018c7dbdb5ead1ddfc06da5233ce359e4f3924f2af1b80c7af9d13437e0107f7e240e485458a3a656e31a543bc5a80f6a598dcbbd87ff9cb4ce6842abadb72d62ae03e6d12b7f43ac1805e408d738148fa3a5c34deae7378d7d7309a7e09f5bac848f4c031693fbe3382a2de66a9d007420512e24b8a78a9489b30794c7cc51dd751c1cdeb2870b7c4a9b9606547f843c1a16d9be986b2f3e3d2f73f89c8e15da9de3dfdf7a667ced977332c5c62fd83d3c9a5e9718643e716db8f1b6fb58904ca17c24c9a4a191ec42be4c405fb00e443429582ff712dfdbadf1bb7e2d2abb0cc14f39c13a3b7013c62fd99488f043a6aec3ed7998b943ee24905fc915e3144c54393fa67ff34fdce2e48bba044f13ee1cd9c4f59a1a41f7fb7bdd8daee73461234f7cce5d54b078c3ad2b0aa37850ed4bb24f4d310d4ce75ede546ed6e73c0ee495ff8ce4b7256e8f43949539bf9df6e8d0fbf066fb506020c5a72e5e8b686641b78b365474c65fe9d8dd41133e27326fe82744b45b6ad1170433f3746647a68b824e94a213cda4c02f78465acbccdccb5bad1c70806d5a5c98c94338f88bf980b05b72cb82a4fd5b2a8586a5e5c5f2760115df595091809cfd12829f09622b53ca1d3809060aa7ab5d1f3640b3c2792a55c58fc3e80ac5d7f39ab5774a80fa1fc70461d396fa70dad1f90598244ce4197cb3ea42dce4afd61ca8ef93c8bc254d347872db21edcc3857bcb8dbe627508aa4856bd7d46e512db071905b3db100f425ba9f7181f0cce005cee2a95ffc190ddc1939e7049e58791b0e186433b0409f5a49e4e3262690a5160f8267c9099afdc58182236833fe7f825dfca34b08801345c1592bbab4964b34d7efa6c9d92e0106ede9a10fbf2a1be32f61f914211c3caa8b4c14edec5f9c139ee14789fe7d6634ede9bf9789caa60f5bf30b092e65ff95d3b32cbdc3e5842e3b16b935d31a3a0963bb0fe60f41efb6590f24eaf5e84006b28b3c755203113237e43fa70a37a009f71da49ea3f8097914d6128ee2b18adac49b5111fd3d18db9fd61ef8a2202fac5cfce646ccbea7eaaa81df0f1b7243465de15a3900143f479852f0e40bfad434b96eea3941f527b0d31c3d8f43188b911140766b5d7146feb93bf4da1ec47023dcd8f89863e487ba25c3105a4e43c4ea90f479eb0f774f3aa044b817f7e69b5dd1b3954e7dcdb2a6c4d191d5b9178262449413310f3876dd93145716781cb077025deb37d23c35e6fdf867d5353d10303b9e60efa50e9ecd013cd3f5270fc0e117a19ffb63038c594190018bd9c1c18a799f548c08a3e6f768de0de344cff160689fed73aa7fc4edc26f77413145775745c25fc2c9da5b62e24eab9b21895cfcda6e6457ae9fdfa6c54b49b0d160dad0aa7f8cbd3820a3098", domain: "ogads-pa.clients6.google.com", wantErr: false, needsMoreData: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pkt, err := hex.DecodeString(tt.hexData) if err != nil { t.Fatalf("failed to decode hex string: %v", err) } quicHdr, err := quic.SniffQUIC(pkt) if (err != nil) != tt.wantErr { t.Errorf("SniffQUIC() error = %v, wantErr %v", err, tt.wantErr) return } if (errors.Is(err, protocol.ErrProtoNeedMoreData)) != tt.needsMoreData { t.Errorf("SniffQUIC() error = %v, expectsNoClue %v", err, tt.needsMoreData) return } if err == nil && quicHdr.Domain() != tt.domain { t.Errorf("SniffQUIC() domain = %v, want %v", quicHdr.Domain(), tt.domain) } }) } } func TestSniffFakeQUICPacketWithInvalidPacketNumberLength(t *testing.T) { pkt, err := hex.DecodeString("cb00000001081c8c6d5aeb53d54400000090709b8600000000000000000000000000000000") common.Must(err) _, err = quic.SniffQUIC(pkt) if err == nil { t.Error("failed") } } func TestSniffFakeQUICPacketWithTooShortData(t *testing.T) { pkt, err := hex.DecodeString("cb00000001081c8c6d5aeb53d54400000090709b86") common.Must(err) _, err = quic.SniffQUIC(pkt) if err == nil { t.Error("failed") } } ================================================ FILE: common/protocol/server_picker.go ================================================ package protocol import ( "sync" ) type ServerList struct { sync.RWMutex servers []*ServerSpec } func NewServerList() *ServerList { return &ServerList{} } func (sl *ServerList) AddServer(server *ServerSpec) { sl.Lock() defer sl.Unlock() sl.servers = append(sl.servers, server) } func (sl *ServerList) Size() uint32 { sl.RLock() defer sl.RUnlock() return uint32(len(sl.servers)) } func (sl *ServerList) GetServer(idx uint32) *ServerSpec { sl.Lock() defer sl.Unlock() for { if idx >= uint32(len(sl.servers)) { return nil } server := sl.servers[idx] if !server.IsValid() { sl.removeServer(idx) continue } return server } } func (sl *ServerList) removeServer(idx uint32) { n := len(sl.servers) sl.servers[idx] = sl.servers[n-1] sl.servers = sl.servers[:n-1] } type ServerPicker interface { PickServer() *ServerSpec } type RoundRobinServerPicker struct { sync.Mutex serverlist *ServerList nextIndex uint32 } func NewRoundRobinServerPicker(serverlist *ServerList) *RoundRobinServerPicker { return &RoundRobinServerPicker{ serverlist: serverlist, nextIndex: 0, } } func (p *RoundRobinServerPicker) PickServer() *ServerSpec { p.Lock() defer p.Unlock() next := p.nextIndex server := p.serverlist.GetServer(next) if server == nil { next = 0 server = p.serverlist.GetServer(0) } next++ if next >= p.serverlist.Size() { next = 0 } p.nextIndex = next return server } ================================================ FILE: common/protocol/server_picker_test.go ================================================ package protocol_test import ( "testing" "time" "github.com/v2fly/v2ray-core/v5/common/net" . "github.com/v2fly/v2ray-core/v5/common/protocol" ) func TestServerList(t *testing.T) { list := NewServerList() list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(1)), AlwaysValid())) if list.Size() != 1 { t.Error("list size: ", list.Size()) } list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(2)), BeforeTime(time.Now().Add(time.Second)))) if list.Size() != 2 { t.Error("list.size: ", list.Size()) } server := list.GetServer(1) if server.Destination().Port != 2 { t.Error("server: ", server.Destination()) } time.Sleep(2 * time.Second) server = list.GetServer(1) if server != nil { t.Error("server: ", server) } server = list.GetServer(0) if server.Destination().Port != 1 { t.Error("server: ", server.Destination()) } } func TestServerPicker(t *testing.T) { list := NewServerList() list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(1)), AlwaysValid())) list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(2)), BeforeTime(time.Now().Add(time.Second)))) list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(3)), BeforeTime(time.Now().Add(time.Second)))) picker := NewRoundRobinServerPicker(list) server := picker.PickServer() if server.Destination().Port != 1 { t.Error("server: ", server.Destination()) } server = picker.PickServer() if server.Destination().Port != 2 { t.Error("server: ", server.Destination()) } server = picker.PickServer() if server.Destination().Port != 3 { t.Error("server: ", server.Destination()) } server = picker.PickServer() if server.Destination().Port != 1 { t.Error("server: ", server.Destination()) } time.Sleep(2 * time.Second) server = picker.PickServer() if server.Destination().Port != 1 { t.Error("server: ", server.Destination()) } server = picker.PickServer() if server.Destination().Port != 1 { t.Error("server: ", server.Destination()) } } ================================================ FILE: common/protocol/server_spec.go ================================================ package protocol import ( "sync" "time" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/net" ) type ValidationStrategy interface { IsValid() bool Invalidate() } type alwaysValidStrategy struct{} func AlwaysValid() ValidationStrategy { return alwaysValidStrategy{} } func (alwaysValidStrategy) IsValid() bool { return true } func (alwaysValidStrategy) Invalidate() {} type timeoutValidStrategy struct { until time.Time } func BeforeTime(t time.Time) ValidationStrategy { return &timeoutValidStrategy{ until: t, } } func (s *timeoutValidStrategy) IsValid() bool { return s.until.After(time.Now()) } func (s *timeoutValidStrategy) Invalidate() { s.until = time.Time{} } type ServerSpec struct { sync.RWMutex dest net.Destination users []*MemoryUser valid ValidationStrategy } func NewServerSpec(dest net.Destination, valid ValidationStrategy, users ...*MemoryUser) *ServerSpec { return &ServerSpec{ dest: dest, users: users, valid: valid, } } func NewServerSpecFromPB(spec *ServerEndpoint) (*ServerSpec, error) { dest := net.TCPDestination(spec.Address.AsAddress(), net.Port(spec.Port)) mUsers := make([]*MemoryUser, len(spec.User)) for idx, u := range spec.User { mUser, err := u.ToMemoryUser() if err != nil { return nil, err } mUsers[idx] = mUser } return NewServerSpec(dest, AlwaysValid(), mUsers...), nil } func (s *ServerSpec) Destination() net.Destination { return s.dest } func (s *ServerSpec) HasUser(user *MemoryUser) bool { s.RLock() defer s.RUnlock() for _, u := range s.users { if u.Account.Equals(user.Account) { return true } } return false } func (s *ServerSpec) AddUser(user *MemoryUser) { if s.HasUser(user) { return } s.Lock() defer s.Unlock() s.users = append(s.users, user) } func (s *ServerSpec) PickUser() *MemoryUser { s.RLock() defer s.RUnlock() userCount := len(s.users) switch userCount { case 0: return nil case 1: return s.users[0] default: return s.users[dice.Roll(userCount)] } } func (s *ServerSpec) IsValid() bool { return s.valid.IsValid() } func (s *ServerSpec) Invalidate() { s.valid.Invalidate() } ================================================ FILE: common/protocol/server_spec.pb.go ================================================ package protocol import ( net "github.com/v2fly/v2ray-core/v5/common/net" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ServerEndpoint struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` User []*User `protobuf:"bytes,3,rep,name=user,proto3" json:"user,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerEndpoint) Reset() { *x = ServerEndpoint{} mi := &file_common_protocol_server_spec_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerEndpoint) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerEndpoint) ProtoMessage() {} func (x *ServerEndpoint) ProtoReflect() protoreflect.Message { mi := &file_common_protocol_server_spec_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerEndpoint.ProtoReflect.Descriptor instead. func (*ServerEndpoint) Descriptor() ([]byte, []int) { return file_common_protocol_server_spec_proto_rawDescGZIP(), []int{0} } func (x *ServerEndpoint) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *ServerEndpoint) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *ServerEndpoint) GetUser() []*User { if x != nil { return x.User } return nil } var File_common_protocol_server_spec_proto protoreflect.FileDescriptor const file_common_protocol_server_spec_proto_rawDesc = "" + "\n" + "!common/protocol/server_spec.proto\x12\x1av2ray.core.common.protocol\x1a\x18common/net/address.proto\x1a\x1acommon/protocol/user.proto\"\x97\x01\n" + "\x0eServerEndpoint\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x124\n" + "\x04user\x18\x03 \x03(\v2 .v2ray.core.common.protocol.UserR\x04userBo\n" + "\x1ecom.v2ray.core.common.protocolP\x01Z.github.com/v2fly/v2ray-core/v5/common/protocol\xaa\x02\x1aV2Ray.Core.Common.Protocolb\x06proto3" var ( file_common_protocol_server_spec_proto_rawDescOnce sync.Once file_common_protocol_server_spec_proto_rawDescData []byte ) func file_common_protocol_server_spec_proto_rawDescGZIP() []byte { file_common_protocol_server_spec_proto_rawDescOnce.Do(func() { file_common_protocol_server_spec_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_protocol_server_spec_proto_rawDesc), len(file_common_protocol_server_spec_proto_rawDesc))) }) return file_common_protocol_server_spec_proto_rawDescData } var file_common_protocol_server_spec_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_common_protocol_server_spec_proto_goTypes = []any{ (*ServerEndpoint)(nil), // 0: v2ray.core.common.protocol.ServerEndpoint (*net.IPOrDomain)(nil), // 1: v2ray.core.common.net.IPOrDomain (*User)(nil), // 2: v2ray.core.common.protocol.User } var file_common_protocol_server_spec_proto_depIdxs = []int32{ 1, // 0: v2ray.core.common.protocol.ServerEndpoint.address:type_name -> v2ray.core.common.net.IPOrDomain 2, // 1: v2ray.core.common.protocol.ServerEndpoint.user:type_name -> v2ray.core.common.protocol.User 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_common_protocol_server_spec_proto_init() } func file_common_protocol_server_spec_proto_init() { if File_common_protocol_server_spec_proto != nil { return } file_common_protocol_user_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_protocol_server_spec_proto_rawDesc), len(file_common_protocol_server_spec_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_protocol_server_spec_proto_goTypes, DependencyIndexes: file_common_protocol_server_spec_proto_depIdxs, MessageInfos: file_common_protocol_server_spec_proto_msgTypes, }.Build() File_common_protocol_server_spec_proto = out.File file_common_protocol_server_spec_proto_goTypes = nil file_common_protocol_server_spec_proto_depIdxs = nil } ================================================ FILE: common/protocol/server_spec.proto ================================================ syntax = "proto3"; package v2ray.core.common.protocol; option csharp_namespace = "V2Ray.Core.Common.Protocol"; option go_package = "github.com/v2fly/v2ray-core/v5/common/protocol"; option java_package = "com.v2ray.core.common.protocol"; option java_multiple_files = true; import "common/net/address.proto"; import "common/protocol/user.proto"; message ServerEndpoint { v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; repeated v2ray.core.common.protocol.User user = 3; } ================================================ FILE: common/protocol/server_spec_test.go ================================================ package protocol_test import ( "strings" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" . "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/vmess" ) func TestAlwaysValidStrategy(t *testing.T) { strategy := AlwaysValid() if !strategy.IsValid() { t.Error("strategy not valid") } strategy.Invalidate() if !strategy.IsValid() { t.Error("strategy not valid") } } func TestTimeoutValidStrategy(t *testing.T) { strategy := BeforeTime(time.Now().Add(2 * time.Second)) if !strategy.IsValid() { t.Error("strategy not valid") } time.Sleep(3 * time.Second) if strategy.IsValid() { t.Error("strategy is valid") } strategy = BeforeTime(time.Now().Add(2 * time.Second)) strategy.Invalidate() if strategy.IsValid() { t.Error("strategy is valid") } } func TestUserInServerSpec(t *testing.T) { uuid1 := uuid.New() uuid2 := uuid.New() toAccount := func(a *vmess.Account) Account { account, err := a.AsAccount() common.Must(err) return account } spec := NewServerSpec(net.Destination{}, AlwaysValid(), &MemoryUser{ Email: "test1@v2fly.org", Account: toAccount(&vmess.Account{Id: uuid1.String()}), }) if spec.HasUser(&MemoryUser{ Email: "test1@v2fly.org", Account: toAccount(&vmess.Account{Id: uuid2.String()}), }) { t.Error("has user: ", uuid2) } spec.AddUser(&MemoryUser{Email: "test2@v2fly.org"}) if !spec.HasUser(&MemoryUser{ Email: "test1@v2fly.org", Account: toAccount(&vmess.Account{Id: uuid1.String()}), }) { t.Error("not having user: ", uuid1) } } func TestPickUser(t *testing.T) { spec := NewServerSpec(net.Destination{}, AlwaysValid(), &MemoryUser{Email: "test1@v2fly.org"}, &MemoryUser{Email: "test2@v2fly.org"}, &MemoryUser{Email: "test3@v2fly.org"}) user := spec.PickUser() if !strings.HasSuffix(user.Email, "@v2fly.org") { t.Error("user: ", user.Email) } } ================================================ FILE: common/protocol/time.go ================================================ package protocol import ( "time" "github.com/v2fly/v2ray-core/v5/common/dice" ) type Timestamp int64 type TimestampGenerator func() Timestamp func NowTime() Timestamp { return Timestamp(time.Now().Unix()) } func NewTimestampGenerator(base Timestamp, delta int) TimestampGenerator { return func() Timestamp { rangeInDelta := dice.Roll(delta*2) - delta return base + Timestamp(rangeInDelta) } } ================================================ FILE: common/protocol/time_test.go ================================================ package protocol_test import ( "testing" "time" . "github.com/v2fly/v2ray-core/v5/common/protocol" ) func TestGenerateRandomInt64InRange(t *testing.T) { base := time.Now().Unix() delta := 100 generator := NewTimestampGenerator(Timestamp(base), delta) for i := 0; i < 100; i++ { val := int64(generator()) if val > base+int64(delta) || val < base-int64(delta) { t.Error(val, " not between ", base-int64(delta), " and ", base+int64(delta)) } } } ================================================ FILE: common/protocol/tls/cert/.gitignore ================================================ *.pem ================================================ FILE: common/protocol/tls/cert/cert.go ================================================ package cert import ( "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/asn1" "encoding/pem" "math/big" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type Certificate struct { // Certificate in ASN.1 DER format Certificate []byte // Private key in ASN.1 DER format PrivateKey []byte } func ParseCertificate(certPEM []byte, keyPEM []byte) (*Certificate, error) { certBlock, _ := pem.Decode(certPEM) if certBlock == nil { return nil, newError("failed to decode certificate") } keyBlock, _ := pem.Decode(keyPEM) if keyBlock == nil { return nil, newError("failed to decode key") } return &Certificate{ Certificate: certBlock.Bytes, PrivateKey: keyBlock.Bytes, }, nil } func (c *Certificate) ToPEM() ([]byte, []byte) { return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.Certificate}), pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: c.PrivateKey}) } type Option func(*x509.Certificate) func Authority(isCA bool) Option { return func(cert *x509.Certificate) { cert.IsCA = isCA } } func NotBefore(t time.Time) Option { return func(c *x509.Certificate) { c.NotBefore = t } } func NotAfter(t time.Time) Option { return func(c *x509.Certificate) { c.NotAfter = t } } func DNSNames(names ...string) Option { return func(c *x509.Certificate) { c.DNSNames = names } } func CommonName(name string) Option { return func(c *x509.Certificate) { c.Subject.CommonName = name } } func IPAddresses(ip ...net.IP) Option { return func(c *x509.Certificate) { c.IPAddresses = ip } } func KeyUsage(usage x509.KeyUsage) Option { return func(c *x509.Certificate) { c.KeyUsage = usage } } func Organization(org string) Option { return func(c *x509.Certificate) { c.Subject.Organization = []string{org} } } func MustGenerate(parent *Certificate, opts ...Option) *Certificate { cert, err := Generate(parent, opts...) common.Must(err) return cert } func publicKey(priv interface{}) interface{} { switch k := priv.(type) { case *rsa.PrivateKey: return &k.PublicKey case *ecdsa.PrivateKey: return &k.PublicKey case ed25519.PrivateKey: return k.Public().(ed25519.PublicKey) default: return nil } } func Generate(parent *Certificate, opts ...Option) (*Certificate, error) { var ( pKey interface{} parentKey interface{} err error ) // higher signing performance than RSA2048 selfKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, newError("failed to generate self private key").Base(err) } parentKey = selfKey if parent != nil { if _, e := asn1.Unmarshal(parent.PrivateKey, &ecPrivateKey{}); e == nil { pKey, err = x509.ParseECPrivateKey(parent.PrivateKey) } else if _, e := asn1.Unmarshal(parent.PrivateKey, &pkcs8{}); e == nil { pKey, err = x509.ParsePKCS8PrivateKey(parent.PrivateKey) } else if _, e := asn1.Unmarshal(parent.PrivateKey, &pkcs1PrivateKey{}); e == nil { pKey, err = x509.ParsePKCS1PrivateKey(parent.PrivateKey) } if err != nil { return nil, newError("failed to parse parent private key").Base(err) } parentKey = pKey } serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { return nil, newError("failed to generate serial number").Base(err) } template := &x509.Certificate{ SerialNumber: serialNumber, NotBefore: time.Now().Add(time.Hour * -1), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, } for _, opt := range opts { opt(template) } parentCert := template if parent != nil { pCert, err := x509.ParseCertificate(parent.Certificate) if err != nil { return nil, newError("failed to parse parent certificate").Base(err) } parentCert = pCert } derBytes, err := x509.CreateCertificate(rand.Reader, template, parentCert, publicKey(selfKey), parentKey) if err != nil { return nil, newError("failed to create certificate").Base(err) } privateKey, err := x509.MarshalPKCS8PrivateKey(selfKey) if err != nil { return nil, newError("Unable to marshal private key").Base(err) } return &Certificate{ Certificate: derBytes, PrivateKey: privateKey, }, nil } ================================================ FILE: common/protocol/tls/cert/cert_test.go ================================================ package cert import ( "context" "crypto/x509" "encoding/json" "os" "strings" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/task" ) func TestGenerate(t *testing.T) { err := generate(nil, true, true, "ca") if err != nil { t.Fatal(err) } } func generate(domainNames []string, isCA bool, jsonOutput bool, fileOutput string) error { commonName := "V2Ray Root CA" organization := "V2Ray Inc" expire := time.Hour * 3 var opts []Option if isCA { opts = append(opts, Authority(isCA)) opts = append(opts, KeyUsage(x509.KeyUsageCertSign|x509.KeyUsageKeyEncipherment|x509.KeyUsageDigitalSignature)) } opts = append(opts, NotAfter(time.Now().Add(expire))) opts = append(opts, CommonName(commonName)) if len(domainNames) > 0 { opts = append(opts, DNSNames(domainNames...)) } opts = append(opts, Organization(organization)) cert, err := Generate(nil, opts...) if err != nil { return newError("failed to generate TLS certificate").Base(err) } if jsonOutput { printJSON(cert) } if len(fileOutput) > 0 { if err := printFile(cert, fileOutput); err != nil { return err } } return nil } type jsonCert struct { Certificate []string `json:"certificate"` Key []string `json:"key"` } func printJSON(certificate *Certificate) { certPEM, keyPEM := certificate.ToPEM() jCert := &jsonCert{ Certificate: strings.Split(strings.TrimSpace(string(certPEM)), "\n"), Key: strings.Split(strings.TrimSpace(string(keyPEM)), "\n"), } content, err := json.MarshalIndent(jCert, "", " ") common.Must(err) os.Stdout.Write(content) os.Stdout.WriteString("\n") } func printFile(certificate *Certificate, name string) error { certPEM, keyPEM := certificate.ToPEM() return task.Run(context.Background(), func() error { return writeFile(certPEM, name+"_cert.pem") }, func() error { return writeFile(keyPEM, name+"_key.pem") }) } func writeFile(content []byte, name string) error { f, err := os.Create(name) if err != nil { return err } defer f.Close() return common.Error2(f.Write(content)) } ================================================ FILE: common/protocol/tls/cert/errors.generated.go ================================================ package cert import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/protocol/tls/cert/privateKey.go ================================================ package cert import ( "crypto/x509/pkix" "encoding/asn1" "math/big" ) type ecPrivateKey struct { Version int PrivateKey []byte NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"` PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"` } type pkcs8 struct { Version int Algo pkix.AlgorithmIdentifier PrivateKey []byte // optional attributes omitted. } type pkcs1AdditionalRSAPrime struct { Prime *big.Int // We ignore these values because rsa will calculate them. Exp *big.Int Coeff *big.Int } type pkcs1PrivateKey struct { Version int N *big.Int E int D *big.Int P *big.Int Q *big.Int // We ignore these values, if present, because rsa will calculate them. Dp *big.Int `asn1:"optional"` Dq *big.Int `asn1:"optional"` Qinv *big.Int `asn1:"optional"` AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"` } ================================================ FILE: common/protocol/tls/sniff.go ================================================ package tls import ( "encoding/binary" "errors" "strings" "github.com/v2fly/v2ray-core/v5/common" ) type SniffHeader struct { domain string } func (h *SniffHeader) Protocol() string { return "tls" } func (h *SniffHeader) Domain() string { return h.domain } var ( errNotTLS = errors.New("not TLS header") errNotClientHello = errors.New("not client hello") ) func IsValidTLSVersion(major, minor byte) bool { return major == 3 } // ReadClientHello returns server name (if any) from TLS client hello message. // https://github.com/golang/go/blob/master/src/crypto/tls/handshake_messages.go#L300 func ReadClientHello(data []byte, h *SniffHeader) error { if len(data) < 42 { return common.ErrNoClue } sessionIDLen := int(data[38]) if sessionIDLen > 32 || len(data) < 39+sessionIDLen { return common.ErrNoClue } data = data[39+sessionIDLen:] if len(data) < 2 { return common.ErrNoClue } // cipherSuiteLen is the number of bytes of cipher suite numbers. Since // they are uint16s, the number must be even. cipherSuiteLen := int(data[0])<<8 | int(data[1]) if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { return errNotClientHello } data = data[2+cipherSuiteLen:] if len(data) < 1 { return common.ErrNoClue } compressionMethodsLen := int(data[0]) if len(data) < 1+compressionMethodsLen { return common.ErrNoClue } data = data[1+compressionMethodsLen:] if len(data) == 0 { return errNotClientHello } if len(data) < 2 { return errNotClientHello } extensionsLength := int(data[0])<<8 | int(data[1]) data = data[2:] if extensionsLength != len(data) { return errNotClientHello } for len(data) != 0 { if len(data) < 4 { return errNotClientHello } extension := uint16(data[0])<<8 | uint16(data[1]) length := int(data[2])<<8 | int(data[3]) data = data[4:] if len(data) < length { return errNotClientHello } if extension == 0x00 { /* extensionServerName */ d := data[:length] if len(d) < 2 { return errNotClientHello } namesLen := int(d[0])<<8 | int(d[1]) d = d[2:] if len(d) != namesLen { return errNotClientHello } for len(d) > 0 { if len(d) < 3 { return errNotClientHello } nameType := d[0] nameLen := int(d[1])<<8 | int(d[2]) d = d[3:] if len(d) < nameLen { return errNotClientHello } if nameType == 0 { serverName := string(d[:nameLen]) // An SNI value may not include a // trailing dot. See // https://tools.ietf.org/html/rfc6066#section-3. if strings.HasSuffix(serverName, ".") { return errNotClientHello } h.domain = serverName return nil } d = d[nameLen:] } } data = data[length:] } return errNotTLS } func SniffTLS(b []byte) (*SniffHeader, error) { if len(b) < 5 { return nil, common.ErrNoClue } if b[0] != 0x16 /* TLS Handshake */ { return nil, errNotTLS } if !IsValidTLSVersion(b[1], b[2]) { return nil, errNotTLS } headerLen := int(binary.BigEndian.Uint16(b[3:5])) if 5+headerLen > len(b) { return nil, common.ErrNoClue } h := &SniffHeader{} err := ReadClientHello(b[5:5+headerLen], h) if err == nil { return h, nil } return nil, err } ================================================ FILE: common/protocol/tls/sniff_test.go ================================================ package tls_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/protocol/tls" ) func TestTLSHeaders(t *testing.T) { cases := []struct { input []byte domain string err bool }{ { input: []byte{ 0x16, 0x03, 0x01, 0x00, 0xc8, 0x01, 0x00, 0x00, 0xc4, 0x03, 0x03, 0x1a, 0xac, 0xb2, 0xa8, 0xfe, 0xb4, 0x96, 0x04, 0x5b, 0xca, 0xf7, 0xc1, 0xf4, 0x2e, 0x53, 0x24, 0x6e, 0x34, 0x0c, 0x58, 0x36, 0x71, 0x97, 0x59, 0xe9, 0x41, 0x66, 0xe2, 0x43, 0xa0, 0x13, 0xb6, 0x00, 0x00, 0x20, 0x1a, 0x1a, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x7b, 0xba, 0xba, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x14, 0x00, 0x00, 0x11, 0x63, 0x2e, 0x73, 0x2d, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0xaa, 0xaa, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0xaa, 0xaa, 0x00, 0x01, 0x00, }, domain: "c.s-microsoft.com", err: false, }, { input: []byte{ 0x16, 0x03, 0x01, 0x00, 0xee, 0x01, 0x00, 0x00, 0xea, 0x03, 0x03, 0xe7, 0x91, 0x9e, 0x93, 0xca, 0x78, 0x1b, 0x3c, 0xe0, 0x65, 0x25, 0x58, 0xb5, 0x93, 0xe1, 0x0f, 0x85, 0xec, 0x9a, 0x66, 0x8e, 0x61, 0x82, 0x88, 0xc8, 0xfc, 0xae, 0x1e, 0xca, 0xd7, 0xa5, 0x63, 0x20, 0xbd, 0x1c, 0x00, 0x00, 0x8b, 0xee, 0x09, 0xe3, 0x47, 0x6a, 0x0e, 0x74, 0xb0, 0xbc, 0xa3, 0x02, 0xa7, 0x35, 0xe8, 0x85, 0x70, 0x7c, 0x7a, 0xf0, 0x00, 0xdf, 0x4a, 0xea, 0x87, 0x01, 0x14, 0x91, 0x00, 0x20, 0xea, 0xea, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x81, 0x9a, 0x9a, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x16, 0x00, 0x00, 0x13, 0x77, 0x77, 0x77, 0x30, 0x37, 0x2e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x74, 0x61, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x75, 0x50, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x9a, 0x9a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x8a, 0x8a, 0x00, 0x01, 0x00, }, domain: "www07.clicktale.net", err: false, }, { input: []byte{ 0x16, 0x03, 0x01, 0x00, 0xe6, 0x01, 0x00, 0x00, 0xe2, 0x03, 0x03, 0x81, 0x47, 0xc1, 0x66, 0xd5, 0x1b, 0xfa, 0x4b, 0xb5, 0xe0, 0x2a, 0xe1, 0xa7, 0x87, 0x13, 0x1d, 0x11, 0xaa, 0xc6, 0xce, 0xfc, 0x7f, 0xab, 0x94, 0xc8, 0x62, 0xad, 0xc8, 0xab, 0x0c, 0xdd, 0xcb, 0x20, 0x6f, 0x9d, 0x07, 0xf1, 0x95, 0x3e, 0x99, 0xd8, 0xf3, 0x6d, 0x97, 0xee, 0x19, 0x0b, 0x06, 0x1b, 0xf4, 0x84, 0x0b, 0xb6, 0x8f, 0xcc, 0xde, 0xe2, 0xd0, 0x2d, 0x6b, 0x0c, 0x1f, 0x52, 0x53, 0x13, 0x00, 0x08, 0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0x00, 0xff, 0x01, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x00, 0x07, 0x64, 0x6f, 0x67, 0x66, 0x69, 0x73, 0x68, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x00, 0x2b, 0x00, 0x07, 0x06, 0x7f, 0x1c, 0x7f, 0x1b, 0x7f, 0x1a, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x2f, 0x35, 0x0c, 0xb6, 0x90, 0x0a, 0xb7, 0xd5, 0xc4, 0x1b, 0x2f, 0x60, 0xaa, 0x56, 0x7b, 0x3f, 0x71, 0xc8, 0x01, 0x7e, 0x86, 0xd3, 0xb7, 0x0c, 0x29, 0x1a, 0x9e, 0x5b, 0x38, 0x3f, 0x01, 0x72, }, domain: "dogfish", err: false, }, { input: []byte{ 0x16, 0x03, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00, 0xff, 0x03, 0x03, 0x3d, 0x89, 0x52, 0x9e, 0xee, 0xbe, 0x17, 0x63, 0x75, 0xef, 0x29, 0xbd, 0x14, 0x6a, 0x49, 0xe0, 0x2c, 0x37, 0x57, 0x71, 0x62, 0x82, 0x44, 0x94, 0x8f, 0x6e, 0x94, 0x08, 0x45, 0x7f, 0xdb, 0xc1, 0x00, 0x00, 0x3e, 0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, 0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x31, 0x30, 0x2e, 0x34, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x34, 0x33, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x7f, 0x14, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x2d, 0x00, 0x03, 0x02, 0x01, 0x00, 0x00, 0x28, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x13, 0x7c, 0x6e, 0x97, 0xc4, 0xfd, 0x09, 0x2e, 0x70, 0x2f, 0x73, 0x5a, 0x9b, 0x57, 0x4d, 0x5f, 0x2b, 0x73, 0x2c, 0xa5, 0x4a, 0x98, 0x40, 0x3d, 0x75, 0x6e, 0xb4, 0x76, 0xf9, 0x48, 0x8f, 0x36, }, domain: "10.42.0.243", err: false, }, } for _, test := range cases { header, err := SniffTLS(test.input) if test.err { if err == nil { t.Errorf("Exepct error but nil in test %v", test) } } else { if err != nil { t.Errorf("Expect no error but actually %s in test %v", err.Error(), test) } if header.Domain() != test.domain { t.Error("expect domain ", test.domain, " but got ", header.Domain()) } } } } ================================================ FILE: common/protocol/udp/packet.go ================================================ package udp import ( "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" ) // Packet is a UDP packet together with its source and destination address. type Packet struct { Payload *buf.Buffer Source net.Destination Target net.Destination } ================================================ FILE: common/protocol/udp/udp.go ================================================ package udp ================================================ FILE: common/protocol/user.go ================================================ package protocol import "github.com/v2fly/v2ray-core/v5/common/serial" func (u *User) GetTypedAccount() (Account, error) { if u.GetAccount() == nil { return nil, newError("Account missing").AtWarning() } rawAccount, err := serial.GetInstanceOf(u.Account) if err != nil { return nil, err } if asAccount, ok := rawAccount.(AsAccount); ok { return asAccount.AsAccount() } if account, ok := rawAccount.(Account); ok { return account, nil } return nil, newError("Unknown account type: ", serial.V2Type(u.Account)) } func (u *User) ToMemoryUser() (*MemoryUser, error) { account, err := u.GetTypedAccount() if err != nil { return nil, err } return &MemoryUser{ Account: account, Email: u.Email, Level: u.Level, }, nil } // MemoryUser is a parsed form of User, to reduce number of parsing of Account proto. type MemoryUser struct { // Account is the parsed account of the protocol. Account Account Email string Level uint32 } ================================================ FILE: common/protocol/user.pb.go ================================================ package protocol import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // User is a generic user for all procotols. type User struct { state protoimpl.MessageState `protogen:"open.v1"` Level uint32 `protobuf:"varint,1,opt,name=level,proto3" json:"level,omitempty"` Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` // Protocol specific account information. Must be the account proto in one of // the proxies. Account *anypb.Any `protobuf:"bytes,3,opt,name=account,proto3" json:"account,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *User) Reset() { *x = User{} mi := &file_common_protocol_user_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *User) String() string { return protoimpl.X.MessageStringOf(x) } func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { mi := &file_common_protocol_user_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use User.ProtoReflect.Descriptor instead. func (*User) Descriptor() ([]byte, []int) { return file_common_protocol_user_proto_rawDescGZIP(), []int{0} } func (x *User) GetLevel() uint32 { if x != nil { return x.Level } return 0 } func (x *User) GetEmail() string { if x != nil { return x.Email } return "" } func (x *User) GetAccount() *anypb.Any { if x != nil { return x.Account } return nil } var File_common_protocol_user_proto protoreflect.FileDescriptor const file_common_protocol_user_proto_rawDesc = "" + "\n" + "\x1acommon/protocol/user.proto\x12\x1av2ray.core.common.protocol\x1a\x19google/protobuf/any.proto\"b\n" + "\x04User\x12\x14\n" + "\x05level\x18\x01 \x01(\rR\x05level\x12\x14\n" + "\x05email\x18\x02 \x01(\tR\x05email\x12.\n" + "\aaccount\x18\x03 \x01(\v2\x14.google.protobuf.AnyR\aaccountBo\n" + "\x1ecom.v2ray.core.common.protocolP\x01Z.github.com/v2fly/v2ray-core/v5/common/protocol\xaa\x02\x1aV2Ray.Core.Common.Protocolb\x06proto3" var ( file_common_protocol_user_proto_rawDescOnce sync.Once file_common_protocol_user_proto_rawDescData []byte ) func file_common_protocol_user_proto_rawDescGZIP() []byte { file_common_protocol_user_proto_rawDescOnce.Do(func() { file_common_protocol_user_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_protocol_user_proto_rawDesc), len(file_common_protocol_user_proto_rawDesc))) }) return file_common_protocol_user_proto_rawDescData } var file_common_protocol_user_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_common_protocol_user_proto_goTypes = []any{ (*User)(nil), // 0: v2ray.core.common.protocol.User (*anypb.Any)(nil), // 1: google.protobuf.Any } var file_common_protocol_user_proto_depIdxs = []int32{ 1, // 0: v2ray.core.common.protocol.User.account:type_name -> google.protobuf.Any 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_common_protocol_user_proto_init() } func file_common_protocol_user_proto_init() { if File_common_protocol_user_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_protocol_user_proto_rawDesc), len(file_common_protocol_user_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_protocol_user_proto_goTypes, DependencyIndexes: file_common_protocol_user_proto_depIdxs, MessageInfos: file_common_protocol_user_proto_msgTypes, }.Build() File_common_protocol_user_proto = out.File file_common_protocol_user_proto_goTypes = nil file_common_protocol_user_proto_depIdxs = nil } ================================================ FILE: common/protocol/user.proto ================================================ syntax = "proto3"; package v2ray.core.common.protocol; option csharp_namespace = "V2Ray.Core.Common.Protocol"; option go_package = "github.com/v2fly/v2ray-core/v5/common/protocol"; option java_package = "com.v2ray.core.common.protocol"; option java_multiple_files = true; import "google/protobuf/any.proto"; // User is a generic user for all procotols. message User { uint32 level = 1; string email = 2; // Protocol specific account information. Must be the account proto in one of // the proxies. google.protobuf.Any account = 3; } ================================================ FILE: common/protoext/errors.generated.go ================================================ package protoext import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/protoext/extensions.go ================================================ package protoext import ( "github.com/golang/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/descriptorpb" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func GetMessageOptions(msgDesc protoreflect.MessageDescriptor) (*MessageOpt, error) { msgOpt := msgDesc.Options().(*descriptorpb.MessageOptions) msgOptRet, err := proto.GetExtension(msgOpt, E_MessageOpt) if err != nil { return nil, newError("unable to parse extension from message").Base(err) } return msgOptRet.(*MessageOpt), nil } func GetFieldOptions(fieldDesc protoreflect.FieldDescriptor) (*FieldOpt, error) { fieldOpt := fieldDesc.Options().(*descriptorpb.FieldOptions) msgOptRet, err := proto.GetExtension(fieldOpt, E_FieldOpt) if err != nil { return nil, newError("unable to parse extension from message").Base(err) } return msgOptRet.(*FieldOpt), nil } ================================================ FILE: common/protoext/extensions.pb.go ================================================ package protoext import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" descriptorpb "google.golang.org/protobuf/types/descriptorpb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type MessageOpt struct { state protoimpl.MessageState `protogen:"open.v1"` Type []string `protobuf:"bytes,1,rep,name=type,proto3" json:"type,omitempty"` ShortName []string `protobuf:"bytes,2,rep,name=short_name,json=shortName,proto3" json:"short_name,omitempty"` TransportOriginalName string `protobuf:"bytes,86001,opt,name=transport_original_name,json=transportOriginalName,proto3" json:"transport_original_name,omitempty"` // allow_restricted_mode_load allow this config to be loaded in restricted mode // this is typically used when a an attacker can control the content AllowRestrictedModeLoad bool `protobuf:"varint,86002,opt,name=allow_restricted_mode_load,json=allowRestrictedModeLoad,proto3" json:"allow_restricted_mode_load,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MessageOpt) Reset() { *x = MessageOpt{} mi := &file_common_protoext_extensions_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MessageOpt) String() string { return protoimpl.X.MessageStringOf(x) } func (*MessageOpt) ProtoMessage() {} func (x *MessageOpt) ProtoReflect() protoreflect.Message { mi := &file_common_protoext_extensions_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MessageOpt.ProtoReflect.Descriptor instead. func (*MessageOpt) Descriptor() ([]byte, []int) { return file_common_protoext_extensions_proto_rawDescGZIP(), []int{0} } func (x *MessageOpt) GetType() []string { if x != nil { return x.Type } return nil } func (x *MessageOpt) GetShortName() []string { if x != nil { return x.ShortName } return nil } func (x *MessageOpt) GetTransportOriginalName() string { if x != nil { return x.TransportOriginalName } return "" } func (x *MessageOpt) GetAllowRestrictedModeLoad() bool { if x != nil { return x.AllowRestrictedModeLoad } return false } type FieldOpt struct { state protoimpl.MessageState `protogen:"open.v1"` AnyWants []string `protobuf:"bytes,1,rep,name=any_wants,json=anyWants,proto3" json:"any_wants,omitempty"` AllowedValues []string `protobuf:"bytes,2,rep,name=allowed_values,json=allowedValues,proto3" json:"allowed_values,omitempty"` AllowedValueTypes []string `protobuf:"bytes,3,rep,name=allowed_value_types,json=allowedValueTypes,proto3" json:"allowed_value_types,omitempty"` // convert_time_read_file_into read a file into another field, and clear this field during input parsing ConvertTimeReadFileInto string `protobuf:"bytes,4,opt,name=convert_time_read_file_into,json=convertTimeReadFileInto,proto3" json:"convert_time_read_file_into,omitempty"` // forbidden marks a boolean to be inaccessible to user Forbidden bool `protobuf:"varint,5,opt,name=forbidden,proto3" json:"forbidden,omitempty"` // convert_time_resource_loading read a file, and place its resource hash into another field ConvertTimeResourceLoading string `protobuf:"bytes,6,opt,name=convert_time_resource_loading,json=convertTimeResourceLoading,proto3" json:"convert_time_resource_loading,omitempty"` // convert_time_parse_ip parse a string ip address, and put its binary representation into another field ConvertTimeParseIp string `protobuf:"bytes,7,opt,name=convert_time_parse_ip,json=convertTimeParseIp,proto3" json:"convert_time_parse_ip,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FieldOpt) Reset() { *x = FieldOpt{} mi := &file_common_protoext_extensions_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FieldOpt) String() string { return protoimpl.X.MessageStringOf(x) } func (*FieldOpt) ProtoMessage() {} func (x *FieldOpt) ProtoReflect() protoreflect.Message { mi := &file_common_protoext_extensions_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FieldOpt.ProtoReflect.Descriptor instead. func (*FieldOpt) Descriptor() ([]byte, []int) { return file_common_protoext_extensions_proto_rawDescGZIP(), []int{1} } func (x *FieldOpt) GetAnyWants() []string { if x != nil { return x.AnyWants } return nil } func (x *FieldOpt) GetAllowedValues() []string { if x != nil { return x.AllowedValues } return nil } func (x *FieldOpt) GetAllowedValueTypes() []string { if x != nil { return x.AllowedValueTypes } return nil } func (x *FieldOpt) GetConvertTimeReadFileInto() string { if x != nil { return x.ConvertTimeReadFileInto } return "" } func (x *FieldOpt) GetForbidden() bool { if x != nil { return x.Forbidden } return false } func (x *FieldOpt) GetConvertTimeResourceLoading() string { if x != nil { return x.ConvertTimeResourceLoading } return "" } func (x *FieldOpt) GetConvertTimeParseIp() string { if x != nil { return x.ConvertTimeParseIp } return "" } var file_common_protoext_extensions_proto_extTypes = []protoimpl.ExtensionInfo{ { ExtendedType: (*descriptorpb.MessageOptions)(nil), ExtensionType: (*MessageOpt)(nil), Field: 50000, Name: "v2ray.core.common.protoext.message_opt", Tag: "bytes,50000,opt,name=message_opt", Filename: "common/protoext/extensions.proto", }, { ExtendedType: (*descriptorpb.FieldOptions)(nil), ExtensionType: (*FieldOpt)(nil), Field: 50000, Name: "v2ray.core.common.protoext.field_opt", Tag: "bytes,50000,opt,name=field_opt", Filename: "common/protoext/extensions.proto", }, } // Extension fields to descriptorpb.MessageOptions. var ( // optional v2ray.core.common.protoext.MessageOpt message_opt = 50000; E_MessageOpt = &file_common_protoext_extensions_proto_extTypes[0] ) // Extension fields to descriptorpb.FieldOptions. var ( // optional v2ray.core.common.protoext.FieldOpt field_opt = 50000; E_FieldOpt = &file_common_protoext_extensions_proto_extTypes[1] ) var File_common_protoext_extensions_proto protoreflect.FileDescriptor const file_common_protoext_extensions_proto_rawDesc = "" + "\n" + " common/protoext/extensions.proto\x12\x1av2ray.core.common.protoext\x1a google/protobuf/descriptor.proto\"\xb8\x01\n" + "\n" + "MessageOpt\x12\x12\n" + "\x04type\x18\x01 \x03(\tR\x04type\x12\x1d\n" + "\n" + "short_name\x18\x02 \x03(\tR\tshortName\x128\n" + "\x17transport_original_name\x18\xf1\x9f\x05 \x01(\tR\x15transportOriginalName\x12=\n" + "\x1aallow_restricted_mode_load\x18\xf2\x9f\x05 \x01(\bR\x17allowRestrictedModeLoad\"\xd0\x02\n" + "\bFieldOpt\x12\x1b\n" + "\tany_wants\x18\x01 \x03(\tR\banyWants\x12%\n" + "\x0eallowed_values\x18\x02 \x03(\tR\rallowedValues\x12.\n" + "\x13allowed_value_types\x18\x03 \x03(\tR\x11allowedValueTypes\x12<\n" + "\x1bconvert_time_read_file_into\x18\x04 \x01(\tR\x17convertTimeReadFileInto\x12\x1c\n" + "\tforbidden\x18\x05 \x01(\bR\tforbidden\x12A\n" + "\x1dconvert_time_resource_loading\x18\x06 \x01(\tR\x1aconvertTimeResourceLoading\x121\n" + "\x15convert_time_parse_ip\x18\a \x01(\tR\x12convertTimeParseIp:j\n" + "\vmessage_opt\x12\x1f.google.protobuf.MessageOptions\x18І\x03 \x01(\v2&.v2ray.core.common.protoext.MessageOptR\n" + "messageOpt:b\n" + "\tfield_opt\x12\x1d.google.protobuf.FieldOptions\x18І\x03 \x01(\v2$.v2ray.core.common.protoext.FieldOptR\bfieldOptBo\n" + "\x1ecom.v2ray.core.common.protoextP\x01Z.github.com/v2fly/v2ray-core/v5/common/protoext\xaa\x02\x1aV2Ray.Core.Common.ProtoExtb\x06proto3" var ( file_common_protoext_extensions_proto_rawDescOnce sync.Once file_common_protoext_extensions_proto_rawDescData []byte ) func file_common_protoext_extensions_proto_rawDescGZIP() []byte { file_common_protoext_extensions_proto_rawDescOnce.Do(func() { file_common_protoext_extensions_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_protoext_extensions_proto_rawDesc), len(file_common_protoext_extensions_proto_rawDesc))) }) return file_common_protoext_extensions_proto_rawDescData } var file_common_protoext_extensions_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_common_protoext_extensions_proto_goTypes = []any{ (*MessageOpt)(nil), // 0: v2ray.core.common.protoext.MessageOpt (*FieldOpt)(nil), // 1: v2ray.core.common.protoext.FieldOpt (*descriptorpb.MessageOptions)(nil), // 2: google.protobuf.MessageOptions (*descriptorpb.FieldOptions)(nil), // 3: google.protobuf.FieldOptions } var file_common_protoext_extensions_proto_depIdxs = []int32{ 2, // 0: v2ray.core.common.protoext.message_opt:extendee -> google.protobuf.MessageOptions 3, // 1: v2ray.core.common.protoext.field_opt:extendee -> google.protobuf.FieldOptions 0, // 2: v2ray.core.common.protoext.message_opt:type_name -> v2ray.core.common.protoext.MessageOpt 1, // 3: v2ray.core.common.protoext.field_opt:type_name -> v2ray.core.common.protoext.FieldOpt 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 2, // [2:4] is the sub-list for extension type_name 0, // [0:2] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_common_protoext_extensions_proto_init() } func file_common_protoext_extensions_proto_init() { if File_common_protoext_extensions_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_protoext_extensions_proto_rawDesc), len(file_common_protoext_extensions_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 2, NumServices: 0, }, GoTypes: file_common_protoext_extensions_proto_goTypes, DependencyIndexes: file_common_protoext_extensions_proto_depIdxs, MessageInfos: file_common_protoext_extensions_proto_msgTypes, ExtensionInfos: file_common_protoext_extensions_proto_extTypes, }.Build() File_common_protoext_extensions_proto = out.File file_common_protoext_extensions_proto_goTypes = nil file_common_protoext_extensions_proto_depIdxs = nil } ================================================ FILE: common/protoext/extensions.proto ================================================ syntax = "proto3"; package v2ray.core.common.protoext; option csharp_namespace = "V2Ray.Core.Common.ProtoExt"; option go_package = "github.com/v2fly/v2ray-core/v5/common/protoext"; option java_package = "com.v2ray.core.common.protoext"; option java_multiple_files = true; import "google/protobuf/descriptor.proto"; extend google.protobuf.MessageOptions { MessageOpt message_opt = 50000; } extend google.protobuf.FieldOptions { FieldOpt field_opt = 50000; } message MessageOpt{ repeated string type = 1; repeated string short_name = 2; string transport_original_name = 86001; // allow_restricted_mode_load allow this config to be loaded in restricted mode // this is typically used when a an attacker can control the content bool allow_restricted_mode_load = 86002; } message FieldOpt{ repeated string any_wants = 1; repeated string allowed_values = 2; repeated string allowed_value_types = 3; // convert_time_read_file_into read a file into another field, and clear this field during input parsing string convert_time_read_file_into = 4; // forbidden marks a boolean to be inaccessible to user bool forbidden = 5; // convert_time_resource_loading read a file, and place its resource hash into another field string convert_time_resource_loading = 6; // convert_time_parse_ip parse a string ip address, and put its binary representation into another field string convert_time_parse_ip = 7; } ================================================ FILE: common/protoext/testing/extension_test.go ================================================ package testing import ( "testing" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/reflect/protoreflect" "github.com/v2fly/v2ray-core/v5/common/protoext" ) func TestMessageOpt(t *testing.T) { msg := TestingMessage{} opt, err := protoext.GetMessageOptions(msg.ProtoReflect().Descriptor()) assert.Nil(t, err) assert.EqualValues(t, []string{"demo", "demo2"}, opt.Type) } func TestFieldOpt(t *testing.T) { msg := TestingMessage{ TestField: "Test", } msgreflect := msg.ProtoReflect() msgreflect.Range(func(descriptor protoreflect.FieldDescriptor, value protoreflect.Value) bool { opt, err := protoext.GetFieldOptions(descriptor) assert.Nil(t, err) assert.EqualValues(t, []string{"test", "test2"}, opt.AllowedValues) return true }) } ================================================ FILE: common/protoext/testing/test.pb.go ================================================ package testing import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type TestingMessage struct { state protoimpl.MessageState `protogen:"open.v1"` TestField string `protobuf:"bytes,1,opt,name=test_field,json=testField,proto3" json:"test_field,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TestingMessage) Reset() { *x = TestingMessage{} mi := &file_common_protoext_testing_test_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TestingMessage) String() string { return protoimpl.X.MessageStringOf(x) } func (*TestingMessage) ProtoMessage() {} func (x *TestingMessage) ProtoReflect() protoreflect.Message { mi := &file_common_protoext_testing_test_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TestingMessage.ProtoReflect.Descriptor instead. func (*TestingMessage) Descriptor() ([]byte, []int) { return file_common_protoext_testing_test_proto_rawDescGZIP(), []int{0} } func (x *TestingMessage) GetTestField() string { if x != nil { return x.TestField } return "" } var File_common_protoext_testing_test_proto protoreflect.FileDescriptor const file_common_protoext_testing_test_proto_rawDesc = "" + "\n" + "\"common/protoext/testing/test.proto\x12\"v2ray.core.common.protoext.testing\x1a common/protoext/extensions.proto\"U\n" + "\x0eTestingMessage\x120\n" + "\n" + "test_field\x18\x01 \x01(\tB\x11\x82\xb5\x18\r\x12\x04test\x12\x05test2R\ttestField:\x11\x82\xb5\x18\r\n" + "\x04demo\n" + "\x05demo2B\x84\x01\n" + "&com.v2ray.core.common.protoext.testingP\x01Z3github.com/v2fly/v2ray-core/common/protoext/testing\xaa\x02\"V2Ray.Core.Common.ProtoExt.Testingb\x06proto3" var ( file_common_protoext_testing_test_proto_rawDescOnce sync.Once file_common_protoext_testing_test_proto_rawDescData []byte ) func file_common_protoext_testing_test_proto_rawDescGZIP() []byte { file_common_protoext_testing_test_proto_rawDescOnce.Do(func() { file_common_protoext_testing_test_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_protoext_testing_test_proto_rawDesc), len(file_common_protoext_testing_test_proto_rawDesc))) }) return file_common_protoext_testing_test_proto_rawDescData } var file_common_protoext_testing_test_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_common_protoext_testing_test_proto_goTypes = []any{ (*TestingMessage)(nil), // 0: v2ray.core.common.protoext.testing.TestingMessage } var file_common_protoext_testing_test_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_common_protoext_testing_test_proto_init() } func file_common_protoext_testing_test_proto_init() { if File_common_protoext_testing_test_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_protoext_testing_test_proto_rawDesc), len(file_common_protoext_testing_test_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_protoext_testing_test_proto_goTypes, DependencyIndexes: file_common_protoext_testing_test_proto_depIdxs, MessageInfos: file_common_protoext_testing_test_proto_msgTypes, }.Build() File_common_protoext_testing_test_proto = out.File file_common_protoext_testing_test_proto_goTypes = nil file_common_protoext_testing_test_proto_depIdxs = nil } ================================================ FILE: common/protoext/testing/test.proto ================================================ syntax = "proto3"; package v2ray.core.common.protoext.testing; option csharp_namespace = "V2Ray.Core.Common.ProtoExt.Testing"; option go_package = "github.com/v2fly/v2ray-core/common/protoext/testing"; option java_package = "com.v2ray.core.common.protoext.testing"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message TestingMessage{ option (v2ray.core.common.protoext.message_opt).type = "demo"; option (v2ray.core.common.protoext.message_opt).type = "demo2"; string test_field = 1 [(v2ray.core.common.protoext.field_opt).allowed_values = "test", (v2ray.core.common.protoext.field_opt).allowed_values = "test2"]; } ================================================ FILE: common/protofilter/errors.generated.go ================================================ package protofilter import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/protofilter/filter.go ================================================ package protofilter import ( "context" "io" "net" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/environment/filesystemcap" "github.com/v2fly/v2ray-core/v5/common/protoext" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func FilterProtoConfig(ctx context.Context, config proto.Message) error { messageProtoReflect := config.ProtoReflect() return filterMessage(ctx, messageProtoReflect) } func filterMessage(ctx context.Context, message protoreflect.Message) error { var err error type fileRead struct { filename string field string } var fileReadingQueue []fileRead type pendingWrite struct { field protoreflect.FieldDescriptor value protoreflect.Value } var pendingWriteQueue []pendingWrite message.Range(func(descriptor protoreflect.FieldDescriptor, value protoreflect.Value) bool { v2extension, ferr := protoext.GetFieldOptions(descriptor) if ferr == nil { if v2extension.Forbidden { if value.Bool() { err = newError("a forbidden value is set ", descriptor.FullName()) return false } } if v2extension.ConvertTimeReadFileInto != "" { fileReadingQueue = append(fileReadingQueue, fileRead{ filename: value.String(), field: v2extension.ConvertTimeReadFileInto, }) } if v2extension.ConvertTimeParseIp != "" { ipValue := net.ParseIP(value.String()) target := message.Descriptor().Fields().ByTextName(v2extension.ConvertTimeParseIp) if ipValue.To4() != nil { ipValue = ipValue.To4() } pendingWriteQueue = append(pendingWriteQueue, pendingWrite{ field: target, value: protoreflect.ValueOf([]byte(ipValue)), }) } } switch descriptor.Kind() { case protoreflect.MessageKind: if descriptor.IsMap() { err = filterMap(ctx, value.Map()) break } if descriptor.IsList() { err = filterList(ctx, value.List()) break } err = filterMessage(ctx, value.Message()) } return true }) if err != nil { return err } fsenvironment := envctx.EnvironmentFromContext(ctx) fsifce, fsifceOk := fsenvironment.(filesystemcap.FileSystemCapabilitySet) for _, v := range fileReadingQueue { if !fsifceOk { return newError("unable to read file as filesystem capability is not given") } field := message.Descriptor().Fields().ByTextName(v.field) if v.filename == "" { continue } if len(message.Get(field).Bytes()) > 0 { continue } file, err := fsifce.OpenFileForRead()(v.filename) if err != nil { return newError("unable to open file").Base(err) } fileContent, err := io.ReadAll(file) if err != nil { return newError("unable to read file").Base(err) } file.Close() message.Set(field, protoreflect.ValueOf(fileContent)) } for _, v := range pendingWriteQueue { message.Set(v.field, v.value) } return nil } func filterMap(ctx context.Context, mapValue protoreflect.Map) error { var err error mapValue.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool { err = filterMessage(ctx, value.Message()) return err == nil }) return err } func filterList(ctx context.Context, listValue protoreflect.List) error { var err error size := listValue.Len() for i := 0; i < size; i++ { err = filterMessage(ctx, listValue.Get(i).Message()) if err != nil { return err } } return nil } ================================================ FILE: common/registry/errors.generated.go ================================================ package registry import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/registry/implementation_set.go ================================================ package registry import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/protoext" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type implementationSet struct { AliasLookup map[string]*implementation } type CustomLoader func(data []byte, loader LoadByAlias) (proto.Message, error) type implementation struct { FullName string Alias []string Loader CustomLoader } func (i *implementationSet) RegisterImplementation(name string, opt *protoext.MessageOpt, loader CustomLoader) { alias := opt.GetShortName() impl := &implementation{ FullName: name, Alias: alias, } for _, aliasName := range alias { i.AliasLookup[aliasName] = impl } } func (i *implementationSet) findImplementationByAlias(alias string) (string, CustomLoader, error) { impl, found := i.AliasLookup[alias] if found { return impl.FullName, impl.Loader, nil } return "", nil, newError("cannot find implementation by alias: ", alias) } func newImplementationSet() *implementationSet { return &implementationSet{AliasLookup: map[string]*implementation{}} } ================================================ FILE: common/registry/registry.go ================================================ package registry import ( "bytes" "context" "reflect" "strings" "sync" "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/proto" protov2 "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/protoext" "github.com/v2fly/v2ray-core/v5/common/protofilter" "github.com/v2fly/v2ray-core/v5/common/serial" ) type implementationRegistry struct { implSet map[string]*implementationSet } func (i *implementationRegistry) RegisterImplementation(name string, opt *protoext.MessageOpt, loader CustomLoader) { interfaceType := opt.GetType() for _, v := range interfaceType { i.registerSingleImplementation(v, name, opt, loader) } } func (i *implementationRegistry) registerSingleImplementation(interfaceType, name string, opt *protoext.MessageOpt, loader CustomLoader) { implSet, found := i.implSet[interfaceType] if !found { implSet = newImplementationSet() i.implSet[interfaceType] = implSet } implSet.RegisterImplementation(name, opt, loader) } func (i *implementationRegistry) findImplementationByAlias(interfaceType, alias string) (string, CustomLoader, error) { implSet, found := i.implSet[interfaceType] if !found { return "", nil, newError("cannot find implemention unknown interface type") } return implSet.findImplementationByAlias(alias) } func (i *implementationRegistry) LoadImplementationByAlias(ctx context.Context, interfaceType, alias string, data []byte) (proto.Message, error) { var implementationFullName string if strings.HasPrefix(alias, "#") { // skip resolution for full name implementationFullName, _ = strings.CutPrefix(alias, "#") } else { registryResult, customLoader, err := i.findImplementationByAlias(interfaceType, alias) if err != nil { return nil, newError("unable to find implementation").Base(err) } if customLoader != nil { return customLoader(data, i) } implementationFullName = registryResult } implementationConfigInstance, err := serial.GetInstance(implementationFullName) if err != nil { return nil, newError("unable to create implementation config instance").Base(err) } unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: false} err = unmarshaler.Unmarshal(bytes.NewReader(data), implementationConfigInstance.(proto.Message)) if err != nil { return nil, newError("unable to parse json content").Base(err) } implementationConfigInstancev2 := proto.MessageV2(implementationConfigInstance) if isRestrictedModeContext(ctx) { if err := enforceRestriction(implementationConfigInstancev2); err != nil { return nil, err } } if err := protofilter.FilterProtoConfig(ctx, implementationConfigInstancev2); err != nil { return nil, err } return implementationConfigInstance.(proto.Message), nil } func newImplementationRegistry() *implementationRegistry { return &implementationRegistry{implSet: map[string]*implementationSet{}} } var globalImplementationRegistry = newImplementationRegistry() var initialized = &sync.Once{} type registerRequest struct { proto interface{} loader CustomLoader } var registerRequests []registerRequest // RegisterImplementation register an implementation of a type of interface // loader(CustomLoader) is a private API, its interface is subject to breaking changes func RegisterImplementation(proto interface{}, loader CustomLoader) error { registerRequests = append(registerRequests, registerRequest{ proto: proto, loader: loader, }) return nil } func registerImplementation(proto interface{}, loader CustomLoader) error { protoReflect := reflect.New(reflect.TypeOf(proto).Elem()) proto2 := protoReflect.Interface().(protov2.Message) msgDesc := proto2.ProtoReflect().Descriptor() fullName := string(msgDesc.FullName()) msgOpts, err := protoext.GetMessageOptions(msgDesc) if err != nil { return newError("unable to find message options").Base(err) } globalImplementationRegistry.RegisterImplementation(fullName, msgOpts, loader) return nil } type LoadByAlias interface { LoadImplementationByAlias(ctx context.Context, interfaceType, alias string, data []byte) (proto.Message, error) } func LoadImplementationByAlias(ctx context.Context, interfaceType, alias string, data []byte) (proto.Message, error) { initialized.Do(func() { for _, v := range registerRequests { registerImplementation(v.proto, v.loader) } }) return globalImplementationRegistry.LoadImplementationByAlias(ctx, interfaceType, alias, data) } ================================================ FILE: common/registry/restrict.go ================================================ package registry import ( "context" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/protoext" ) const restrictedLoadModeCtx = "restrictedLoadModeCtx" func CreateRestrictedModeContext(ctx context.Context) context.Context { return context.WithValue(ctx, restrictedLoadModeCtx, true) //nolint: staticcheck } func isRestrictedModeContext(ctx context.Context) bool { v := ctx.Value(restrictedLoadModeCtx) if v == nil { return false } return v.(bool) } func enforceRestriction(config proto.Message) error { configDescriptor := config.ProtoReflect().Descriptor() msgOpts, err := protoext.GetMessageOptions(configDescriptor) if err != nil { return newError("unable to find message options").Base(err) } if !msgOpts.AllowRestrictedModeLoad { return newError("component has not opted in for load in restricted mode") } return nil } ================================================ FILE: common/retry/errors.generated.go ================================================ package retry import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/retry/retry.go ================================================ package retry //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "time" ) var ErrRetryFailed = newError("all retry attempts failed") // Strategy is a way to retry on a specific function. type Strategy interface { // On performs a retry on a specific function, until it doesn't return any error. On(func() error) error } type retryer struct { totalAttempt int nextDelay func() uint32 } // On implements Strategy.On. func (r *retryer) On(method func() error) error { attempt := 0 accumulatedError := make([]error, 0, r.totalAttempt) for attempt < r.totalAttempt { err := method() if err == nil { return nil } numErrors := len(accumulatedError) if numErrors == 0 || err.Error() != accumulatedError[numErrors-1].Error() { accumulatedError = append(accumulatedError, err) } delay := r.nextDelay() time.Sleep(time.Duration(delay) * time.Millisecond) attempt++ } return newError(accumulatedError).Base(ErrRetryFailed) } // Timed returns a retry strategy with fixed interval. func Timed(attempts int, delay uint32) Strategy { return &retryer{ totalAttempt: attempts, nextDelay: func() uint32 { return delay }, } } func ExponentialBackoff(attempts int, delay uint32) Strategy { nextDelay := uint32(0) return &retryer{ totalAttempt: attempts, nextDelay: func() uint32 { r := nextDelay nextDelay += delay return r }, } } ================================================ FILE: common/retry/retry_test.go ================================================ package retry_test import ( "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" . "github.com/v2fly/v2ray-core/v5/common/retry" ) var errorTestOnly = errors.New("this is a fake error") func TestNoRetry(t *testing.T) { startTime := time.Now().Unix() err := Timed(10, 100000).On(func() error { return nil }) endTime := time.Now().Unix() common.Must(err) if endTime < startTime { t.Error("endTime < startTime: ", startTime, " -> ", endTime) } } func TestRetryOnce(t *testing.T) { startTime := time.Now() called := 0 err := Timed(10, 1000).On(func() error { if called == 0 { called++ return errorTestOnly } return nil }) duration := time.Since(startTime) common.Must(err) if v := int64(duration / time.Millisecond); v < 900 { t.Error("duration: ", v) } } func TestRetryMultiple(t *testing.T) { startTime := time.Now() called := 0 err := Timed(10, 1000).On(func() error { if called < 5 { called++ return errorTestOnly } return nil }) duration := time.Since(startTime) common.Must(err) if v := int64(duration / time.Millisecond); v < 4900 { t.Error("duration: ", v) } } func TestRetryExhausted(t *testing.T) { startTime := time.Now() called := 0 err := Timed(2, 1000).On(func() error { called++ return errorTestOnly }) duration := time.Since(startTime) if errors.Cause(err) != ErrRetryFailed { t.Error("cause: ", err) } if v := int64(duration / time.Millisecond); v < 1900 { t.Error("duration: ", v) } } func TestExponentialBackoff(t *testing.T) { startTime := time.Now() called := 0 err := ExponentialBackoff(10, 100).On(func() error { called++ return errorTestOnly }) duration := time.Since(startTime) if errors.Cause(err) != ErrRetryFailed { t.Error("cause: ", err) } if v := int64(duration / time.Millisecond); v < 4000 { t.Error("duration: ", v) } } ================================================ FILE: common/serial/resolver.go ================================================ package serial import ( "github.com/golang/protobuf/proto" ) type AnyResolver interface { Resolve(typeURL string) (proto.Message, error) } type serialResolver struct{} func (s serialResolver) Resolve(typeURL string) (proto.Message, error) { instance, err := GetInstance(typeURL) if err != nil { return nil, err } return instance.(proto.Message), nil } func GetResolver() AnyResolver { return &serialResolver{} } ================================================ FILE: common/serial/serial.go ================================================ package serial import ( "encoding/binary" "io" ) // ReadUint16 reads first two bytes from the reader, and then coverts them to an uint16 value. func ReadUint16(reader io.Reader) (uint16, error) { var b [2]byte if _, err := io.ReadFull(reader, b[:]); err != nil { return 0, err } return binary.BigEndian.Uint16(b[:]), nil } // WriteUint16 writes an uint16 value into writer. func WriteUint16(writer io.Writer, value uint16) (int, error) { var b [2]byte binary.BigEndian.PutUint16(b[:], value) return writer.Write(b[:]) } // WriteUint64 writes an uint64 value into writer. func WriteUint64(writer io.Writer, value uint64) (int, error) { var b [8]byte binary.BigEndian.PutUint64(b[:], value) return writer.Write(b[:]) } ================================================ FILE: common/serial/serial_test.go ================================================ package serial_test import ( "bytes" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/serial" ) func TestUint16Serial(t *testing.T) { b := buf.New() defer b.Release() n, err := serial.WriteUint16(b, 10) common.Must(err) if n != 2 { t.Error("expect 2 bytes writtng, but actually ", n) } if diff := cmp.Diff(b.Bytes(), []byte{0, 10}); diff != "" { t.Error(diff) } } func TestUint64Serial(t *testing.T) { b := buf.New() defer b.Release() n, err := serial.WriteUint64(b, 10) common.Must(err) if n != 8 { t.Error("expect 8 bytes writtng, but actually ", n) } if diff := cmp.Diff(b.Bytes(), []byte{0, 0, 0, 0, 0, 0, 0, 10}); diff != "" { t.Error(diff) } } func TestReadUint16(t *testing.T) { testCases := []struct { Input []byte Output uint16 }{ { Input: []byte{0, 1}, Output: 1, }, } for _, testCase := range testCases { v, err := serial.ReadUint16(bytes.NewReader(testCase.Input)) common.Must(err) if v != testCase.Output { t.Error("for input ", testCase.Input, " expect output ", testCase.Output, " but got ", v) } } } func BenchmarkReadUint16(b *testing.B) { reader := buf.New() defer reader.Release() common.Must2(reader.Write([]byte{0, 1})) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := serial.ReadUint16(reader) common.Must(err) reader.Clear() reader.Extend(2) } } func BenchmarkWriteUint64(b *testing.B) { writer := buf.New() defer writer.Release() b.ResetTimer() for i := 0; i < b.N; i++ { _, err := serial.WriteUint64(writer, 8) common.Must(err) writer.Clear() } } ================================================ FILE: common/serial/string.go ================================================ package serial import ( "fmt" "strings" ) // ToString serialize an arbitrary value into string. func ToString(v interface{}) string { if v == nil { return "" } switch value := v.(type) { case string: return value case *string: return *value case fmt.Stringer: return value.String() case error: return value.Error() default: return fmt.Sprintf("%+v", value) } } // Concat concatenates all input into a single string. func Concat(v ...interface{}) string { builder := strings.Builder{} for _, value := range v { builder.WriteString(ToString(value)) } return builder.String() } ================================================ FILE: common/serial/string_test.go ================================================ package serial_test import ( "errors" "testing" "github.com/google/go-cmp/cmp" . "github.com/v2fly/v2ray-core/v5/common/serial" ) func TestToString(t *testing.T) { s := "a" data := []struct { Value interface{} String string }{ {Value: s, String: s}, {Value: &s, String: s}, {Value: errors.New("t"), String: "t"}, {Value: []byte{'b', 'c'}, String: "[98 99]"}, } for _, c := range data { if r := cmp.Diff(ToString(c.Value), c.String); r != "" { t.Error(r) } } } func TestConcat(t *testing.T) { testCases := []struct { Input []interface{} Output string }{ { Input: []interface{}{ "a", "b", }, Output: "ab", }, } for _, testCase := range testCases { actual := Concat(testCase.Input...) if actual != testCase.Output { t.Error("Unexpected output: ", actual, " but want: ", testCase.Output) } } } func BenchmarkConcat(b *testing.B) { input := []interface{}{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"} b.ReportAllocs() for i := 0; i < b.N; i++ { _ = Concat(input...) } } ================================================ FILE: common/serial/typed_message.go ================================================ package serial import ( "errors" "reflect" "strings" "github.com/golang/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" ) const V2RayTypeURLHeader = "types.v2fly.org/" // ToTypedMessage converts a proto Message into TypedMessage. func ToTypedMessage(message proto.Message) *anypb.Any { if message == nil { return nil } settings, _ := proto.Marshal(message) return &anypb.Any{ TypeUrl: V2RayTypeURLHeader + GetMessageType(message), Value: settings, } } // GetMessageType returns the name of this proto Message. func GetMessageType(message proto.Message) string { return proto.MessageName(message) } // GetInstance creates a new instance of the message with messageType. func GetInstance(messageType string) (interface{}, error) { mType := proto.MessageType(messageType) if mType == nil || mType.Elem() == nil { return nil, errors.New("Serial: Unknown type: " + messageType) } return reflect.New(mType.Elem()).Interface(), nil } func GetInstanceOf(v *anypb.Any) (proto.Message, error) { instance, err := GetInstance(V2TypeFromURL(v.TypeUrl)) if err != nil { return nil, err } protoMessage := instance.(proto.Message) if err := proto.Unmarshal(v.Value, protoMessage); err != nil { return nil, err } return protoMessage, nil } func V2Type(v *anypb.Any) string { return V2TypeFromURL(v.TypeUrl) } func V2TypeFromURL(string2 string) string { return strings.TrimPrefix(string2, V2RayTypeURLHeader) } func V2TypeHumanReadable(v *anypb.Any) string { return v.TypeUrl } func V2URLFromV2Type(readableType string) string { return readableType } ================================================ FILE: common/serial/typed_message_test.go ================================================ package serial_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/serial" ) func TestGetInstance(t *testing.T) { p, err := GetInstance("") if p != nil { t.Error("expected nil instance, but got ", p) } if err == nil { t.Error("expect non-nil error, but got nil") } } func TestConvertingNilMessage(t *testing.T) { x := ToTypedMessage(nil) if x != nil { t.Error("expect nil, but actually not") } } ================================================ FILE: common/session/context.go ================================================ package session import ( "context" ) type sessionKey int const ( idSessionKey sessionKey = iota inboundSessionKey outboundSessionKey contentSessionKey muxPreferedSessionKey sockoptSessionKey trackedConnectionErrorKey handlerSessionKey // nolint: varcheck ) // ContextWithID returns a new context with the given ID. func ContextWithID(ctx context.Context, id ID) context.Context { return context.WithValue(ctx, idSessionKey, id) } // IDFromContext returns ID in this context, or 0 if not contained. func IDFromContext(ctx context.Context) ID { if id, ok := ctx.Value(idSessionKey).(ID); ok { return id } return 0 } func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context { return context.WithValue(ctx, inboundSessionKey, inbound) } func InboundFromContext(ctx context.Context) *Inbound { if inbound, ok := ctx.Value(inboundSessionKey).(*Inbound); ok { return inbound } return nil } func ContextWithOutbound(ctx context.Context, outbound *Outbound) context.Context { return context.WithValue(ctx, outboundSessionKey, outbound) } func OutboundFromContext(ctx context.Context) *Outbound { if outbound, ok := ctx.Value(outboundSessionKey).(*Outbound); ok { return outbound } return nil } func ContextWithContent(ctx context.Context, content *Content) context.Context { return context.WithValue(ctx, contentSessionKey, content) } func ContentFromContext(ctx context.Context) *Content { if content, ok := ctx.Value(contentSessionKey).(*Content); ok { return content } return nil } // ContextWithMuxPrefered returns a new context with the given bool func ContextWithMuxPrefered(ctx context.Context, forced bool) context.Context { return context.WithValue(ctx, muxPreferedSessionKey, forced) } // MuxPreferedFromContext returns value in this context, or false if not contained. func MuxPreferedFromContext(ctx context.Context) bool { if val, ok := ctx.Value(muxPreferedSessionKey).(bool); ok { return val } return false } // ContextWithSockopt returns a new context with Socket configs included func ContextWithSockopt(ctx context.Context, s *Sockopt) context.Context { return context.WithValue(ctx, sockoptSessionKey, s) } // SockoptFromContext returns Socket configs in this context, or nil if not contained. func SockoptFromContext(ctx context.Context) *Sockopt { if sockopt, ok := ctx.Value(sockoptSessionKey).(*Sockopt); ok { return sockopt } return nil } func GetTransportLayerProxyTagFromContext(ctx context.Context) string { if ContentFromContext(ctx) == nil { return "" } return ContentFromContext(ctx).Attribute("transportLayerOutgoingTag") } func SetTransportLayerProxyTagToContext(ctx context.Context, tag string) context.Context { if contentFromContext := ContentFromContext(ctx); contentFromContext == nil { ctx = ContextWithContent(ctx, &Content{}) } ContentFromContext(ctx).SetAttribute("transportLayerOutgoingTag", tag) return ctx } func GetForcedOutboundTagFromContext(ctx context.Context) string { if ContentFromContext(ctx) == nil { return "" } return ContentFromContext(ctx).Attribute("forcedOutboundTag") } func SetForcedOutboundTagToContext(ctx context.Context, tag string) context.Context { if contentFromContext := ContentFromContext(ctx); contentFromContext == nil { ctx = ContextWithContent(ctx, &Content{}) } ContentFromContext(ctx).SetAttribute("forcedOutboundTag", tag) return ctx } type TrackedRequestErrorFeedback interface { SubmitError(err error) } func SubmitOutboundErrorToOriginator(ctx context.Context, err error) { if errorTracker := ctx.Value(trackedConnectionErrorKey); errorTracker != nil { errorTracker := errorTracker.(TrackedRequestErrorFeedback) errorTracker.SubmitError(err) } } func TrackedConnectionError(ctx context.Context, tracker TrackedRequestErrorFeedback) context.Context { return context.WithValue(ctx, trackedConnectionErrorKey, tracker) } ================================================ FILE: common/session/session.go ================================================ // Package session provides functions for sessions of incoming requests. package session import ( "context" "math/rand" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) // ID of a session. type ID uint32 // NewID generates a new ID. The generated ID is high likely to be unique, but not cryptographically secure. // The generated ID will never be 0. func NewID() ID { for { id := ID(rand.Uint32()) if id != 0 { return id } } } // ExportIDToError transfers session.ID into an error object, for logging purpose. // This can be used with error.WriteToLog(). func ExportIDToError(ctx context.Context) errors.ExportOption { id := IDFromContext(ctx) return func(h *errors.ExportOptionHolder) { h.SessionID = uint32(id) } } // Inbound is the metadata of an inbound connection. type Inbound struct { // Source address of the inbound connection. Source net.Destination // Gateway address Gateway net.Destination // Tag of the inbound proxy that handles the connection. Tag string // User is the user that authencates for the inbound. May be nil if the protocol allows anounymous traffic. User *protocol.MemoryUser } // Outbound is the metadata of an outbound connection. type Outbound struct { // Target address of the outbound connection. Target net.Destination // Gateway address Gateway net.Address // Domain resolver to use when dialing Resolver func(ctx context.Context, domain string) net.Address } // SniffingRequest controls the behavior of content sniffing. type SniffingRequest struct { OverrideDestinationForProtocol []string Enabled bool MetadataOnly bool } // Content is the metadata of the connection content. type Content struct { // Protocol of current content. Protocol string SniffingRequest SniffingRequest Attributes map[string]string SkipDNSResolve bool } // Sockopt is the settings for socket connection. type Sockopt struct { // Mark of the socket connection. Mark uint32 } // SetAttribute attachs additional string attributes to content. func (c *Content) SetAttribute(name string, value string) { if c.Attributes == nil { c.Attributes = make(map[string]string) } c.Attributes[name] = value } // Attribute retrieves additional string attributes from content. func (c *Content) Attribute(name string) string { if c.Attributes == nil { return "" } return c.Attributes[name] } ================================================ FILE: common/signal/done/done.go ================================================ package done import ( "sync" ) // Instance is a utility for notifications of something being done. type Instance struct { access sync.Mutex c chan struct{} closed bool } // New returns a new Done. func New() *Instance { return &Instance{ c: make(chan struct{}), } } // Done returns true if Close() is called. func (d *Instance) Done() bool { select { case <-d.Wait(): return true default: return false } } // Wait returns a channel for waiting for done. func (d *Instance) Wait() <-chan struct{} { return d.c } // Close marks this Done 'done'. This method may be called multiple times. All calls after first call will have no effect on its status. func (d *Instance) Close() error { d.access.Lock() defer d.access.Unlock() if d.closed { return nil } d.closed = true close(d.c) return nil } ================================================ FILE: common/signal/notifier.go ================================================ package signal // Notifier is a utility for notifying changes. The change producer may notify changes multiple time, and the consumer may get notified asynchronously. type Notifier struct { c chan struct{} } // NewNotifier creates a new Notifier. func NewNotifier() *Notifier { return &Notifier{ c: make(chan struct{}, 1), } } // Signal signals a change, usually by producer. This method never blocks. func (n *Notifier) Signal() { select { case n.c <- struct{}{}: default: } } // Wait returns a channel for waiting for changes. The returned channel never gets closed. func (n *Notifier) Wait() <-chan struct{} { return n.c } ================================================ FILE: common/signal/notifier_test.go ================================================ package signal_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/signal" ) func TestNotifierSignal(t *testing.T) { n := NewNotifier() w := n.Wait() n.Signal() select { case <-w: default: t.Fail() } } ================================================ FILE: common/signal/pubsub/pubsub.go ================================================ package pubsub import ( "errors" "sync" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/common/task" ) type Subscriber struct { buffer chan interface{} done *done.Instance } func (s *Subscriber) push(msg interface{}) { select { case s.buffer <- msg: default: } } func (s *Subscriber) Wait() <-chan interface{} { return s.buffer } func (s *Subscriber) Close() error { return s.done.Close() } func (s *Subscriber) IsClosed() bool { return s.done.Done() } type Service struct { sync.RWMutex subs map[string][]*Subscriber ctask *task.Periodic } func NewService() *Service { s := &Service{ subs: make(map[string][]*Subscriber), } s.ctask = &task.Periodic{ Execute: s.Cleanup, Interval: time.Second * 30, } return s } // Cleanup cleans up internal caches of subscribers. // Visible for testing only. func (s *Service) Cleanup() error { s.Lock() defer s.Unlock() if len(s.subs) == 0 { return errors.New("nothing to do") } for name, subs := range s.subs { newSub := make([]*Subscriber, 0, len(s.subs)) for _, sub := range subs { if !sub.IsClosed() { newSub = append(newSub, sub) } } if len(newSub) == 0 { delete(s.subs, name) } else { s.subs[name] = newSub } } if len(s.subs) == 0 { s.subs = make(map[string][]*Subscriber) } return nil } func (s *Service) Subscribe(name string) *Subscriber { sub := &Subscriber{ buffer: make(chan interface{}, 16), done: done.New(), } s.Lock() s.subs[name] = append(s.subs[name], sub) s.Unlock() common.Must(s.ctask.Start()) return sub } func (s *Service) Publish(name string, message interface{}) { s.RLock() defer s.RUnlock() for _, sub := range s.subs[name] { if !sub.IsClosed() { sub.push(message) } } } ================================================ FILE: common/signal/pubsub/pubsub_test.go ================================================ package pubsub_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/signal/pubsub" ) func TestPubsub(t *testing.T) { service := NewService() sub := service.Subscribe("a") service.Publish("a", 1) select { case v := <-sub.Wait(): if v != 1 { t.Error("expected subscribed value 1, but got ", v) } default: t.Fail() } sub.Close() service.Publish("a", 2) select { case <-sub.Wait(): t.Fail() default: } service.Cleanup() } ================================================ FILE: common/signal/semaphore/semaphore.go ================================================ package semaphore // Instance is an implementation of semaphore. type Instance struct { token chan struct{} } // New create a new Semaphore with n permits. func New(n int) *Instance { s := &Instance{ token: make(chan struct{}, n), } for i := 0; i < n; i++ { s.token <- struct{}{} } return s } // Wait returns a channel for acquiring a permit. func (s *Instance) Wait() <-chan struct{} { return s.token } // Signal releases a permit into the semaphore. func (s *Instance) Signal() { s.token <- struct{}{} } ================================================ FILE: common/signal/timer.go ================================================ package signal import ( "context" "sync" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/task" ) type ActivityUpdater interface { Update() } type ActivityTimer struct { sync.RWMutex updated chan struct{} checkTask *task.Periodic onTimeout func() } func (t *ActivityTimer) Update() { select { case t.updated <- struct{}{}: default: } } func (t *ActivityTimer) check() error { select { case <-t.updated: default: t.finish() } return nil } func (t *ActivityTimer) finish() { t.Lock() defer t.Unlock() if t.onTimeout != nil { t.onTimeout() t.onTimeout = nil } if t.checkTask != nil { t.checkTask.Close() t.checkTask = nil } } func (t *ActivityTimer) SetTimeout(timeout time.Duration) { if timeout == 0 { t.finish() return } checkTask := &task.Periodic{ Interval: timeout, Execute: t.check, } t.Lock() if t.checkTask != nil { t.checkTask.Close() } t.checkTask = checkTask t.Unlock() t.Update() common.Must(checkTask.Start()) } func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer { timer := &ActivityTimer{ updated: make(chan struct{}, 1), onTimeout: cancel, } timer.SetTimeout(timeout) return timer } ================================================ FILE: common/signal/timer_test.go ================================================ package signal_test import ( "context" "runtime" "testing" "time" . "github.com/v2fly/v2ray-core/v5/common/signal" ) func TestActivityTimer(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) timer := CancelAfterInactivity(ctx, cancel, time.Second*4) time.Sleep(time.Second * 6) if ctx.Err() == nil { t.Error("expected some error, but got nil") } runtime.KeepAlive(timer) } func TestActivityTimerUpdate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) timer := CancelAfterInactivity(ctx, cancel, time.Second*10) time.Sleep(time.Second * 3) if ctx.Err() != nil { t.Error("expected nil, but got ", ctx.Err().Error()) } timer.SetTimeout(time.Second * 1) time.Sleep(time.Second * 2) if ctx.Err() == nil { t.Error("expected some error, but got nil") } runtime.KeepAlive(timer) } func TestActivityTimerNonBlocking(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) timer := CancelAfterInactivity(ctx, cancel, 0) time.Sleep(time.Second * 1) select { case <-ctx.Done(): default: t.Error("context not done") } timer.SetTimeout(0) timer.SetTimeout(1) timer.SetTimeout(2) } func TestActivityTimerZeroTimeout(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) timer := CancelAfterInactivity(ctx, cancel, 0) select { case <-ctx.Done(): default: t.Error("context not done") } runtime.KeepAlive(timer) } ================================================ FILE: common/strmatcher/benchmark_indexmatcher_test.go ================================================ package strmatcher_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func BenchmarkLinearIndexMatcher(b *testing.B) { benchmarkIndexMatcher(b, func() IndexMatcher { return NewLinearIndexMatcher() }) } func BenchmarkMphIndexMatcher(b *testing.B) { benchmarkIndexMatcher(b, func() IndexMatcher { return NewMphIndexMatcher() }) } func benchmarkIndexMatcher(b *testing.B, ctor func() IndexMatcher) { b.Run("Match", func(b *testing.B) { b.Run("Domain------------", func(b *testing.B) { benchmarkMatch(b, ctor(), map[Type]bool{Domain: true}) }) b.Run("Domain+Full-------", func(b *testing.B) { benchmarkMatch(b, ctor(), map[Type]bool{Domain: true, Full: true}) }) b.Run("Domain+Full+Substr", func(b *testing.B) { benchmarkMatch(b, ctor(), map[Type]bool{Domain: true, Full: true, Substr: true}) }) b.Run("All-Fail----------", func(b *testing.B) { benchmarkMatch(b, ctor(), map[Type]bool{Domain: false, Full: false, Substr: false}) }) }) b.Run("Match/Dotless", func(b *testing.B) { // Dotless domain matcher automatically inserted in DNS app when "localhost" DNS is used. b.Run("All-Succ", func(b *testing.B) { benchmarkMatch(b, ctor(), map[Type]bool{Domain: true, Full: true, Substr: true, Regex: true}) }) b.Run("All-Fail", func(b *testing.B) { benchmarkMatch(b, ctor(), map[Type]bool{Domain: false, Full: false, Substr: false, Regex: false}) }) }) b.Run("MatchAny", func(b *testing.B) { b.Run("First-Full--", func(b *testing.B) { benchmarkMatchAny(b, ctor(), map[Type]bool{Full: true, Domain: true, Substr: true}) }) b.Run("First-Domain", func(b *testing.B) { benchmarkMatchAny(b, ctor(), map[Type]bool{Full: false, Domain: true, Substr: true}) }) b.Run("First-Substr", func(b *testing.B) { benchmarkMatchAny(b, ctor(), map[Type]bool{Full: false, Domain: false, Substr: true}) }) b.Run("All-Fail----", func(b *testing.B) { benchmarkMatchAny(b, ctor(), map[Type]bool{Full: false, Domain: false, Substr: false}) }) }) } ================================================ FILE: common/strmatcher/benchmark_matchers_test.go ================================================ package strmatcher_test import ( "strconv" "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func BenchmarkFullMatcher(b *testing.B) { b.Run("SimpleMatcherGroup------", func(b *testing.B) { benchmarkMatcherType(b, Full, func() MatcherGroup { return new(SimpleMatcherGroup) }) }) b.Run("FullMatcherGroup--------", func(b *testing.B) { benchmarkMatcherType(b, Full, func() MatcherGroup { return NewFullMatcherGroup() }) }) b.Run("ACAutomationMatcherGroup", func(b *testing.B) { benchmarkMatcherType(b, Full, func() MatcherGroup { return NewACAutomatonMatcherGroup() }) }) b.Run("MphMatcherGroup---------", func(b *testing.B) { benchmarkMatcherType(b, Full, func() MatcherGroup { return NewMphMatcherGroup() }) }) } func BenchmarkDomainMatcher(b *testing.B) { b.Run("SimpleMatcherGroup------", func(b *testing.B) { benchmarkMatcherType(b, Domain, func() MatcherGroup { return new(SimpleMatcherGroup) }) }) b.Run("DomainMatcherGroup------", func(b *testing.B) { benchmarkMatcherType(b, Domain, func() MatcherGroup { return NewDomainMatcherGroup() }) }) b.Run("ACAutomationMatcherGroup", func(b *testing.B) { benchmarkMatcherType(b, Domain, func() MatcherGroup { return NewACAutomatonMatcherGroup() }) }) b.Run("MphMatcherGroup---------", func(b *testing.B) { benchmarkMatcherType(b, Domain, func() MatcherGroup { return NewMphMatcherGroup() }) }) } func BenchmarkSubstrMatcher(b *testing.B) { b.Run("SimpleMatcherGroup------", func(b *testing.B) { benchmarkMatcherType(b, Substr, func() MatcherGroup { return new(SimpleMatcherGroup) }) }) b.Run("SubstrMatcherGroup------", func(b *testing.B) { benchmarkMatcherType(b, Substr, func() MatcherGroup { return new(SubstrMatcherGroup) }) }) b.Run("ACAutomationMatcherGroup", func(b *testing.B) { benchmarkMatcherType(b, Substr, func() MatcherGroup { return NewACAutomatonMatcherGroup() }) }) } // Utility functions for benchmark func benchmarkMatcherType(b *testing.B, t Type, ctor func() MatcherGroup) { b.Run("Match", func(b *testing.B) { b.Run("Succ", func(b *testing.B) { benchmarkMatch(b, ctor(), map[Type]bool{t: true}) }) b.Run("Fail", func(b *testing.B) { benchmarkMatch(b, ctor(), map[Type]bool{t: false}) }) }) b.Run("MatchAny", func(b *testing.B) { b.Run("Succ", func(b *testing.B) { benchmarkMatchAny(b, ctor(), map[Type]bool{t: true}) }) b.Run("Fail", func(b *testing.B) { benchmarkMatchAny(b, ctor(), map[Type]bool{t: false}) }) }) } func benchmarkMatch(b *testing.B, g MatcherGroup, enabledTypes map[Type]bool) { prepareMatchers(g, enabledTypes) b.ResetTimer() for i := 0; i < b.N; i++ { _ = g.Match("0.v2fly.org") } } func benchmarkMatchAny(b *testing.B, g MatcherGroup, enabledTypes map[Type]bool) { prepareMatchers(g, enabledTypes) b.ResetTimer() for i := 0; i < b.N; i++ { _ = g.MatchAny("0.v2fly.org") } } func prepareMatchers(g MatcherGroup, enabledTypes map[Type]bool) { for matcherType, hasMatch := range enabledTypes { switch matcherType { case Domain: if hasMatch { AddMatcherToGroup(g, DomainMatcher("v2fly.org"), 0) } for i := 1; i < 1024; i++ { AddMatcherToGroup(g, DomainMatcher(strconv.Itoa(i)+".v2fly.org"), uint32(i)) } case Full: if hasMatch { AddMatcherToGroup(g, FullMatcher("0.v2fly.org"), 0) } for i := 1; i < 64; i++ { AddMatcherToGroup(g, FullMatcher(strconv.Itoa(i)+".v2fly.org"), uint32(i)) } case Substr: if hasMatch { AddMatcherToGroup(g, SubstrMatcher("v2fly.org"), 0) } for i := 1; i < 4; i++ { AddMatcherToGroup(g, SubstrMatcher(strconv.Itoa(i)+".v2fly.org"), uint32(i)) } case Regex: matcher, err := Regex.New("^[^.]*$") // Dotless domain matcher automatically inserted in DNS app when "localhost" DNS is used. common.Must(err) AddMatcherToGroup(g, matcher, 0) } } if g, ok := g.(buildable); ok { common.Must(g.Build()) } } type buildable interface { Build() error } ================================================ FILE: common/strmatcher/indexmatcher_linear.go ================================================ package strmatcher // LinearIndexMatcher is an implementation of IndexMatcher. type LinearIndexMatcher struct { count uint32 full *FullMatcherGroup domain *DomainMatcherGroup substr *SubstrMatcherGroup regex *SimpleMatcherGroup } func NewLinearIndexMatcher() *LinearIndexMatcher { return new(LinearIndexMatcher) } // Add implements IndexMatcher.Add. func (g *LinearIndexMatcher) Add(matcher Matcher) uint32 { g.count++ index := g.count switch matcher := matcher.(type) { case FullMatcher: if g.full == nil { g.full = NewFullMatcherGroup() } g.full.AddFullMatcher(matcher, index) case DomainMatcher: if g.domain == nil { g.domain = NewDomainMatcherGroup() } g.domain.AddDomainMatcher(matcher, index) case SubstrMatcher: if g.substr == nil { g.substr = new(SubstrMatcherGroup) } g.substr.AddSubstrMatcher(matcher, index) default: if g.regex == nil { g.regex = new(SimpleMatcherGroup) } g.regex.AddMatcher(matcher, index) } return index } // Build implements IndexMatcher.Build. func (*LinearIndexMatcher) Build() error { return nil } // Match implements IndexMatcher.Match. func (g *LinearIndexMatcher) Match(input string) []uint32 { // Allocate capacity to prevent matches escaping to heap result := make([][]uint32, 0, 5) if g.full != nil { if matches := g.full.Match(input); len(matches) > 0 { result = append(result, matches) } } if g.domain != nil { if matches := g.domain.Match(input); len(matches) > 0 { result = append(result, matches) } } if g.substr != nil { if matches := g.substr.Match(input); len(matches) > 0 { result = append(result, matches) } } if g.regex != nil { if matches := g.regex.Match(input); len(matches) > 0 { result = append(result, matches) } } return CompositeMatches(result) } // MatchAny implements IndexMatcher.MatchAny. func (g *LinearIndexMatcher) MatchAny(input string) bool { if g.full != nil && g.full.MatchAny(input) { return true } if g.domain != nil && g.domain.MatchAny(input) { return true } if g.substr != nil && g.substr.MatchAny(input) { return true } return g.regex != nil && g.regex.MatchAny(input) } // Size implements IndexMatcher.Size. func (g *LinearIndexMatcher) Size() uint32 { return g.count } ================================================ FILE: common/strmatcher/indexmatcher_linear_test.go ================================================ package strmatcher_test import ( "reflect" "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) // See https://github.com/v2fly/v2ray-core/issues/92#issuecomment-673238489 func TestLinearIndexMatcher(t *testing.T) { rules := []struct { Type Type Domain string }{ { Type: Regex, Domain: "apis\\.us$", }, { Type: Substr, Domain: "apis", }, { Type: Domain, Domain: "googleapis.com", }, { Type: Domain, Domain: "com", }, { Type: Full, Domain: "www.baidu.com", }, { Type: Substr, Domain: "apis", }, { Type: Domain, Domain: "googleapis.com", }, { Type: Full, Domain: "fonts.googleapis.com", }, { Type: Full, Domain: "www.baidu.com", }, { Type: Domain, Domain: "example.com", }, } cases := []struct { Input string Output []uint32 }{ { Input: "www.baidu.com", Output: []uint32{5, 9, 4}, }, { Input: "fonts.googleapis.com", Output: []uint32{8, 3, 7, 4, 2, 6}, }, { Input: "example.googleapis.com", Output: []uint32{3, 7, 4, 2, 6}, }, { Input: "testapis.us", Output: []uint32{2, 6, 1}, }, { Input: "example.com", Output: []uint32{10, 4}, }, } matcherGroup := NewLinearIndexMatcher() for _, rule := range rules { matcher, err := rule.Type.New(rule.Domain) common.Must(err) matcherGroup.Add(matcher) } matcherGroup.Build() for _, test := range cases { if m := matcherGroup.Match(test.Input); !reflect.DeepEqual(m, test.Output) { t.Error("unexpected output: ", m, " for test case ", test) } } } ================================================ FILE: common/strmatcher/indexmatcher_mph.go ================================================ package strmatcher // A MphIndexMatcher is divided into three parts: // 1. `full` and `domain` patterns are matched by Rabin-Karp algorithm and minimal perfect hash table; // 2. `substr` patterns are matched by ac automaton; // 3. `regex` patterns are matched with the regex library. type MphIndexMatcher struct { count uint32 mph *MphMatcherGroup ac *ACAutomatonMatcherGroup regex *SimpleMatcherGroup } func NewMphIndexMatcher() *MphIndexMatcher { return new(MphIndexMatcher) } // Add implements IndexMatcher.Add. func (g *MphIndexMatcher) Add(matcher Matcher) uint32 { g.count++ index := g.count switch matcher := matcher.(type) { case FullMatcher: if g.mph == nil { g.mph = NewMphMatcherGroup() } g.mph.AddFullMatcher(matcher, index) case DomainMatcher: if g.mph == nil { g.mph = NewMphMatcherGroup() } g.mph.AddDomainMatcher(matcher, index) case SubstrMatcher: if g.ac == nil { g.ac = NewACAutomatonMatcherGroup() } g.ac.AddSubstrMatcher(matcher, index) case *RegexMatcher: if g.regex == nil { g.regex = &SimpleMatcherGroup{} } g.regex.AddMatcher(matcher, index) } return index } // Build implements IndexMatcher.Build. func (g *MphIndexMatcher) Build() error { if g.mph != nil { g.mph.Build() } if g.ac != nil { g.ac.Build() } return nil } // Match implements IndexMatcher.Match. func (g *MphIndexMatcher) Match(input string) []uint32 { result := make([][]uint32, 0, 5) if g.mph != nil { if matches := g.mph.Match(input); len(matches) > 0 { result = append(result, matches) } } if g.ac != nil { if matches := g.ac.Match(input); len(matches) > 0 { result = append(result, matches) } } if g.regex != nil { if matches := g.regex.Match(input); len(matches) > 0 { result = append(result, matches) } } return CompositeMatches(result) } // MatchAny implements IndexMatcher.MatchAny. func (g *MphIndexMatcher) MatchAny(input string) bool { if g.mph != nil && g.mph.MatchAny(input) { return true } if g.ac != nil && g.ac.MatchAny(input) { return true } return g.regex != nil && g.regex.MatchAny(input) } // Size implements IndexMatcher.Size. func (g *MphIndexMatcher) Size() uint32 { return g.count } ================================================ FILE: common/strmatcher/indexmatcher_mph_test.go ================================================ package strmatcher_test import ( "reflect" "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func TestMphIndexMatcher(t *testing.T) { rules := []struct { Type Type Domain string }{ { Type: Regex, Domain: "apis\\.us$", }, { Type: Substr, Domain: "apis", }, { Type: Domain, Domain: "googleapis.com", }, { Type: Domain, Domain: "com", }, { Type: Full, Domain: "www.baidu.com", }, { Type: Substr, Domain: "apis", }, { Type: Domain, Domain: "googleapis.com", }, { Type: Full, Domain: "fonts.googleapis.com", }, { Type: Full, Domain: "www.baidu.com", }, { Type: Domain, Domain: "example.com", }, } cases := []struct { Input string Output []uint32 }{ { Input: "www.baidu.com", Output: []uint32{5, 9, 4}, }, { Input: "fonts.googleapis.com", Output: []uint32{8, 3, 7, 4, 2, 6}, }, { Input: "example.googleapis.com", Output: []uint32{3, 7, 4, 2, 6}, }, { Input: "testapis.us", Output: []uint32{2, 6, 1}, }, { Input: "example.com", Output: []uint32{10, 4}, }, } matcherGroup := NewMphIndexMatcher() for _, rule := range rules { matcher, err := rule.Type.New(rule.Domain) common.Must(err) matcherGroup.Add(matcher) } matcherGroup.Build() for _, test := range cases { if m := matcherGroup.Match(test.Input); !reflect.DeepEqual(m, test.Output) { t.Error("unexpected output: ", m, " for test case ", test) } } } ================================================ FILE: common/strmatcher/matchergroup_ac_automation.go ================================================ package strmatcher import ( "container/list" ) const ( acValidCharCount = 39 // aA-zZ (26), 0-9 (10), - (1), . (1), invalid(1) acMatchTypeCount = 3 // Full, Domain and Substr ) type acEdge byte const ( acTrieEdge acEdge = 1 acFailEdge acEdge = 0 ) type acNode struct { next [acValidCharCount]uint32 // EdgeIdx -> Next NodeIdx (Next trie node or fail node) edge [acValidCharCount]acEdge // EdgeIdx -> Trie Edge / Fail Edge fail uint32 // NodeIdx of *next matched* Substr Pattern on its fail path match uint32 // MatchIdx of matchers registered on this node, 0 indicates no match } // Sizeof acNode: (4+1)*acValidCharCount + + 4 + 4 type acValue [acMatchTypeCount][]uint32 // MatcherType -> Registered Matcher Values // ACAutoMationMatcherGroup is an implementation of MatcherGroup. // It uses an AC Automata to provide support for Full, Domain and Substr matcher. Trie node is char based. // // NOTICE: ACAutomatonMatcherGroup currently uses a restricted charset (LDH Subset), // upstream should manually in a way to ensure all patterns and inputs passed to it to be in this charset. type ACAutomatonMatcherGroup struct { nodes []acNode // NodeIdx -> acNode values []acValue // MatchIdx -> acValue } func NewACAutomatonMatcherGroup() *ACAutomatonMatcherGroup { ac := new(ACAutomatonMatcherGroup) ac.addNode() // Create root node (NodeIdx 0) ac.addMatchEntry() // Create sentinel match entry (MatchIdx 0) return ac } // AddFullMatcher implements MatcherGroupForFull.AddFullMatcher. func (ac *ACAutomatonMatcherGroup) AddFullMatcher(matcher FullMatcher, value uint32) { ac.addPattern(0, matcher.Pattern(), matcher.Type(), value) } // AddDomainMatcher implements MatcherGroupForDomain.AddDomainMatcher. func (ac *ACAutomatonMatcherGroup) AddDomainMatcher(matcher DomainMatcher, value uint32) { node := ac.addPattern(0, matcher.Pattern(), matcher.Type(), value) // For full domain match ac.addPattern(node, ".", matcher.Type(), value) // For partial domain match } // AddSubstrMatcher implements MatcherGroupForSubstr.AddSubstrMatcher. func (ac *ACAutomatonMatcherGroup) AddSubstrMatcher(matcher SubstrMatcher, value uint32) { ac.addPattern(0, matcher.Pattern(), matcher.Type(), value) } func (ac *ACAutomatonMatcherGroup) addPattern(nodeIdx uint32, pattern string, matcherType Type, value uint32) uint32 { node := &ac.nodes[nodeIdx] for i := len(pattern) - 1; i >= 0; i-- { edgeIdx := acCharset[pattern[i]] nextIdx := node.next[edgeIdx] if nextIdx == 0 { // Add new Trie Edge nextIdx = ac.addNode() ac.nodes[nodeIdx].next[edgeIdx] = nextIdx ac.nodes[nodeIdx].edge[edgeIdx] = acTrieEdge } nodeIdx = nextIdx node = &ac.nodes[nodeIdx] } if node.match == 0 { // Add new match entry node.match = ac.addMatchEntry() } ac.values[node.match][matcherType] = append(ac.values[node.match][matcherType], value) return nodeIdx } func (ac *ACAutomatonMatcherGroup) addNode() uint32 { ac.nodes = append(ac.nodes, acNode{}) return uint32(len(ac.nodes) - 1) } func (ac *ACAutomatonMatcherGroup) addMatchEntry() uint32 { ac.values = append(ac.values, acValue{}) return uint32(len(ac.values) - 1) } func (ac *ACAutomatonMatcherGroup) Build() error { fail := make([]uint32, len(ac.nodes)) queue := list.New() for edgeIdx := 0; edgeIdx < acValidCharCount; edgeIdx++ { if nextIdx := ac.nodes[0].next[edgeIdx]; nextIdx != 0 { queue.PushBack(nextIdx) } } for { front := queue.Front() if front == nil { break } queue.Remove(front) nodeIdx := front.Value.(uint32) node := &ac.nodes[nodeIdx] // Current node failNode := &ac.nodes[fail[nodeIdx]] // Fail node of currrent node for edgeIdx := 0; edgeIdx < acValidCharCount; edgeIdx++ { nodeIdx := node.next[edgeIdx] // Next node through trie edge failIdx := failNode.next[edgeIdx] // Next node through fail edge if nodeIdx != 0 { queue.PushBack(nodeIdx) fail[nodeIdx] = failIdx if match := ac.nodes[failIdx].match; match != 0 && len(ac.values[match][Substr]) > 0 { // Fail node is a Substr match node ac.nodes[nodeIdx].fail = failIdx } else { // Use path compression to reduce fail path to only contain match nodes ac.nodes[nodeIdx].fail = ac.nodes[failIdx].fail } } else { // Add new fail edge node.next[edgeIdx] = failIdx node.edge[edgeIdx] = acFailEdge } } } return nil } // Match implements MatcherGroup.Match. func (ac *ACAutomatonMatcherGroup) Match(input string) []uint32 { suffixMatches := make([][]uint32, 0, 5) substrMatches := make([][]uint32, 0, 5) fullMatch := true // fullMatch indicates no fail edge traversed so far. node := &ac.nodes[0] // start from root node. // 1. the match string is all through trie edge. FULL MATCH or DOMAIN // 2. the match string is through a fail edge. NOT FULL MATCH // 2.1 Through a fail edge, but there exists a valid node. SUBSTR for i := len(input) - 1; i >= 0; i-- { edge := acCharset[input[i]] fullMatch = fullMatch && (node.edge[edge] == acTrieEdge) node = &ac.nodes[node.next[edge]] // Advance to next node // When entering a new node, traverse the fail path to find all possible Substr patterns: // 1. The fail path is compressed to only contains match nodes and root node (for terminate condition). // 2. node.fail != 0 is added here for better performance (as shown by benchmark), possibly it helps branch prediction. if node.fail != 0 { for failIdx, failNode := node.fail, &ac.nodes[node.fail]; failIdx != 0; failIdx, failNode = failNode.fail, &ac.nodes[failIdx] { substrMatches = append(substrMatches, ac.values[failNode.match][Substr]) } } // When entering a new node, check whether this node is a match. // For Substr matchers: // 1. Matched in any situation, whether a failNode edge is traversed or not. // For Domain matchers: // 1. Should not traverse any fail edge (fullMatch). // 2. Only check on dot separator (input[i] == '.'). if node.match != 0 { values := ac.values[node.match] if len(values[Substr]) > 0 { substrMatches = append(substrMatches, values[Substr]) } if fullMatch && input[i] == '.' && len(values[Domain]) > 0 { suffixMatches = append(suffixMatches, values[Domain]) } } } // At the end of input, check if the whole string matches a pattern. // For Domain matchers: // 1. Exact match on Domain Matcher works like Full Match. e.g. foo.com is a full match for domain:foo.com. // For Full matchers: // 1. Only when no fail edge is traversed (fullMatch). // 2. Takes the highest priority (added at last). if fullMatch && node.match != 0 { values := ac.values[node.match] if len(values[Domain]) > 0 { suffixMatches = append(suffixMatches, values[Domain]) } if len(values[Full]) > 0 { suffixMatches = append(suffixMatches, values[Full]) } } if len(substrMatches) == 0 { return CompositeMatchesReverse(suffixMatches) } return CompositeMatchesReverse(append(substrMatches, suffixMatches...)) } // MatchAny implements MatcherGroup.MatchAny. func (ac *ACAutomatonMatcherGroup) MatchAny(input string) bool { fullMatch := true node := &ac.nodes[0] for i := len(input) - 1; i >= 0; i-- { edge := acCharset[input[i]] fullMatch = fullMatch && (node.edge[edge] == acTrieEdge) node = &ac.nodes[node.next[edge]] if node.fail != 0 { // There is a match on this node's fail path return true } if node.match != 0 { // There is a match on this node values := ac.values[node.match] if len(values[Substr]) > 0 { // Substr match succeeds unconditionally return true } if fullMatch && input[i] == '.' && len(values[Domain]) > 0 { // Domain match only succeeds with dot separator on trie path return true } } } return fullMatch && node.match != 0 // At the end of input, Domain and Full match will succeed if no fail edge is traversed } // Letter-Digit-Hyphen (LDH) subset (https://tools.ietf.org/html/rfc952): // - Letters A to Z (no distinction is made between uppercase and lowercase) // - Digits 0 to 9 // - Hyphens(-) and Periods(.) // // If for future the strmatcher are used for other scenarios than domain, // we could add a new Charset interface to represent variable charsets. var acCharset = [256]int{ 'A': 1, 'a': 1, 'B': 2, 'b': 2, 'C': 3, 'c': 3, 'D': 4, 'd': 4, 'E': 5, 'e': 5, 'F': 6, 'f': 6, 'G': 7, 'g': 7, 'H': 8, 'h': 8, 'I': 9, 'i': 9, 'J': 10, 'j': 10, 'K': 11, 'k': 11, 'L': 12, 'l': 12, 'M': 13, 'm': 13, 'N': 14, 'n': 14, 'O': 15, 'o': 15, 'P': 16, 'p': 16, 'Q': 17, 'q': 17, 'R': 18, 'r': 18, 'S': 19, 's': 19, 'T': 20, 't': 20, 'U': 21, 'u': 21, 'V': 22, 'v': 22, 'W': 23, 'w': 23, 'X': 24, 'x': 24, 'Y': 25, 'y': 25, 'Z': 26, 'z': 26, '-': 27, '.': 28, '0': 29, '1': 30, '2': 31, '3': 32, '4': 33, '5': 34, '6': 35, '7': 36, '8': 37, '9': 38, } ================================================ FILE: common/strmatcher/matchergroup_ac_automation_test.go ================================================ package strmatcher_test import ( "reflect" "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func TestACAutomatonMatcherGroup(t *testing.T) { cases1 := []struct { pattern string mType Type input string output bool }{ { pattern: "v2fly.org", mType: Domain, input: "www.v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Domain, input: "v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Domain, input: "www.v3fly.org", output: false, }, { pattern: "v2fly.org", mType: Domain, input: "2fly.org", output: false, }, { pattern: "v2fly.org", mType: Domain, input: "xv2fly.org", output: false, }, { pattern: "v2fly.org", mType: Full, input: "v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Full, input: "xv2fly.org", output: false, }, } for _, test := range cases1 { ac := NewACAutomatonMatcherGroup() matcher, err := test.mType.New(test.pattern) common.Must(err) common.Must(AddMatcherToGroup(ac, matcher, 0)) ac.Build() if m := ac.MatchAny(test.input); m != test.output { t.Error("unexpected output: ", m, " for test case ", test) } } { cases2Input := []struct { pattern string mType Type }{ { pattern: "163.com", mType: Domain, }, { pattern: "m.126.com", mType: Full, }, { pattern: "3.com", mType: Full, }, { pattern: "google.com", mType: Substr, }, { pattern: "vgoogle.com", mType: Substr, }, } ac := NewACAutomatonMatcherGroup() for _, test := range cases2Input { matcher, err := test.mType.New(test.pattern) common.Must(err) common.Must(AddMatcherToGroup(ac, matcher, 0)) } ac.Build() cases2Output := []struct { pattern string res bool }{ { pattern: "126.com", res: false, }, { pattern: "m.163.com", res: true, }, { pattern: "mm163.com", res: false, }, { pattern: "m.126.com", res: true, }, { pattern: "163.com", res: true, }, { pattern: "63.com", res: false, }, { pattern: "oogle.com", res: false, }, { pattern: "vvgoogle.com", res: true, }, } for _, test := range cases2Output { if m := ac.MatchAny(test.pattern); m != test.res { t.Error("unexpected output: ", m, " for test case ", test) } } } { cases3Input := []struct { pattern string mType Type }{ { pattern: "video.google.com", mType: Domain, }, { pattern: "gle.com", mType: Domain, }, } ac := NewACAutomatonMatcherGroup() for _, test := range cases3Input { matcher, err := test.mType.New(test.pattern) common.Must(err) common.Must(AddMatcherToGroup(ac, matcher, 0)) } ac.Build() cases3Output := []struct { pattern string res bool }{ { pattern: "google.com", res: false, }, } for _, test := range cases3Output { if m := ac.MatchAny(test.pattern); m != test.res { t.Error("unexpected output: ", m, " for test case ", test) } } } { cases4Input := []struct { pattern string mType Type }{ { pattern: "apis", mType: Substr, }, { pattern: "googleapis.com", mType: Domain, }, } ac := NewACAutomatonMatcherGroup() for _, test := range cases4Input { matcher, err := test.mType.New(test.pattern) common.Must(err) common.Must(AddMatcherToGroup(ac, matcher, 0)) } ac.Build() cases4Output := []struct { pattern string res bool }{ { pattern: "gapis.com", res: true, }, } for _, test := range cases4Output { if m := ac.MatchAny(test.pattern); m != test.res { t.Error("unexpected output: ", m, " for test case ", test) } } } } func TestACAutomatonMatcherGroupSubstr(t *testing.T) { patterns := []struct { pattern string mType Type }{ { pattern: "apis", mType: Substr, }, { pattern: "google", mType: Substr, }, { pattern: "apis", mType: Substr, }, } cases := []struct { input string output []uint32 }{ { input: "google.com", output: []uint32{1}, }, { input: "apis.com", output: []uint32{0, 2}, }, { input: "googleapis.com", output: []uint32{1, 0, 2}, }, { input: "fonts.googleapis.com", output: []uint32{1, 0, 2}, }, { input: "apis.googleapis.com", output: []uint32{0, 2, 1, 0, 2}, }, } matcherGroup := NewACAutomatonMatcherGroup() for id, entry := range patterns { matcher, err := entry.mType.New(entry.pattern) common.Must(err) common.Must(AddMatcherToGroup(matcherGroup, matcher, uint32(id))) } matcherGroup.Build() for _, test := range cases { if r := matcherGroup.Match(test.input); !reflect.DeepEqual(r, test.output) { t.Error("unexpected output: ", r, " for test case ", test) } } } // See https://github.com/v2fly/v2ray-core/issues/92#issuecomment-673238489 func TestACAutomatonMatcherGroupAsIndexMatcher(t *testing.T) { rules := []struct { Type Type Domain string }{ // Regex not supported by ACAutomationMatcherGroup // { // Type: Regex, // Domain: "apis\\.us$", // }, { Type: Substr, Domain: "apis", }, { Type: Domain, Domain: "googleapis.com", }, { Type: Domain, Domain: "com", }, { Type: Full, Domain: "www.baidu.com", }, { Type: Substr, Domain: "apis", }, { Type: Domain, Domain: "googleapis.com", }, { Type: Full, Domain: "fonts.googleapis.com", }, { Type: Full, Domain: "www.baidu.com", }, { Type: Domain, Domain: "example.com", }, } cases := []struct { Input string Output []uint32 }{ { Input: "www.baidu.com", Output: []uint32{5, 9, 4}, }, { Input: "fonts.googleapis.com", Output: []uint32{8, 3, 7, 4, 2, 6}, }, { Input: "example.googleapis.com", Output: []uint32{3, 7, 4, 2, 6}, }, { Input: "testapis.us", Output: []uint32{2, 6 /*, 1*/}, }, { Input: "example.com", Output: []uint32{10, 4}, }, } matcherGroup := NewACAutomatonMatcherGroup() for i, rule := range rules { matcher, err := rule.Type.New(rule.Domain) common.Must(err) common.Must(AddMatcherToGroup(matcherGroup, matcher, uint32(i+2))) } matcherGroup.Build() for _, test := range cases { if m := matcherGroup.Match(test.Input); !reflect.DeepEqual(m, test.Output) { t.Error("unexpected output: ", m, " for test case ", test) } } } ================================================ FILE: common/strmatcher/matchergroup_domain.go ================================================ package strmatcher type trieNode struct { values []uint32 children map[string]*trieNode } // DomainMatcherGroup is an implementation of MatcherGroup. // It uses trie to optimize both memory consumption and lookup speed. Trie node is domain label based. type DomainMatcherGroup struct { root *trieNode } func NewDomainMatcherGroup() *DomainMatcherGroup { return &DomainMatcherGroup{ root: new(trieNode), } } // AddDomainMatcher implements MatcherGroupForDomain.AddDomainMatcher. func (g *DomainMatcherGroup) AddDomainMatcher(matcher DomainMatcher, value uint32) { node := g.root pattern := matcher.Pattern() for i := len(pattern); i > 0; { var part string for j := i - 1; ; j-- { if pattern[j] == '.' { part = pattern[j+1 : i] i = j break } if j == 0 { part = pattern[j:i] i = j break } } if node.children == nil { node.children = make(map[string]*trieNode) } next := node.children[part] if next == nil { next = new(trieNode) node.children[part] = next } node = next } node.values = append(node.values, value) } // Match implements MatcherGroup.Match. func (g *DomainMatcherGroup) Match(input string) []uint32 { matches := make([][]uint32, 0, 5) node := g.root for i := len(input); i > 0; { for j := i - 1; ; j-- { if input[j] == '.' { // Domain label found node = node.children[input[j+1:i]] i = j break } if j == 0 { // The last part of domain label node = node.children[input[j:i]] i = j break } } if node == nil { // No more match if no trie edge transition break } if len(node.values) > 0 { // Found matched matchers matches = append(matches, node.values) } if node.children == nil { // No more match if leaf node reached break } } return CompositeMatchesReverse(matches) } // MatchAny implements MatcherGroup.MatchAny. func (g *DomainMatcherGroup) MatchAny(input string) bool { node := g.root for i := len(input); i > 0; { for j := i - 1; ; j-- { if input[j] == '.' { node = node.children[input[j+1:i]] i = j break } if j == 0 { node = node.children[input[j:i]] i = j break } } if node == nil { return false } if len(node.values) > 0 { return true } if node.children == nil { return false } } return false } ================================================ FILE: common/strmatcher/matchergroup_domain_test.go ================================================ package strmatcher_test import ( "reflect" "testing" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func TestDomainMatcherGroup(t *testing.T) { patterns := []struct { Pattern string Value uint32 }{ { Pattern: "v2fly.org", Value: 1, }, { Pattern: "google.com", Value: 2, }, { Pattern: "x.a.com", Value: 3, }, { Pattern: "a.b.com", Value: 4, }, { Pattern: "c.a.b.com", Value: 5, }, { Pattern: "x.y.com", Value: 4, }, { Pattern: "x.y.com", Value: 6, }, } testCases := []struct { Domain string Result []uint32 }{ { Domain: "x.v2fly.org", Result: []uint32{1}, }, { Domain: "y.com", Result: nil, }, { Domain: "a.b.com", Result: []uint32{4}, }, { // Matches [c.a.b.com, a.b.com] Domain: "c.a.b.com", Result: []uint32{5, 4}, }, { Domain: "c.a..b.com", Result: nil, }, { Domain: ".com", Result: nil, }, { Domain: "com", Result: nil, }, { Domain: "", Result: nil, }, { Domain: "x.y.com", Result: []uint32{4, 6}, }, } g := NewDomainMatcherGroup() for _, pattern := range patterns { AddMatcherToGroup(g, DomainMatcher(pattern.Pattern), pattern.Value) } for _, testCase := range testCases { r := g.Match(testCase.Domain) if !reflect.DeepEqual(r, testCase.Result) { t.Error("Failed to match domain: ", testCase.Domain, ", expect ", testCase.Result, ", but got ", r) } } } func TestEmptyDomainMatcherGroup(t *testing.T) { g := NewDomainMatcherGroup() r := g.Match("v2fly.org") if len(r) != 0 { t.Error("Expect [], but ", r) } } ================================================ FILE: common/strmatcher/matchergroup_full.go ================================================ package strmatcher // FullMatcherGroup is an implementation of MatcherGroup. // It uses a hash table to facilitate exact match lookup. type FullMatcherGroup struct { matchers map[string][]uint32 } func NewFullMatcherGroup() *FullMatcherGroup { return &FullMatcherGroup{ matchers: make(map[string][]uint32), } } // AddFullMatcher implements MatcherGroupForFull.AddFullMatcher. func (g *FullMatcherGroup) AddFullMatcher(matcher FullMatcher, value uint32) { domain := matcher.Pattern() g.matchers[domain] = append(g.matchers[domain], value) } // Match implements MatcherGroup.Match. func (g *FullMatcherGroup) Match(input string) []uint32 { return g.matchers[input] } // MatchAny implements MatcherGroup.Any. func (g *FullMatcherGroup) MatchAny(input string) bool { _, found := g.matchers[input] return found } ================================================ FILE: common/strmatcher/matchergroup_full_test.go ================================================ package strmatcher_test import ( "reflect" "testing" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func TestFullMatcherGroup(t *testing.T) { patterns := []struct { Pattern string Value uint32 }{ { Pattern: "v2fly.org", Value: 1, }, { Pattern: "google.com", Value: 2, }, { Pattern: "x.a.com", Value: 3, }, { Pattern: "x.y.com", Value: 4, }, { Pattern: "x.y.com", Value: 6, }, } testCases := []struct { Domain string Result []uint32 }{ { Domain: "v2fly.org", Result: []uint32{1}, }, { Domain: "y.com", Result: nil, }, { Domain: "x.y.com", Result: []uint32{4, 6}, }, } g := NewFullMatcherGroup() for _, pattern := range patterns { AddMatcherToGroup(g, FullMatcher(pattern.Pattern), pattern.Value) } for _, testCase := range testCases { r := g.Match(testCase.Domain) if !reflect.DeepEqual(r, testCase.Result) { t.Error("Failed to match domain: ", testCase.Domain, ", expect ", testCase.Result, ", but got ", r) } } } func TestEmptyFullMatcherGroup(t *testing.T) { g := NewFullMatcherGroup() r := g.Match("v2fly.org") if len(r) != 0 { t.Error("Expect [], but ", r) } } ================================================ FILE: common/strmatcher/matchergroup_mph.go ================================================ package strmatcher import ( "math/bits" "sort" "strings" "unsafe" ) // PrimeRK is the prime base used in Rabin-Karp algorithm. const PrimeRK = 16777619 // RollingHash calculates the rolling murmurHash of given string based on a provided suffix hash. func RollingHash(hash uint32, input string) uint32 { for i := len(input) - 1; i >= 0; i-- { hash = hash*PrimeRK + uint32(input[i]) } return hash } // MemHash is the hash function used by go map, it utilizes available hardware instructions(behaves // as aeshash if aes instruction is available). // With different seed, each MemHash performs as distinct hash functions. func MemHash(seed uint32, input string) uint32 { return uint32(strhash(unsafe.Pointer(&input), uintptr(seed))) // nosemgrep } const ( mphMatchTypeCount = 2 // Full and Domain ) type mphRuleInfo struct { rollingHash uint32 matchers [mphMatchTypeCount][]uint32 } // MphMatcherGroup is an implementation of MatcherGroup. // It implements Rabin-Karp algorithm and minimal perfect hash table for Full and Domain matcher. type MphMatcherGroup struct { rules []string // RuleIdx -> pattern string, index 0 reserved for failed lookup values [][]uint32 // RuleIdx -> registered matcher values for the pattern (Full Matcher takes precedence) level0 []uint32 // RollingHash & Mask -> seed for Memhash level0Mask uint32 // Mask restricting RollingHash to 0 ~ len(level0) level1 []uint32 // Memhash & Mask -> stored index for rules level1Mask uint32 // Mask for restricting Memhash to 0 ~ len(level1) ruleInfos *map[string]mphRuleInfo } func NewMphMatcherGroup() *MphMatcherGroup { return &MphMatcherGroup{ rules: []string{""}, values: [][]uint32{nil}, level0: nil, level0Mask: 0, level1: nil, level1Mask: 0, ruleInfos: &map[string]mphRuleInfo{}, // Only used for building, destroyed after build complete } } // AddFullMatcher implements MatcherGroupForFull. func (g *MphMatcherGroup) AddFullMatcher(matcher FullMatcher, value uint32) { pattern := strings.ToLower(matcher.Pattern()) g.addPattern(0, "", pattern, matcher.Type(), value) } // AddDomainMatcher implements MatcherGroupForDomain. func (g *MphMatcherGroup) AddDomainMatcher(matcher DomainMatcher, value uint32) { pattern := strings.ToLower(matcher.Pattern()) hash := g.addPattern(0, "", pattern, matcher.Type(), value) // For full domain match g.addPattern(hash, pattern, ".", matcher.Type(), value) // For partial domain match } func (g *MphMatcherGroup) addPattern(suffixHash uint32, suffixPattern string, pattern string, matcherType Type, value uint32) uint32 { fullPattern := pattern + suffixPattern info, found := (*g.ruleInfos)[fullPattern] if !found { info = mphRuleInfo{rollingHash: RollingHash(suffixHash, pattern)} g.rules = append(g.rules, fullPattern) g.values = append(g.values, nil) } info.matchers[matcherType] = append(info.matchers[matcherType], value) (*g.ruleInfos)[fullPattern] = info return info.rollingHash } // Build builds a minimal perfect hash table for insert rules. // Algorithm used: Hash, displace, and compress. See http://cmph.sourceforge.net/papers/esa09.pdf func (g *MphMatcherGroup) Build() error { ruleCount := len(*g.ruleInfos) g.level0 = make([]uint32, nextPow2(ruleCount/4)) g.level0Mask = uint32(len(g.level0) - 1) g.level1 = make([]uint32, nextPow2(ruleCount)) g.level1Mask = uint32(len(g.level1) - 1) // Create buckets based on all rule's rolling hash buckets := make([][]uint32, len(g.level0)) for ruleIdx := 1; ruleIdx < len(g.rules); ruleIdx++ { // Traverse rules starting from index 1 (0 reserved for failed lookup) ruleInfo := (*g.ruleInfos)[g.rules[ruleIdx]] bucketIdx := ruleInfo.rollingHash & g.level0Mask buckets[bucketIdx] = append(buckets[bucketIdx], uint32(ruleIdx)) g.values[ruleIdx] = append(ruleInfo.matchers[Full], ruleInfo.matchers[Domain]...) // nolint:gocritic } g.ruleInfos = nil // Set ruleInfos nil to release memory // Sort buckets in descending order with respect to each bucket's size bucketIdxs := make([]int, len(buckets)) for bucketIdx := range buckets { bucketIdxs[bucketIdx] = bucketIdx } sort.Slice(bucketIdxs, func(i, j int) bool { return len(buckets[bucketIdxs[i]]) > len(buckets[bucketIdxs[j]]) }) // Exercise Hash, Displace, and Compress algorithm to construct minimal perfect hash table occupied := make([]bool, len(g.level1)) // Whether a second-level hash has been already used hashedBucket := make([]uint32, 0, 4) // Second-level hashes for each rule in a specific bucket for _, bucketIdx := range bucketIdxs { bucket := buckets[bucketIdx] hashedBucket = hashedBucket[:0] seed := uint32(0) for len(hashedBucket) != len(bucket) { for _, ruleIdx := range bucket { memHash := MemHash(seed, g.rules[ruleIdx]) & g.level1Mask if occupied[memHash] { // Collision occurred with this seed for _, hash := range hashedBucket { // Revert all values in this hashed bucket occupied[hash] = false g.level1[hash] = 0 } hashedBucket = hashedBucket[:0] seed++ // Try next seed break } occupied[memHash] = true g.level1[memHash] = ruleIdx // The final value in the hash table hashedBucket = append(hashedBucket, memHash) } } g.level0[bucketIdx] = seed // Displacement value for this bucket } return nil } // Lookup searches for input in minimal perfect hash table and returns its index. 0 indicates not found. func (g *MphMatcherGroup) Lookup(rollingHash uint32, input string) uint32 { i0 := rollingHash & g.level0Mask seed := g.level0[i0] i1 := MemHash(seed, input) & g.level1Mask if n := g.level1[i1]; g.rules[n] == input { return n } return 0 } // Match implements MatcherGroup.Match. func (g *MphMatcherGroup) Match(input string) []uint32 { matches := make([][]uint32, 0, 5) hash := uint32(0) for i := len(input) - 1; i >= 0; i-- { hash = hash*PrimeRK + uint32(input[i]) if input[i] == '.' { if mphIdx := g.Lookup(hash, input[i:]); mphIdx != 0 { matches = append(matches, g.values[mphIdx]) } } } if mphIdx := g.Lookup(hash, input); mphIdx != 0 { matches = append(matches, g.values[mphIdx]) } return CompositeMatchesReverse(matches) } // MatchAny implements MatcherGroup.MatchAny. func (g *MphMatcherGroup) MatchAny(input string) bool { hash := uint32(0) for i := len(input) - 1; i >= 0; i-- { hash = hash*PrimeRK + uint32(input[i]) if input[i] == '.' { if g.Lookup(hash, input[i:]) != 0 { return true } } } return g.Lookup(hash, input) != 0 } func nextPow2(v int) int { if v <= 1 { return 1 } const MaxUInt = ^uint(0) n := (MaxUInt >> bits.LeadingZeros(uint(v))) + 1 return int(n) } //go:noescape //go:linkname strhash runtime.strhash func strhash(p unsafe.Pointer, h uintptr) uintptr ================================================ FILE: common/strmatcher/matchergroup_mph_test.go ================================================ package strmatcher_test import ( "reflect" "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func TestMphMatcherGroup(t *testing.T) { cases1 := []struct { pattern string mType Type input string output bool }{ { pattern: "v2fly.org", mType: Domain, input: "www.v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Domain, input: "v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Domain, input: "www.v3fly.org", output: false, }, { pattern: "v2fly.org", mType: Domain, input: "2fly.org", output: false, }, { pattern: "v2fly.org", mType: Domain, input: "xv2fly.org", output: false, }, { pattern: "v2fly.org", mType: Full, input: "v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Full, input: "xv2fly.org", output: false, }, } for _, test := range cases1 { mph := NewMphMatcherGroup() matcher, err := test.mType.New(test.pattern) common.Must(err) common.Must(AddMatcherToGroup(mph, matcher, 0)) mph.Build() if m := mph.MatchAny(test.input); m != test.output { t.Error("unexpected output: ", m, " for test case ", test) } } { cases2Input := []struct { pattern string mType Type }{ { pattern: "163.com", mType: Domain, }, { pattern: "m.126.com", mType: Full, }, { pattern: "3.com", mType: Full, }, } mph := NewMphMatcherGroup() for _, test := range cases2Input { matcher, err := test.mType.New(test.pattern) common.Must(err) common.Must(AddMatcherToGroup(mph, matcher, 0)) } mph.Build() cases2Output := []struct { pattern string res bool }{ { pattern: "126.com", res: false, }, { pattern: "m.163.com", res: true, }, { pattern: "mm163.com", res: false, }, { pattern: "m.126.com", res: true, }, { pattern: "163.com", res: true, }, { pattern: "63.com", res: false, }, { pattern: "oogle.com", res: false, }, { pattern: "vvgoogle.com", res: false, }, } for _, test := range cases2Output { if m := mph.MatchAny(test.pattern); m != test.res { t.Error("unexpected output: ", m, " for test case ", test) } } } { cases3Input := []struct { pattern string mType Type }{ { pattern: "video.google.com", mType: Domain, }, { pattern: "gle.com", mType: Domain, }, } mph := NewMphMatcherGroup() for _, test := range cases3Input { matcher, err := test.mType.New(test.pattern) common.Must(err) common.Must(AddMatcherToGroup(mph, matcher, 0)) } mph.Build() cases3Output := []struct { pattern string res bool }{ { pattern: "google.com", res: false, }, } for _, test := range cases3Output { if m := mph.MatchAny(test.pattern); m != test.res { t.Error("unexpected output: ", m, " for test case ", test) } } } } // See https://github.com/v2fly/v2ray-core/issues/92#issuecomment-673238489 func TestMphMatcherGroupAsIndexMatcher(t *testing.T) { rules := []struct { Type Type Domain string }{ // Regex not supported by MphMatcherGroup // { // Type: Regex, // Domain: "apis\\.us$", // }, // Substr not supported by MphMatcherGroup // { // Type: Substr, // Domain: "apis", // }, { Type: Domain, Domain: "googleapis.com", }, { Type: Domain, Domain: "com", }, { Type: Full, Domain: "www.baidu.com", }, // Substr not supported by MphMatcherGroup, We add another matcher to preserve index { Type: Domain, // Substr, Domain: "example.com", // "apis", }, { Type: Domain, Domain: "googleapis.com", }, { Type: Full, Domain: "fonts.googleapis.com", }, { Type: Full, Domain: "www.baidu.com", }, { // This matcher (index 10) is swapped with matcher (index 6) to test that full matcher takes high priority. Type: Full, Domain: "example.com", }, { Type: Domain, Domain: "example.com", }, } cases := []struct { Input string Output []uint32 }{ { Input: "www.baidu.com", Output: []uint32{5, 9, 4}, }, { Input: "fonts.googleapis.com", Output: []uint32{8, 3, 7, 4 /*2, 6*/}, }, { Input: "example.googleapis.com", Output: []uint32{3, 7, 4 /*2, 6*/}, }, { Input: "testapis.us", // Output: []uint32{ /*2, 6*/ /*1,*/ }, Output: nil, }, { Input: "example.com", Output: []uint32{10, 6, 11, 4}, }, } matcherGroup := NewMphMatcherGroup() for i, rule := range rules { matcher, err := rule.Type.New(rule.Domain) common.Must(err) common.Must(AddMatcherToGroup(matcherGroup, matcher, uint32(i+3))) } matcherGroup.Build() for _, test := range cases { if m := matcherGroup.Match(test.Input); !reflect.DeepEqual(m, test.Output) { t.Error("unexpected output: ", m, " for test case ", test) } } } func TestEmptyMphMatcherGroup(t *testing.T) { g := NewMphMatcherGroup() g.Build() r := g.Match("v2fly.org") if len(r) != 0 { t.Error("Expect [], but ", r) } } ================================================ FILE: common/strmatcher/matchergroup_simple.go ================================================ package strmatcher type matcherEntry struct { matcher Matcher value uint32 } // SimpleMatcherGroup is an implementation of MatcherGroup. // It simply stores all matchers in an array and sequentially matches them. type SimpleMatcherGroup struct { matchers []matcherEntry } // AddMatcher implements MatcherGroupForAll.AddMatcher. func (g *SimpleMatcherGroup) AddMatcher(matcher Matcher, value uint32) { g.matchers = append(g.matchers, matcherEntry{ matcher: matcher, value: value, }) } // Match implements MatcherGroup.Match. func (g *SimpleMatcherGroup) Match(input string) []uint32 { result := []uint32{} for _, e := range g.matchers { if e.matcher.Match(input) { result = append(result, e.value) } } return result } // MatchAny implements MatcherGroup.MatchAny. func (g *SimpleMatcherGroup) MatchAny(input string) bool { for _, e := range g.matchers { if e.matcher.Match(input) { return true } } return false } ================================================ FILE: common/strmatcher/matchergroup_simple_test.go ================================================ package strmatcher_test import ( "reflect" "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func TestSimpleMatcherGroup(t *testing.T) { patterns := []struct { pattern string mType Type }{ { pattern: "v2fly.org", mType: Domain, }, { pattern: "v2fly.org", mType: Full, }, { pattern: "v2fly.org", mType: Regex, }, } cases := []struct { input string output []uint32 }{ { input: "www.v2fly.org", output: []uint32{0, 2}, }, { input: "v2fly.org", output: []uint32{0, 1, 2}, }, { input: "www.v3fly.org", output: []uint32{}, }, { input: "2fly.org", output: []uint32{}, }, { input: "xv2fly.org", output: []uint32{2}, }, { input: "v2flyxorg", output: []uint32{2}, }, } matcherGroup := &SimpleMatcherGroup{} for id, entry := range patterns { matcher, err := entry.mType.New(entry.pattern) common.Must(err) common.Must(AddMatcherToGroup(matcherGroup, matcher, uint32(id))) } for _, test := range cases { if r := matcherGroup.Match(test.input); !reflect.DeepEqual(r, test.output) { t.Error("unexpected output: ", r, " for test case ", test) } } } ================================================ FILE: common/strmatcher/matchergroup_substr.go ================================================ package strmatcher import ( "sort" "strings" ) // SubstrMatcherGroup is implementation of MatcherGroup, // It is simply implmeneted to comply with the priority specification of Substr matchers. type SubstrMatcherGroup struct { patterns []string values []uint32 } // AddSubstrMatcher implements MatcherGroupForSubstr.AddSubstrMatcher. func (g *SubstrMatcherGroup) AddSubstrMatcher(matcher SubstrMatcher, value uint32) { g.patterns = append(g.patterns, matcher.Pattern()) g.values = append(g.values, value) } // Match implements MatcherGroup.Match. func (g *SubstrMatcherGroup) Match(input string) []uint32 { var result []uint32 for i, pattern := range g.patterns { for j := strings.LastIndex(input, pattern); j != -1; j = strings.LastIndex(input[:j], pattern) { result = append(result, uint32(j)<<16|uint32(i)&0xffff) // uint32: position (higher 16 bit) | patternIdx (lower 16 bit) } } // sort.Slice will trigger allocation no matter what input is. See https://github.com/golang/go/issues/17332 // We optimize the sorting by length to prevent memory allocation as possible. switch len(result) { case 0: return nil case 1: // No need to sort case 2: // Do a simple swap if unsorted if result[0] > result[1] { result[0], result[1] = result[1], result[0] } default: // Sort the match results in dictionary order, so that: // 1. Pattern matched at smaller position (meaning matched further) takes precedence. // 2. When patterns matched at same position, pattern with smaller index (meaning inserted early) takes precedence. sort.Slice(result, func(i, j int) bool { return result[i] < result[j] }) } for i, entry := range result { result[i] = g.values[entry&0xffff] // Get pattern value from its index (the lower 16 bit) } return result } // MatchAny implements MatcherGroup.MatchAny. func (g *SubstrMatcherGroup) MatchAny(input string) bool { for _, pattern := range g.patterns { if strings.Contains(input, pattern) { return true } } return false } ================================================ FILE: common/strmatcher/matchergroup_substr_test.go ================================================ package strmatcher_test import ( "reflect" "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func TestSubstrMatcherGroup(t *testing.T) { patterns := []struct { pattern string mType Type }{ { pattern: "apis", mType: Substr, }, { pattern: "google", mType: Substr, }, { pattern: "apis", mType: Substr, }, } cases := []struct { input string output []uint32 }{ { input: "google.com", output: []uint32{1}, }, { input: "apis.com", output: []uint32{0, 2}, }, { input: "googleapis.com", output: []uint32{1, 0, 2}, }, { input: "fonts.googleapis.com", output: []uint32{1, 0, 2}, }, { input: "apis.googleapis.com", output: []uint32{0, 2, 1, 0, 2}, }, } matcherGroup := &SubstrMatcherGroup{} for id, entry := range patterns { matcher, err := entry.mType.New(entry.pattern) common.Must(err) common.Must(AddMatcherToGroup(matcherGroup, matcher, uint32(id))) } for _, test := range cases { if r := matcherGroup.Match(test.input); !reflect.DeepEqual(r, test.output) { t.Error("unexpected output: ", r, " for test case ", test) } } } ================================================ FILE: common/strmatcher/matchers.go ================================================ package strmatcher import ( "errors" "regexp" "strings" "unicode/utf8" "golang.org/x/net/idna" ) // FullMatcher is an implementation of Matcher. type FullMatcher string func (FullMatcher) Type() Type { return Full } func (m FullMatcher) Pattern() string { return string(m) } func (m FullMatcher) String() string { return "full:" + m.Pattern() } func (m FullMatcher) Match(s string) bool { return string(m) == s } // DomainMatcher is an implementation of Matcher. type DomainMatcher string func (DomainMatcher) Type() Type { return Domain } func (m DomainMatcher) Pattern() string { return string(m) } func (m DomainMatcher) String() string { return "domain:" + m.Pattern() } func (m DomainMatcher) Match(s string) bool { pattern := m.Pattern() if !strings.HasSuffix(s, pattern) { return false } return len(s) == len(pattern) || s[len(s)-len(pattern)-1] == '.' } // SubstrMatcher is an implementation of Matcher. type SubstrMatcher string func (SubstrMatcher) Type() Type { return Substr } func (m SubstrMatcher) Pattern() string { return string(m) } func (m SubstrMatcher) String() string { return "keyword:" + m.Pattern() } func (m SubstrMatcher) Match(s string) bool { return strings.Contains(s, m.Pattern()) } // RegexMatcher is an implementation of Matcher. type RegexMatcher struct { pattern *regexp.Regexp } func (*RegexMatcher) Type() Type { return Regex } func (m *RegexMatcher) Pattern() string { return m.pattern.String() } func (m *RegexMatcher) String() string { return "regexp:" + m.Pattern() } func (m *RegexMatcher) Match(s string) bool { return m.pattern.MatchString(s) } // New creates a new Matcher based on the given pattern. func (t Type) New(pattern string) (Matcher, error) { switch t { case Full: return FullMatcher(pattern), nil case Substr: return SubstrMatcher(pattern), nil case Domain: pattern, err := ToDomain(pattern) if err != nil { return nil, err } return DomainMatcher(pattern), nil case Regex: // 1. regex matching is case-sensitive regex, err := regexp.Compile(pattern) if err != nil { return nil, err } return &RegexMatcher{pattern: regex}, nil default: return nil, errors.New("unknown matcher type") } } // NewDomainPattern creates a new Matcher based on the given domain pattern. // It works like `Type.New`, but will do validation and conversion to ensure it's a valid domain pattern. func (t Type) NewDomainPattern(pattern string) (Matcher, error) { switch t { case Full: pattern, err := ToDomain(pattern) if err != nil { return nil, err } return FullMatcher(pattern), nil case Substr: pattern, err := ToDomain(pattern) if err != nil { return nil, err } return SubstrMatcher(pattern), nil case Domain: pattern, err := ToDomain(pattern) if err != nil { return nil, err } return DomainMatcher(pattern), nil case Regex: // Regex's charset not in LDH subset regex, err := regexp.Compile(pattern) if err != nil { return nil, err } return &RegexMatcher{pattern: regex}, nil default: return nil, errors.New("unknown matcher type") } } // ToDomain converts input pattern to a domain string, and return error if such a conversion cannot be made. // 1. Conforms to Letter-Digit-Hyphen (LDH) subset (https://tools.ietf.org/html/rfc952): // * Letters A to Z (no distinction between uppercase and lowercase, we convert to lowers) // * Digits 0 to 9 // * Hyphens(-) and Periods(.) // 2. If any non-ASCII characters, domain are converted from Internationalized domain name to Punycode. func ToDomain(pattern string) (string, error) { for { isASCII, hasUpper := true, false for i := 0; i < len(pattern); i++ { c := pattern[i] if c >= utf8.RuneSelf { isASCII = false break } switch { case 'A' <= c && c <= 'Z': hasUpper = true case 'a' <= c && c <= 'z': case '0' <= c && c <= '9': case c == '-': case c == '.': default: return "", errors.New("pattern string does not conform to Letter-Digit-Hyphen (LDH) subset") } } if !isASCII { var err error pattern, err = idna.Punycode.ToASCII(pattern) if err != nil { return "", err } continue } if hasUpper { pattern = strings.ToLower(pattern) } break } return pattern, nil } // MatcherGroupForAll is an interface indicating a MatcherGroup could accept all types of matchers. type MatcherGroupForAll interface { AddMatcher(matcher Matcher, value uint32) } // MatcherGroupForFull is an interface indicating a MatcherGroup could accept FullMatchers. type MatcherGroupForFull interface { AddFullMatcher(matcher FullMatcher, value uint32) } // MatcherGroupForDomain is an interface indicating a MatcherGroup could accept DomainMatchers. type MatcherGroupForDomain interface { AddDomainMatcher(matcher DomainMatcher, value uint32) } // MatcherGroupForSubstr is an interface indicating a MatcherGroup could accept SubstrMatchers. type MatcherGroupForSubstr interface { AddSubstrMatcher(matcher SubstrMatcher, value uint32) } // MatcherGroupForRegex is an interface indicating a MatcherGroup could accept RegexMatchers. type MatcherGroupForRegex interface { AddRegexMatcher(matcher *RegexMatcher, value uint32) } // AddMatcherToGroup is a helper function to try to add a Matcher to any kind of MatcherGroup. // It returns error if the MatcherGroup does not accept the provided Matcher's type. // This function is provided to help writing code to test a MatcherGroup. func AddMatcherToGroup(g MatcherGroup, matcher Matcher, value uint32) error { if g, ok := g.(IndexMatcher); ok { g.Add(matcher) return nil } if g, ok := g.(MatcherGroupForAll); ok { g.AddMatcher(matcher, value) return nil } switch matcher := matcher.(type) { case FullMatcher: if g, ok := g.(MatcherGroupForFull); ok { g.AddFullMatcher(matcher, value) return nil } case DomainMatcher: if g, ok := g.(MatcherGroupForDomain); ok { g.AddDomainMatcher(matcher, value) return nil } case SubstrMatcher: if g, ok := g.(MatcherGroupForSubstr); ok { g.AddSubstrMatcher(matcher, value) return nil } case *RegexMatcher: if g, ok := g.(MatcherGroupForRegex); ok { g.AddRegexMatcher(matcher, value) return nil } } return errors.New("cannot add matcher to matcher group") } // CompositeMatches flattens the matches slice to produce a single matched indices slice. // It is designed to avoid new memory allocation as possible. func CompositeMatches(matches [][]uint32) []uint32 { switch len(matches) { case 0: return nil case 1: return matches[0] default: result := make([]uint32, 0, 5) for i := 0; i < len(matches); i++ { result = append(result, matches[i]...) } return result } } // CompositeMatches flattens the matches slice to produce a single matched indices slice. // It is designed that: // 1. All matchers are concatenated in reverse order, so the matcher that matches further ranks higher. // 2. Indices in the same matcher keeps their original order. // 3. Avoid new memory allocation as possible. func CompositeMatchesReverse(matches [][]uint32) []uint32 { switch len(matches) { case 0: return nil case 1: return matches[0] default: result := make([]uint32, 0, 5) for i := len(matches) - 1; i >= 0; i-- { result = append(result, matches[i]...) } return result } } ================================================ FILE: common/strmatcher/matchers_test.go ================================================ package strmatcher_test import ( "reflect" "testing" "unsafe" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/strmatcher" ) func TestMatcher(t *testing.T) { cases := []struct { pattern string mType Type input string output bool }{ { pattern: "v2fly.org", mType: Domain, input: "www.v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Domain, input: "v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Domain, input: "www.v3fly.org", output: false, }, { pattern: "v2fly.org", mType: Domain, input: "2fly.org", output: false, }, { pattern: "v2fly.org", mType: Domain, input: "xv2fly.org", output: false, }, { pattern: "v2fly.org", mType: Full, input: "v2fly.org", output: true, }, { pattern: "v2fly.org", mType: Full, input: "xv2fly.org", output: false, }, { pattern: "v2fly.org", mType: Regex, input: "v2flyxorg", output: true, }, } for _, test := range cases { matcher, err := test.mType.New(test.pattern) common.Must(err) if m := matcher.Match(test.input); m != test.output { t.Error("unexpected output: ", m, " for test case ", test) } } } func TestToDomain(t *testing.T) { { // Test normal ASCII domain, which should not trigger new string data allocation input := "v2fly.org" domain, err := ToDomain(input) if err != nil { t.Error("unexpected error: ", err) } if domain != input { t.Error("unexpected output: ", domain, " for test case ", input) } if (*reflect.StringHeader)(unsafe.Pointer(&input)).Data != (*reflect.StringHeader)(unsafe.Pointer(&domain)).Data { t.Error("different string data of output: ", domain, " and test case ", input) } } { // Test ASCII domain containing upper case letter, which should be converted to lower case input := "v2FLY.oRg" domain, err := ToDomain(input) if err != nil { t.Error("unexpected error: ", err) } if domain != "v2fly.org" { t.Error("unexpected output: ", domain, " for test case ", input) } } { // Test internationalized domain, which should be translated to ASCII punycode input := "v2fly.公益" domain, err := ToDomain(input) if err != nil { t.Error("unexpected error: ", err) } if domain != "v2fly.xn--55qw42g" { t.Error("unexpected output: ", domain, " for test case ", input) } } { // Test internationalized domain containing upper case letter input := "v2FLY.公益" domain, err := ToDomain(input) if err != nil { t.Error("unexpected error: ", err) } if domain != "v2fly.xn--55qw42g" { t.Error("unexpected output: ", domain, " for test case ", input) } } { // Test domain name of invalid character, which should return with error input := "{" _, err := ToDomain(input) if err == nil { t.Error("unexpected non error for test case ", input) } } { // Test domain name containing a space, which should return with error input := "Mijia Cloud" _, err := ToDomain(input) if err == nil { t.Error("unexpected non error for test case ", input) } } { // Test domain name containing an underscore, which should return with error input := "Mijia_Cloud.com" _, err := ToDomain(input) if err == nil { t.Error("unexpected non error for test case ", input) } } { // Test internationalized domain containing invalid character input := "Mijia Cloud.公司" _, err := ToDomain(input) if err == nil { t.Error("unexpected non error for test case ", input) } } } ================================================ FILE: common/strmatcher/strmatcher.go ================================================ package strmatcher // Type is the type of the matcher. type Type byte const ( // Full is the type of matcher that the input string must exactly equal to the pattern. Full Type = 0 // Domain is the type of matcher that the input string must be a sub-domain or itself of the pattern. Domain Type = 1 // Substr is the type of matcher that the input string must contain the pattern as a sub-string. Substr Type = 2 // Regex is the type of matcher that the input string must matches the regular-expression pattern. Regex Type = 3 ) // Matcher is the interface to determine a string matches a pattern. // - This is a basic matcher to represent a certain kind of match semantic(full, substr, domain or regex). type Matcher interface { // Type returns the matcher's type. Type() Type // Pattern returns the matcher's raw string representation. Pattern() string // String returns a string representation of the matcher containing its type and pattern. String() string // Match returns true if the given string matches a predefined pattern. // * This method is seldom used for performance reason // and is generally taken over by their corresponding MatcherGroup. Match(input string) bool } // MatcherGroup is an advanced type of matcher to accept a bunch of basic Matchers (of certain type, not all matcher types). // For example: // - FullMatcherGroup accepts FullMatcher and uses a hash table to facilitate lookup. // - DomainMatcherGroup accepts DomainMatcher and uses a trie to optimize both memory consumption and lookup speed. type MatcherGroup interface { // Match returns all matched matchers with their corresponding values. Match(input string) []uint32 // MatchAny returns true as soon as one matching matcher is found. MatchAny(input string) bool } // IndexMatcher is a general type of matcher thats accepts all kinds of basic matchers. // It should: // - Accept all Matcher types with no exception. // - Optimize string matching with a combination of MatcherGroups. // - Obey certain priority order specification when returning matched Matchers. type IndexMatcher interface { // Size returns number of matchers added to IndexMatcher. Size() uint32 // Add adds a new Matcher to IndexMatcher, and returns its index. The index will never be 0. Add(matcher Matcher) uint32 // Build builds the IndexMatcher to be ready for matching. Build() error // Match returns the indices of all matchers that matches the input. // * Empty array is returned if no such matcher exists. // * The order of returned matchers should follow priority specification. // Priority specification: // 1. Priority between matcher types: full > domain > substr > regex. // 2. Priority of same-priority matchers matching at same position: the early added takes precedence. // 3. Priority of domain matchers matching at different levels: the further matched domain takes precedence. // 4. Priority of substr matchers matching at different positions: the further matched substr takes precedence. Match(input string) []uint32 // MatchAny returns true as soon as one matching matcher is found. MatchAny(input string) bool } ================================================ FILE: common/taggedfeatures/configloader.go ================================================ package taggedfeatures import ( "context" "encoding/json" "google.golang.org/protobuf/types/known/anypb" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/v5cfg" ) func LoadJSONConfig(ctx context.Context, interfaceType, defaultImpl string, message json.RawMessage) (*Config, error) { type ItemStub struct { MemberType string `json:"type"` Tag string `json:"tag"` Value json.RawMessage `json:"settings"` } type namedStub []ItemStub var stub namedStub err := json.Unmarshal(message, &stub) if err != nil { return nil, err } config := &Config{Features: map[string]*anypb.Any{}} for _, v := range stub { if v.MemberType == "" { v.MemberType = defaultImpl } pack, err := v5cfg.LoadHeterogeneousConfigFromRawJSON(ctx, interfaceType, v.MemberType, v.Value) if err != nil { return nil, err } config.Features[v.Tag] = serial.ToTypedMessage(pack) } return config, nil } ================================================ FILE: common/taggedfeatures/creator.go ================================================ package taggedfeatures import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features" ) func NewHolderFromConfig(ctx context.Context, config *Config, memberType interface{}) (features.TaggedFeatures, error) { holder := NewHolder(ctx, memberType) for k, v := range config.Features { var err error instance, err := serial.GetInstanceOf(v) if err != nil { return nil, newError("unable to get instance").Base(err) } obj, err := common.CreateObject(ctx, instance) if err != nil { return nil, newError("unable to create object").Base(err) } if feat, ok := obj.(features.Feature); ok { err = holder.AddFeaturesByTag(k, feat) if err != nil { return nil, newError("unable to add feature").Base(err) } continue } return nil, newError("not a feature ", k) } return holder, nil } ================================================ FILE: common/taggedfeatures/errors.generated.go ================================================ package taggedfeatures import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/taggedfeatures/holder.go ================================================ package taggedfeatures import ( "context" "reflect" "sync" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features" ) type Holder struct { access *sync.RWMutex features map[string]features.Feature memberType reflect.Type ctx context.Context } func NewHolder(ctx context.Context, memberType interface{}) *Holder { return &Holder{ ctx: ctx, access: &sync.RWMutex{}, features: make(map[string]features.Feature), memberType: reflect.TypeOf(memberType), } } func (h *Holder) GetFeaturesByTag(tag string) (features.Feature, error) { h.access.RLock() defer h.access.RUnlock() feature, ok := h.features[tag] if !ok { return nil, newError("unable to find feature with tag") } return feature, nil } func (h *Holder) AddFeaturesByTag(tag string, feature features.Feature) error { h.access.Lock() defer h.access.Unlock() featureType := reflect.TypeOf(feature.Type()) if !featureType.AssignableTo(h.memberType) { return newError("feature is not assignable to the base type") } h.features[tag] = feature return nil } func (h *Holder) RemoveFeaturesByTag(tag string) error { h.access.Lock() defer h.access.Unlock() delete(h.features, tag) return nil } func (h *Holder) GetFeaturesTag() ([]string, error) { h.access.RLock() defer h.access.RUnlock() var ret []string for key := range h.features { ret = append(ret, key) } return ret, nil } func (h *Holder) Start() error { h.access.Lock() defer h.access.Unlock() var startTasks []func() error for _, v := range h.features { startTasks = append(startTasks, v.Start) } return task.Run(h.ctx, startTasks...) } func (h *Holder) Close() error { h.access.Lock() defer h.access.Unlock() var closeTasks []func() error for _, v := range h.features { closeTasks = append(closeTasks, v.Close) } return task.Run(h.ctx, closeTasks...) } ================================================ FILE: common/taggedfeatures/skeleton.pb.go ================================================ package taggedfeatures import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Features map[string]*anypb.Any `protobuf:"bytes,1,rep,name=features,proto3" json:"features,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_common_taggedfeatures_skeleton_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_common_taggedfeatures_skeleton_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_common_taggedfeatures_skeleton_proto_rawDescGZIP(), []int{0} } func (x *Config) GetFeatures() map[string]*anypb.Any { if x != nil { return x.Features } return nil } var File_common_taggedfeatures_skeleton_proto protoreflect.FileDescriptor const file_common_taggedfeatures_skeleton_proto_rawDesc = "" + "\n" + "$common/taggedfeatures/skeleton.proto\x12 v2ray.core.common.taggedfeatures\x1a\x19google/protobuf/any.proto\"\xaf\x01\n" + "\x06Config\x12R\n" + "\bfeatures\x18\x01 \x03(\v26.v2ray.core.common.taggedfeatures.Config.FeaturesEntryR\bfeatures\x1aQ\n" + "\rFeaturesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + "\x05value\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x05value:\x028\x01B\x81\x01\n" + "$com.v2ray.core.common.taggedfeaturesP\x01Z4github.com/v2fly/v2ray-core/v5/common/taggedfeatures\xaa\x02 V2Ray.Core.Common.Taggedfeaturesb\x06proto3" var ( file_common_taggedfeatures_skeleton_proto_rawDescOnce sync.Once file_common_taggedfeatures_skeleton_proto_rawDescData []byte ) func file_common_taggedfeatures_skeleton_proto_rawDescGZIP() []byte { file_common_taggedfeatures_skeleton_proto_rawDescOnce.Do(func() { file_common_taggedfeatures_skeleton_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_taggedfeatures_skeleton_proto_rawDesc), len(file_common_taggedfeatures_skeleton_proto_rawDesc))) }) return file_common_taggedfeatures_skeleton_proto_rawDescData } var file_common_taggedfeatures_skeleton_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_common_taggedfeatures_skeleton_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.common.taggedfeatures.Config nil, // 1: v2ray.core.common.taggedfeatures.Config.FeaturesEntry (*anypb.Any)(nil), // 2: google.protobuf.Any } var file_common_taggedfeatures_skeleton_proto_depIdxs = []int32{ 1, // 0: v2ray.core.common.taggedfeatures.Config.features:type_name -> v2ray.core.common.taggedfeatures.Config.FeaturesEntry 2, // 1: v2ray.core.common.taggedfeatures.Config.FeaturesEntry.value:type_name -> google.protobuf.Any 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_common_taggedfeatures_skeleton_proto_init() } func file_common_taggedfeatures_skeleton_proto_init() { if File_common_taggedfeatures_skeleton_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_taggedfeatures_skeleton_proto_rawDesc), len(file_common_taggedfeatures_skeleton_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_common_taggedfeatures_skeleton_proto_goTypes, DependencyIndexes: file_common_taggedfeatures_skeleton_proto_depIdxs, MessageInfos: file_common_taggedfeatures_skeleton_proto_msgTypes, }.Build() File_common_taggedfeatures_skeleton_proto = out.File file_common_taggedfeatures_skeleton_proto_goTypes = nil file_common_taggedfeatures_skeleton_proto_depIdxs = nil } ================================================ FILE: common/taggedfeatures/skeleton.proto ================================================ syntax = "proto3"; package v2ray.core.common.taggedfeatures; option csharp_namespace = "V2Ray.Core.Common.Taggedfeatures"; option go_package = "github.com/v2fly/v2ray-core/v5/common/taggedfeatures"; option java_package = "com.v2ray.core.common.taggedfeatures"; option java_multiple_files = true; import "google/protobuf/any.proto"; message Config { map features = 1; } ================================================ FILE: common/taggedfeatures/taggedfeatures.go ================================================ package taggedfeatures //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: common/task/common.go ================================================ package task import "github.com/v2fly/v2ray-core/v5/common" // Close returns a func() that closes v. func Close(v interface{}) func() error { return func() error { return common.Close(v) } } ================================================ FILE: common/task/periodic.go ================================================ package task import ( "sync" "time" ) // Periodic is a task that runs periodically. type Periodic struct { // Interval of the task being run Interval time.Duration // Execute is the task function Execute func() error access sync.Mutex timer *time.Timer running bool } func (t *Periodic) hasClosed() bool { t.access.Lock() defer t.access.Unlock() return !t.running } func (t *Periodic) checkedExecute() error { if t.hasClosed() { return nil } if err := t.Execute(); err != nil { t.access.Lock() t.running = false t.access.Unlock() return err } t.access.Lock() defer t.access.Unlock() if !t.running { return nil } t.timer = time.AfterFunc(t.Interval, func() { t.checkedExecute() }) return nil } // Start implements common.Runnable. func (t *Periodic) Start() error { t.access.Lock() if t.running { t.access.Unlock() return nil } t.running = true t.access.Unlock() if err := t.checkedExecute(); err != nil { t.access.Lock() t.running = false t.access.Unlock() return err } return nil } // Close implements common.Closable. func (t *Periodic) Close() error { t.access.Lock() defer t.access.Unlock() t.running = false if t.timer != nil { t.timer.Stop() t.timer = nil } return nil } ================================================ FILE: common/task/periodic_test.go ================================================ package task_test import ( "sync/atomic" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/task" ) func TestPeriodicTaskStop(t *testing.T) { var value uint64 task := &Periodic{ Interval: time.Second * 2, Execute: func() error { atomic.AddUint64(&value, 1) return nil }, } common.Must(task.Start()) time.Sleep(time.Second * 5) common.Must(task.Close()) value1 := atomic.LoadUint64(&value) if value1 != 3 { t.Fatal("expected 3, but got ", value1) } time.Sleep(time.Second * 4) value2 := atomic.LoadUint64(&value) if value2 != 3 { t.Fatal("expected 3, but got ", value2) } common.Must(task.Start()) time.Sleep(time.Second * 3) value3 := atomic.LoadUint64(&value) if value3 != 5 { t.Fatal("Expected 5, but ", value3) } common.Must(task.Close()) } ================================================ FILE: common/task/task.go ================================================ package task import ( "context" "github.com/v2fly/v2ray-core/v5/common/signal/semaphore" ) // OnSuccess executes g() after f() returns nil. func OnSuccess(f func() error, g func() error) func() error { return func() error { if err := f(); err != nil { return err } return g() } } // Run executes a list of tasks in parallel, returns the first error encountered or nil if all tasks pass. func Run(ctx context.Context, tasks ...func() error) error { n := len(tasks) s := semaphore.New(n) done := make(chan error, 1) for _, task := range tasks { <-s.Wait() go func(f func() error) { err := f() if err == nil { s.Signal() return } select { case done <- err: default: } }(task) } for i := 0; i < n; i++ { select { case err := <-done: return err case <-ctx.Done(): return ctx.Err() case <-s.Wait(): } } return nil } ================================================ FILE: common/task/taskDerive/derive.go ================================================ package taskDerive //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: common/task/taskDerive/errors.generated.go ================================================ package taskDerive import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: common/task/taskDerive/tryAll.go ================================================ package taskDerive import ( "context" "github.com/v2fly/v2ray-core/v5/common/task" ) func RunTryAll(ctx context.Context, tasks ...func() error) []error { errors := make([]error, len(tasks)) wrappedTasks := make([]func() error, len(tasks)) for i, currentTask := range tasks { index := i wrappedTasks[i] = func() error { err := currentTask() errors[index] = err if err != nil { return nil } return newError() } } runErr := task.Run(ctx, wrappedTasks...) if runErr != nil { return nil } return errors } ================================================ FILE: common/task/task_test.go ================================================ package task_test import ( "context" "errors" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/task" ) func TestExecuteParallel(t *testing.T) { err := Run(context.Background(), func() error { time.Sleep(time.Millisecond * 200) return errors.New("test") }, func() error { time.Sleep(time.Millisecond * 500) return errors.New("test2") }) if r := cmp.Diff(err.Error(), "test"); r != "" { t.Error(r) } } func TestExecuteParallelContextCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) err := Run(ctx, func() error { time.Sleep(time.Millisecond * 2000) return errors.New("test") }, func() error { time.Sleep(time.Millisecond * 5000) return errors.New("test2") }, func() error { cancel() return nil }) errStr := err.Error() if !strings.Contains(errStr, "canceled") { t.Error("expected error string to contain 'canceled', but actually not: ", errStr) } } func BenchmarkExecuteOne(b *testing.B) { noop := func() error { return nil } for i := 0; i < b.N; i++ { common.Must(Run(context.Background(), noop)) } } func BenchmarkExecuteTwo(b *testing.B) { noop := func() error { return nil } for i := 0; i < b.N; i++ { common.Must(Run(context.Background(), noop, noop)) } } ================================================ FILE: common/type.go ================================================ package common import ( "context" "reflect" "github.com/v2fly/v2ray-core/v5/common/registry" ) // ConfigCreator is a function to create an object by a config. type ConfigCreator func(ctx context.Context, config interface{}) (interface{}, error) var typeCreatorRegistry = make(map[reflect.Type]ConfigCreator) // RegisterConfig registers a global config creator. The config can be nil but must have a type. func RegisterConfig(config interface{}, configCreator ConfigCreator) error { configType := reflect.TypeOf(config) if _, found := typeCreatorRegistry[configType]; found { return newError(configType.Name() + " is already registered").AtError() } typeCreatorRegistry[configType] = configCreator registry.RegisterImplementation(config, nil) return nil } // CreateObject creates an object by its config. The config type must be registered through RegisterConfig(). func CreateObject(ctx context.Context, config interface{}) (interface{}, error) { configType := reflect.TypeOf(config) creator, found := typeCreatorRegistry[configType] if !found { return nil, newError(configType.String() + " is not registered").AtError() } return creator(ctx, config) } ================================================ FILE: common/type_test.go ================================================ package common_test import ( "context" "testing" . "github.com/v2fly/v2ray-core/v5/common" ) type TConfig struct { value int } type YConfig struct { value string } func TestObjectCreation(t *testing.T) { f := func(ctx context.Context, t interface{}) (interface{}, error) { return func() int { return t.(*TConfig).value }, nil } Must(RegisterConfig((*TConfig)(nil), f)) err := RegisterConfig((*TConfig)(nil), f) if err == nil { t.Error("expect non-nil error, but got nil") } g, err := CreateObject(context.Background(), &TConfig{value: 2}) Must(err) if v := g.(func() int)(); v != 2 { t.Error("expect return value 2, but got ", v) } _, err = CreateObject(context.Background(), &YConfig{value: "T"}) if err == nil { t.Error("expect non-nil error, but got nil") } } ================================================ FILE: common/units/bytesize.go ================================================ package units import ( "errors" "strconv" "strings" "unicode" ) var ( errInvalidSize = errors.New("invalid size") errInvalidUnit = errors.New("invalid or unsupported unit") ) // ByteSize is the size of bytes type ByteSize uint64 const ( _ = iota // KB = 1KB KB ByteSize = 1 << (10 * iota) // MB = 1MB MB // GB = 1GB GB // TB = 1TB TB // PB = 1PB PB // EB = 1EB EB ) func (b ByteSize) String() string { unit := "" value := float64(0) switch { case b == 0: return "0" case b < KB: unit = "B" value = float64(b) case b < MB: unit = "KB" value = float64(b) / float64(KB) case b < GB: unit = "MB" value = float64(b) / float64(MB) case b < TB: unit = "GB" value = float64(b) / float64(GB) case b < PB: unit = "TB" value = float64(b) / float64(TB) case b < EB: unit = "PB" value = float64(b) / float64(PB) default: unit = "EB" value = float64(b) / float64(EB) } result := strconv.FormatFloat(value, 'f', 2, 64) result = strings.TrimSuffix(result, ".0") return result + unit } // Parse parses ByteSize from string func (b *ByteSize) Parse(s string) error { s = strings.TrimSpace(s) s = strings.ToUpper(s) i := strings.IndexFunc(s, unicode.IsLetter) if i == -1 { return errInvalidUnit } bytesString, multiple := s[:i], s[i:] bytes, err := strconv.ParseFloat(bytesString, 64) if err != nil || bytes <= 0 { return errInvalidSize } switch multiple { case "B": *b = ByteSize(bytes) case "K", "KB", "KIB": *b = ByteSize(bytes * float64(KB)) case "M", "MB", "MIB": *b = ByteSize(bytes * float64(MB)) case "G", "GB", "GIB": *b = ByteSize(bytes * float64(GB)) case "T", "TB", "TIB": *b = ByteSize(bytes * float64(TB)) case "P", "PB", "PIB": *b = ByteSize(bytes * float64(PB)) case "E", "EB", "EIB": *b = ByteSize(bytes * float64(EB)) default: return errInvalidUnit } return nil } ================================================ FILE: common/units/bytesize_test.go ================================================ package units_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/units" ) func TestByteSizes(t *testing.T) { size := units.ByteSize(0) assertSizeString(t, size, "0") size++ assertSizeValue(t, assertSizeString(t, size, "1.00B"), size, ) size <<= 10 assertSizeValue(t, assertSizeString(t, size, "1.00KB"), size, ) size <<= 10 assertSizeValue(t, assertSizeString(t, size, "1.00MB"), size, ) size <<= 10 assertSizeValue(t, assertSizeString(t, size, "1.00GB"), size, ) size <<= 10 assertSizeValue(t, assertSizeString(t, size, "1.00TB"), size, ) size <<= 10 assertSizeValue(t, assertSizeString(t, size, "1.00PB"), size, ) size <<= 10 assertSizeValue(t, assertSizeString(t, size, "1.00EB"), size, ) } func assertSizeValue(t *testing.T, size string, expected units.ByteSize) { actual := units.ByteSize(0) err := actual.Parse(size) if err != nil { t.Error(err) } if actual != expected { t.Errorf("expect %s, but got %s", expected, actual) } } func assertSizeString(t *testing.T, size units.ByteSize, expected string) string { actual := size.String() if actual != expected { t.Errorf("expect %s, but got %s", expected, actual) } return expected } ================================================ FILE: common/uuid/uuid.go ================================================ package uuid import ( "bytes" "crypto/rand" "encoding/hex" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" ) var byteGroups = []int{8, 4, 4, 4, 12} type UUID [16]byte // String returns the string representation of this UUID. func (u *UUID) String() string { bytes := u.Bytes() result := hex.EncodeToString(bytes[0 : byteGroups[0]/2]) start := byteGroups[0] / 2 for i := 1; i < len(byteGroups); i++ { nBytes := byteGroups[i] / 2 result += "-" result += hex.EncodeToString(bytes[start : start+nBytes]) start += nBytes } return result } // Bytes returns the bytes representation of this UUID. func (u *UUID) Bytes() []byte { return u[:] } // Equals returns true if this UUID equals another UUID by value. func (u *UUID) Equals(another *UUID) bool { if u == nil && another == nil { return true } if u == nil || another == nil { return false } return bytes.Equal(u.Bytes(), another.Bytes()) } // New creates a UUID with random value. func New() UUID { var uuid UUID common.Must2(rand.Read(uuid.Bytes())) return uuid } // ParseBytes converts a UUID in byte form to object. func ParseBytes(b []byte) (UUID, error) { var uuid UUID if len(b) != 16 { return uuid, errors.New("invalid UUID: ", b) } copy(uuid[:], b) return uuid, nil } // ParseString converts a UUID in string form to object. func ParseString(str string) (UUID, error) { var uuid UUID text := []byte(str) if len(text) < 32 { return uuid, errors.New("invalid UUID: ", str) } b := uuid.Bytes() for _, byteGroup := range byteGroups { if text[0] == '-' { text = text[1:] } if _, err := hex.Decode(b[:byteGroup/2], text[:byteGroup]); err != nil { return uuid, err } text = text[byteGroup:] b = b[byteGroup/2:] } return uuid, nil } ================================================ FILE: common/uuid/uuid_test.go ================================================ package uuid_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/common/uuid" ) func TestParseBytes(t *testing.T) { str := "2418d087-648d-4990-86e8-19dca1d006d3" bytes := []byte{0x24, 0x18, 0xd0, 0x87, 0x64, 0x8d, 0x49, 0x90, 0x86, 0xe8, 0x19, 0xdc, 0xa1, 0xd0, 0x06, 0xd3} uuid, err := ParseBytes(bytes) common.Must(err) if diff := cmp.Diff(uuid.String(), str); diff != "" { t.Error(diff) } _, err = ParseBytes([]byte{1, 3, 2, 4}) if err == nil { t.Fatal("Expect error but nil") } } func TestParseString(t *testing.T) { str := "2418d087-648d-4990-86e8-19dca1d006d3" expectedBytes := []byte{0x24, 0x18, 0xd0, 0x87, 0x64, 0x8d, 0x49, 0x90, 0x86, 0xe8, 0x19, 0xdc, 0xa1, 0xd0, 0x06, 0xd3} uuid, err := ParseString(str) common.Must(err) if r := cmp.Diff(expectedBytes, uuid.Bytes()); r != "" { t.Fatal(r) } _, err = ParseString("2418d087") if err == nil { t.Fatal("Expect error but nil") } _, err = ParseString("2418d087-648k-4990-86e8-19dca1d006d3") if err == nil { t.Fatal("Expect error but nil") } } func TestNewUUID(t *testing.T) { uuid := New() uuid2, err := ParseString(uuid.String()) common.Must(err) if uuid.String() != uuid2.String() { t.Error("uuid string: ", uuid.String(), " != ", uuid2.String()) } if r := cmp.Diff(uuid.Bytes(), uuid2.Bytes()); r != "" { t.Error(r) } } func TestRandom(t *testing.T) { uuid := New() uuid2 := New() if uuid.String() == uuid2.String() { t.Error("duplicated uuid") } } func TestEquals(t *testing.T) { var uuid *UUID var uuid2 *UUID if !uuid.Equals(uuid2) { t.Error("empty uuid should equal") } uuid3 := New() if uuid.Equals(&uuid3) { t.Error("nil uuid equals non-nil uuid") } } ================================================ FILE: config.go ================================================ package core import ( "fmt" "io" "log" "os" "path/filepath" "reflect" "strings" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/cmdarg" ) const ( // FormatAuto represents all available formats by auto selecting FormatAuto = "auto" // FormatJSON represents json format FormatJSON = "json" // FormatTOML represents toml format FormatTOML = "toml" // FormatYAML represents yaml format FormatYAML = "yaml" // FormatProtobuf represents protobuf format FormatProtobuf = "protobuf" // FormatProtobufShort is the short of FormatProtobuf FormatProtobufShort = "pb" ) // ConfigFormat is a configurable format of V2Ray config file. type ConfigFormat struct { Name []string Extension []string Loader ConfigLoader } // ConfigLoader is a utility to load V2Ray config from external source. type ConfigLoader func(input interface{}) (*Config, error) var ( configLoaders = make([]*ConfigFormat, 0) configLoaderByName = make(map[string]*ConfigFormat) configLoaderByExt = make(map[string]*ConfigFormat) ) // RegisterConfigLoader add a new ConfigLoader. func RegisterConfigLoader(format *ConfigFormat) error { for _, name := range format.Name { if _, found := configLoaderByName[name]; found { return newError(name, " already registered.") } configLoaderByName[name] = format } for _, ext := range format.Extension { lext := strings.ToLower(ext) if f, found := configLoaderByExt[lext]; found { return newError(ext, " already registered to ", f.Name) } configLoaderByExt[lext] = format } configLoaders = append(configLoaders, format) return nil } func getExtension(filename string) string { ext := filepath.Ext(filename) return strings.ToLower(ext) } // GetLoaderExtensions get config loader extensions. func GetLoaderExtensions(formatName string) ([]string, error) { if formatName == FormatAuto { return GetAllExtensions(), nil } if f, found := configLoaderByName[formatName]; found { return f.Extension, nil } return nil, newError("config loader not found: ", formatName).AtWarning() } // GetAllExtensions get all extensions supported func GetAllExtensions() []string { extensions := make([]string, 0) for _, f := range configLoaderByName { extensions = append(extensions, f.Extension...) } return extensions } // LoadConfig loads multiple config with given format from given source. // input accepts: // * string of a single filename/url(s) to open to read // * []string slice of multiple filename/url(s) to open to read // * io.Reader that reads a config content (the original way) func LoadConfig(formatName string, input interface{}) (*Config, error) { cnt := getInputCount(input) if cnt == 0 { log.Println("Using config from STDIN") input = os.Stdin cnt = 1 } if formatName == FormatAuto && cnt == 1 { // This ensures only to call auto loader for multiple files, // so that it can only care about merging scenarios return loadSingleConfigAutoFormat(input) } // if input is a slice with single element, extract it // so that unmergeable loaders don't need to deal with // slices s := reflect.Indirect(reflect.ValueOf(input)) k := s.Kind() if (k == reflect.Slice || k == reflect.Array) && s.Len() == 1 { value := reflect.Indirect(s.Index(0)) if value.Kind() == reflect.String { // string type alias input = fmt.Sprint(value.Interface()) } else { input = value.Interface() } } f, found := configLoaderByName[formatName] if !found { return nil, newError("config loader not found: ", formatName).AtWarning() } return f.Loader(input) } // loadSingleConfigAutoFormat loads a single config with from given source. // input accepts: // * string of a single filename/url(s) to open to read // * io.Reader that reads a config content (the original way) func loadSingleConfigAutoFormat(input interface{}) (*Config, error) { switch v := input.(type) { case cmdarg.Arg: return loadSingleConfigAutoFormatFromFile(v.String()) case string: return loadSingleConfigByTryingAllLoaders(v) case io.Reader: data, err := buf.ReadAllToBytes(v) if err != nil { return nil, err } return loadSingleConfigByTryingAllLoaders(data) default: return loadSingleConfigByTryingAllLoaders(v) } } func loadSingleConfigAutoFormatFromFile(file string) (*Config, error) { extension := getExtension(file) if extension != "" { lowerName := strings.ToLower(extension) if f, found := configLoaderByExt[lowerName]; found { return f.Loader(file) } return nil, newError("config loader not found for: ", extension).AtWarning() } return loadSingleConfigByTryingAllLoaders(file) } func loadSingleConfigByTryingAllLoaders(input interface{}) (*Config, error) { var errorReasons strings.Builder for _, f := range configLoaders { if f.Name[0] == FormatAuto { continue } c, err := f.Loader(input) if err == nil { return c, nil } errorReasons.WriteString(fmt.Sprintf("unable to parse as %v:%v;", f.Name[0], err.Error())) } return nil, newError("tried all loaders but failed when attempting to parse: ", input, ";", errorReasons.String()).AtWarning() } func getInputCount(input interface{}) int { s := reflect.Indirect(reflect.ValueOf(input)) k := s.Kind() if k == reflect.Slice || k == reflect.Array { return s.Len() } return 1 } func loadProtobufConfig(data []byte) (*Config, error) { config := new(Config) if err := proto.Unmarshal(data, config); err != nil { return nil, err } return config, nil } func init() { common.Must(RegisterConfigLoader(&ConfigFormat{ Name: []string{FormatProtobuf, FormatProtobufShort}, Extension: []string{".pb"}, Loader: func(input interface{}) (*Config, error) { switch v := input.(type) { case string: r, err := cmdarg.LoadArg(v) if err != nil { return nil, err } data, err := buf.ReadAllToBytes(r) if err != nil { return nil, err } return loadProtobufConfig(data) case io.Reader: data, err := buf.ReadAllToBytes(v) if err != nil { return nil, err } return loadProtobufConfig(data) default: return nil, newError("unknown type") } }, })) } ================================================ FILE: config.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.10 // protoc v5.28.1 // source: config.proto package core import ( transport "github.com/v2fly/v2ray-core/v5/transport" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Config is the master config of V2Ray. V2Ray takes this config as input and // functions accordingly. type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // Inbound handler configurations. Must have at least one item. Inbound []*InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound,proto3" json:"inbound,omitempty"` // Outbound handler configurations. Must have at least one item. The first // item is used as default for routing. Outbound []*OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound,proto3" json:"outbound,omitempty"` // App is for configurations of all features in V2Ray. A feature must // implement the Feature interface, and its config type must be registered // through common.RegisterConfig. App []*anypb.Any `protobuf:"bytes,4,rep,name=app,proto3" json:"app,omitempty"` // Transport settings. // Deprecated. Each inbound and outbound should choose their own transport // config. Date to remove: 2020-01-13 // // Deprecated: Marked as deprecated in config.proto. Transport *transport.Config `protobuf:"bytes,5,opt,name=transport,proto3" json:"transport,omitempty"` // Configuration for extensions. The config may not work if corresponding // extension is not loaded into V2Ray. V2Ray will ignore such config during // initialization. Extension []*anypb.Any `protobuf:"bytes,6,rep,name=extension,proto3" json:"extension,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetInbound() []*InboundHandlerConfig { if x != nil { return x.Inbound } return nil } func (x *Config) GetOutbound() []*OutboundHandlerConfig { if x != nil { return x.Outbound } return nil } func (x *Config) GetApp() []*anypb.Any { if x != nil { return x.App } return nil } // Deprecated: Marked as deprecated in config.proto. func (x *Config) GetTransport() *transport.Config { if x != nil { return x.Transport } return nil } func (x *Config) GetExtension() []*anypb.Any { if x != nil { return x.Extension } return nil } // InboundHandlerConfig is the configuration for inbound handler. type InboundHandlerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Tag of the inbound handler. The tag must be unique among all inbound // handlers Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` // Settings for how this inbound proxy is handled. ReceiverSettings *anypb.Any `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings,proto3" json:"receiver_settings,omitempty"` // Settings for inbound proxy. Must be one of the inbound proxies. ProxySettings *anypb.Any `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *InboundHandlerConfig) Reset() { *x = InboundHandlerConfig{} mi := &file_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *InboundHandlerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*InboundHandlerConfig) ProtoMessage() {} func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message { mi := &file_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InboundHandlerConfig.ProtoReflect.Descriptor instead. func (*InboundHandlerConfig) Descriptor() ([]byte, []int) { return file_config_proto_rawDescGZIP(), []int{1} } func (x *InboundHandlerConfig) GetTag() string { if x != nil { return x.Tag } return "" } func (x *InboundHandlerConfig) GetReceiverSettings() *anypb.Any { if x != nil { return x.ReceiverSettings } return nil } func (x *InboundHandlerConfig) GetProxySettings() *anypb.Any { if x != nil { return x.ProxySettings } return nil } // OutboundHandlerConfig is the configuration for outbound handler. type OutboundHandlerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Tag of this outbound handler. Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` // Settings for how to dial connection for this outbound handler. SenderSettings *anypb.Any `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings,proto3" json:"sender_settings,omitempty"` // Settings for this outbound proxy. Must be one of the outbound proxies. ProxySettings *anypb.Any `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"` // If not zero, this outbound will be expired in seconds. Not used for now. Expire int64 `protobuf:"varint,4,opt,name=expire,proto3" json:"expire,omitempty"` // Comment of this outbound handler. Not used for now. Comment string `protobuf:"bytes,5,opt,name=comment,proto3" json:"comment,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OutboundHandlerConfig) Reset() { *x = OutboundHandlerConfig{} mi := &file_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OutboundHandlerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*OutboundHandlerConfig) ProtoMessage() {} func (x *OutboundHandlerConfig) ProtoReflect() protoreflect.Message { mi := &file_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OutboundHandlerConfig.ProtoReflect.Descriptor instead. func (*OutboundHandlerConfig) Descriptor() ([]byte, []int) { return file_config_proto_rawDescGZIP(), []int{2} } func (x *OutboundHandlerConfig) GetTag() string { if x != nil { return x.Tag } return "" } func (x *OutboundHandlerConfig) GetSenderSettings() *anypb.Any { if x != nil { return x.SenderSettings } return nil } func (x *OutboundHandlerConfig) GetProxySettings() *anypb.Any { if x != nil { return x.ProxySettings } return nil } func (x *OutboundHandlerConfig) GetExpire() int64 { if x != nil { return x.Expire } return 0 } func (x *OutboundHandlerConfig) GetComment() string { if x != nil { return x.Comment } return "" } var File_config_proto protoreflect.FileDescriptor const file_config_proto_rawDesc = "" + "\n" + "\fconfig.proto\x12\n" + "v2ray.core\x1a\x19google/protobuf/any.proto\x1a\x16transport/config.proto\"\xa5\x02\n" + "\x06Config\x12:\n" + "\ainbound\x18\x01 \x03(\v2 .v2ray.core.InboundHandlerConfigR\ainbound\x12=\n" + "\boutbound\x18\x02 \x03(\v2!.v2ray.core.OutboundHandlerConfigR\boutbound\x12&\n" + "\x03app\x18\x04 \x03(\v2\x14.google.protobuf.AnyR\x03app\x12>\n" + "\ttransport\x18\x05 \x01(\v2\x1c.v2ray.core.transport.ConfigB\x02\x18\x01R\ttransport\x122\n" + "\textension\x18\x06 \x03(\v2\x14.google.protobuf.AnyR\textensionJ\x04\b\x03\x10\x04\"\xa8\x01\n" + "\x14InboundHandlerConfig\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12A\n" + "\x11receiver_settings\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x10receiverSettings\x12;\n" + "\x0eproxy_settings\x18\x03 \x01(\v2\x14.google.protobuf.AnyR\rproxySettings\"\xd7\x01\n" + "\x15OutboundHandlerConfig\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12=\n" + "\x0fsender_settings\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x0esenderSettings\x12;\n" + "\x0eproxy_settings\x18\x03 \x01(\v2\x14.google.protobuf.AnyR\rproxySettings\x12\x16\n" + "\x06expire\x18\x04 \x01(\x03R\x06expire\x12\x18\n" + "\acomment\x18\x05 \x01(\tR\acommentBD\n" + "\x0ecom.v2ray.coreP\x01Z#github.com/v2fly/v2ray-core/v5;core\xaa\x02\n" + "V2Ray.Coreb\x06proto3" var ( file_config_proto_rawDescOnce sync.Once file_config_proto_rawDescData []byte ) func file_config_proto_rawDescGZIP() []byte { file_config_proto_rawDescOnce.Do(func() { file_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_config_proto_rawDesc), len(file_config_proto_rawDesc))) }) return file_config_proto_rawDescData } var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.Config (*InboundHandlerConfig)(nil), // 1: v2ray.core.InboundHandlerConfig (*OutboundHandlerConfig)(nil), // 2: v2ray.core.OutboundHandlerConfig (*anypb.Any)(nil), // 3: google.protobuf.Any (*transport.Config)(nil), // 4: v2ray.core.transport.Config } var file_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.Config.inbound:type_name -> v2ray.core.InboundHandlerConfig 2, // 1: v2ray.core.Config.outbound:type_name -> v2ray.core.OutboundHandlerConfig 3, // 2: v2ray.core.Config.app:type_name -> google.protobuf.Any 4, // 3: v2ray.core.Config.transport:type_name -> v2ray.core.transport.Config 3, // 4: v2ray.core.Config.extension:type_name -> google.protobuf.Any 3, // 5: v2ray.core.InboundHandlerConfig.receiver_settings:type_name -> google.protobuf.Any 3, // 6: v2ray.core.InboundHandlerConfig.proxy_settings:type_name -> google.protobuf.Any 3, // 7: v2ray.core.OutboundHandlerConfig.sender_settings:type_name -> google.protobuf.Any 3, // 8: v2ray.core.OutboundHandlerConfig.proxy_settings:type_name -> google.protobuf.Any 9, // [9:9] is the sub-list for method output_type 9, // [9:9] is the sub-list for method input_type 9, // [9:9] is the sub-list for extension type_name 9, // [9:9] is the sub-list for extension extendee 0, // [0:9] is the sub-list for field type_name } func init() { file_config_proto_init() } func file_config_proto_init() { if File_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_config_proto_rawDesc), len(file_config_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_config_proto_goTypes, DependencyIndexes: file_config_proto_depIdxs, MessageInfos: file_config_proto_msgTypes, }.Build() File_config_proto = out.File file_config_proto_goTypes = nil file_config_proto_depIdxs = nil } ================================================ FILE: config.proto ================================================ syntax = "proto3"; package v2ray.core; option csharp_namespace = "V2Ray.Core"; option go_package = "github.com/v2fly/v2ray-core/v5;core"; option java_package = "com.v2ray.core"; option java_multiple_files = true; import "google/protobuf/any.proto"; import "transport/config.proto"; // Config is the master config of V2Ray. V2Ray takes this config as input and // functions accordingly. message Config { // Inbound handler configurations. Must have at least one item. repeated InboundHandlerConfig inbound = 1; // Outbound handler configurations. Must have at least one item. The first // item is used as default for routing. repeated OutboundHandlerConfig outbound = 2; reserved 3; // App is for configurations of all features in V2Ray. A feature must // implement the Feature interface, and its config type must be registered // through common.RegisterConfig. repeated google.protobuf.Any app = 4; // Transport settings. // Deprecated. Each inbound and outbound should choose their own transport // config. Date to remove: 2020-01-13 v2ray.core.transport.Config transport = 5 [deprecated = true]; // Configuration for extensions. The config may not work if corresponding // extension is not loaded into V2Ray. V2Ray will ignore such config during // initialization. repeated google.protobuf.Any extension = 6; } // InboundHandlerConfig is the configuration for inbound handler. message InboundHandlerConfig { // Tag of the inbound handler. The tag must be unique among all inbound // handlers string tag = 1; // Settings for how this inbound proxy is handled. google.protobuf.Any receiver_settings = 2; // Settings for inbound proxy. Must be one of the inbound proxies. google.protobuf.Any proxy_settings = 3; } // OutboundHandlerConfig is the configuration for outbound handler. message OutboundHandlerConfig { // Tag of this outbound handler. string tag = 1; // Settings for how to dial connection for this outbound handler. google.protobuf.Any sender_settings = 2; // Settings for this outbound proxy. Must be one of the outbound proxies. google.protobuf.Any proxy_settings = 3; // If not zero, this outbound will be expired in seconds. Not used for now. int64 expire = 4; // Comment of this outbound handler. Not used for now. string comment = 5; } ================================================ FILE: context.go ================================================ package core import ( "context" ) // V2rayKey is the key type of Instance in Context, exported for test. type v2rayKeyType int const v2rayKey v2rayKeyType = 1 // FromContext returns an Instance from the given context, or nil if the context doesn't contain one. func FromContext(ctx context.Context) *Instance { if s, ok := ctx.Value(v2rayKey).(*Instance); ok { return s } return nil } // MustFromContext returns an Instance from the given context, or panics if not present. func MustFromContext(ctx context.Context) *Instance { v := FromContext(ctx) if v == nil { panic("V is not in context.") } return v } /* toContext returns ctx from the given context, or creates an Instance if the context doesn't find that. It is unsupported to use this function to create a context that is suitable to invoke V2Ray's internal component in third party code, you shouldn't use //go:linkname to alias of this function into your own package and use this function in your third party code. For third party code, usage enabled by creating a context to interact with V2Ray's internal component is unsupported, and may break at any time. */ func toContext(ctx context.Context, v *Instance) context.Context { if FromContext(ctx) != v { ctx = context.WithValue(ctx, v2rayKey, v) } return ctx } /* ToBackgroundDetachedContext create a detached context from another context Internal API */ func ToBackgroundDetachedContext(ctx context.Context) context.Context { return &temporaryValueDelegationFix{context.Background(), ctx} } type temporaryValueDelegationFix struct { context.Context value context.Context } func (t *temporaryValueDelegationFix) Value(key interface{}) interface{} { return t.value.Value(key) } ================================================ FILE: context_test.go ================================================ package core_test import ( "context" "testing" _ "unsafe" . "github.com/v2fly/v2ray-core/v5" ) func TestFromContextPanic(t *testing.T) { defer func() { r := recover() if r == nil { t.Error("expect panic, but nil") } }() MustFromContext(context.Background()) } ================================================ FILE: core.go ================================================ // Package core provides an entry point to use V2Ray core functionalities. // // V2Ray makes it possible to accept incoming network connections with certain // protocol, process the data, and send them through another connection with // the same or a difference protocol on demand. // // It may be configured to work with multiple protocols at the same time, and // uses the internal router to tunnel through different inbound and outbound // connections. package core //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "runtime" "github.com/v2fly/v2ray-core/v5/common/serial" ) var ( version = "5.47.0" build = "Custom" codename = "V2Fly, a community-driven edition of V2Ray." intro = "A unified platform for anti-censorship." ) // Version returns V2Ray's version as a string, in the form of "x.y.z" where x, y and z are numbers. // ".z" part may be omitted in regular releases. func Version() string { return version } // VersionStatement returns a list of strings representing the full version info. func VersionStatement() []string { return []string{ serial.Concat("V2Ray ", Version(), " (", codename, ") ", build, " (", runtime.Version(), " ", runtime.GOOS, "/", runtime.GOARCH, ")"), intro, } } /* ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::c:::::::::::::::::::::::ccc:::cccc::::::c:::::cccc::::cc::::::::::::::::::::::::ccccccc::cccccccccc::cc:::;:::::::::::::::::::::::::::ccc:::cccc:ccc::::::c:::::::::::::::::::::::::::cccc:cccccccccccccccccccccc:;;::::::::::::::::::c::::ccc::::::ccc:::ccc::cccccc::::cc:cc:::ccccccccccccccccccccccccccccccccccccccc :::;::::::::::::::::::::::::::::::::::::::::::::::cc:::::::::::::::::::::::::::::::::::::ccc::cc:::::c:::::::::::ccc::ccc:::::ccc::::::cc:::::cccc:::::::::::::::::::::cccccccccccccccccccccccccc:::::::::::::::::::::::::cc::ccc::::::::::::::c::c::::::::::::c::::::::c::::::cccccccccccccccccccccccccc:;:::::::::::::::ccc::cccccccc::c:cccc::cc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccc :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::cc::::::::cc::::::::::::ccccc:::::ccccccccc::cccc::ccccccc:::c::::::::c:c:::::cccccccccccccccccccccccccc::::::::::::::::::::::::cc:::::ccc:::c::::::::::::::::c::::::::::::::::::::::ccccccccccccccccccccccccccc:::::::::::::::::::::cccccc:ccccc::ccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ::;;::::::::::::::::::::::::::::::::::::::::::::::::::::::::c:::::::::::::::::::::cc::::::::::::::::c:::cc::::c::::::::cccccc::::cccccccccc::ccccccc::c::::::::c::::::::ccccccccccccccccccccccccc:::::::::::::::::::::::::c::::cccc:::cc:::cc:::::::::::::::::::::::::::c::::::ccccccccccccccccccccccccccc::::::::::::::::::::cccc::cccccc:ccc:cccccccccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccc :::::::::::::::::::::::::::::::::::::::::::::::::::::::::cc:::::::::::::::ccc:::::c:::cc::::::::::::::::::::::cc:::c::cccc::::::::::cccccccccccc:ccc::c:::::::ccc:::::::cccccccccccccccccccccccccc:::::::::::::::::::cccccccccccccc::::::::ccc::c::cc:::cc::::::::::::::cc:::::cccccccccccccccccccccccccccc::::::::::::::::::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ::::::::::::::::::::::::::::::::::::::::::::::::::::::c::::::::::::::::::::c::::::::::::::::::::ccccc::::::::::::ccc::cc:::::cccc::::cccccccccccc::::cccccc:::ccccc:::::cccccccccccccccccccccccccc::::::::::::::::::::c:cc:::cc::::::::::::cc::cc:::::::cccc::::::c:::::cc::::::ccccccccccccccccccccccccccc:::::::::ccc::::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::cc:::::::::::::::ccccccc:::::ccccc::ccc::ccccccccccccccccccccccccccccccc:cccc::::cccccc::::cccccccccccccccccccccccccc::::::::::::::::c:::::cc::::::::cc:::cccc::::cc::::ccc:ccc::c:::::::::cccc::::ccccccccccccccccccccccccccc::::::::::cc::::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ::::::::::::::::::::::::::::::::::::::::::::::::::::c::::::::cc::::::::::::::cc:::::::ccccc:::c:::::ccc::cccc:::cccccc:ccccccccccccccccccccccccccccccccccc::::ccccccc:::cccccccccccccccccccccccccc:::::::::cc:::::::::::::cc:::cc::cccccccc::cccccc::ccccccccc:c::::::::ccc:::::cccccccccccccccccccccccccccc:::::::::ccc:::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ::::::::::::::::::::::::::::::::::::::::::::cc:::::::::ccc:::::c::::::cc:::cccc:::c:::ccccc::::ccc::cccccccccc::cccccc::ccccccccccccccccccccccccccccccccccc:ccccccccc::ccccccccccccccccccccccccccc:::::::::::::::::::::::::cc:cccc::cccccccccccc:ccc::cccccc:ccc::::::::cccc:::::ccccccccccccccccccccccccccc:::::::::ccc:::::c:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc :::::::::::::::::::::::::::::::::::c:::::::c:::::c:::::cc::::::::::::cccccc::::ccccccccc:::::::ccccccccccccccccccccccc:cccccccccccccccccccccccllccccccccccccccccccccc::cccccccccccccccccccccccccccc:::::::::c:::::::::::ccccccccccccccccccccccc::cc:::::cccccccc:::cc:::ccccc::::ccccccccccccccccccccccccccc:::::::::cccc::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc :::::::::::::::::::::::::::::cc::cc:::::::c:::::::::::::c:::cc:::::::cccccc::::cccccccc:cc:::ccccccccccccccccccccccccccccccccccccccccccccccoxk00Okxddoolllllloodxkkxdoloolccccccccccccccccccccccccc:::::::::c:::::::::cccccccc::cc::ccccc:::cccccc:ccc:ccccccccc:ccccc:::cc:cc:::ccccccccccccccccccccccccccc:::::::::ccccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ::::::::::::::::::c::::::::ccc:::cc:::::::c::::cc::c::::c:::ccc:::ccc::::::ccc:::ccccccccccccccccccccccccccccccccccccccccccccloxxkOO0OkkkkOKXK00000000KKK0KKXXNNWWWWNXKKK0kdoolclloooddollccccccccc:::::::::cc:::::cccccccccccc:ccccccccc::cccccccccccccc:cccccc:ccccc:::cc:cc:::cccccccccccccccccccccccccccc:::::::cccccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc :::::::::::::::::::::::::::::::::::::::c::ccc:::cc::cc::ccc:::::cc:::cc::::ccc::cccc:cccccccccccccccccccccccccccccccccccccccdkO0KKK0000OOkkOKXXXXXXXXXNNXKXNNNNNNNNNNNXXXXXKKKOO0KKKKKKK0Oxxoolllcc:::::::::cc::::ccc::cccc:cccccccc:ccccccccccccccccccccccccccccccc:cc:::cc:::::ccccccccccccccccccccccccccccc::::::::c::c:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc :::::::::::::::::::::ccc::::c:::c:ccc::c::ccccc:ccc::::ccccccccc:cc:cccccccccccccc:ccccccccccccccccccccccccccccccccccccccclxOOOO00KKKKKKK00KKXXNNNNXNNNNXXXXXNNNNNNNNNXXKKXXXXXXXXXXXKKXXNXXK00KOdl::::::::ccc:::::cccccccccccc:cccc::ccccccccccccccccccccccccccccc::cc:::ccc:cc::ccccccccccccccccccccccccccccc:::::::cccc::::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ::::::::::::::::::ccccccccc:cc::cccc::ccc:ccccc:ccc:::ccccccccccccccccccccccccc:ccccccccccccccccccccccccccccccccccclok000O0XNNNNNXK00KKXK0K0xxk000KXXXXXNNNNNNNXNNNNNNXXXXXNNXXXXXKX0dodxxxxddxOOxl::::::::cccc::::ccccccccccccccccccccc::cccccccccccccccccccccccccccccc::cccccc:::cccccccccccccccccccccccccccc::::::::ccccc::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc :::::::::::::c::::cccc:::c::ccccccccc::cc:ccccc:ccc::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclx0XNNNXNXXXNNNXKKKKXXX0OOxooddoodkKXKKKKXNXXNXXXXKKKKXX00KXKKXXNNXK0kko:;;::cccccc::::::::ccc::::ccccccccccccccccccccccccccccccccccccccccccccccccccccc::cccccc:::cccccccccccccccccccccccccccc::::::::cccccc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ::::::::::::::cc:ccc::ccccc:cccccccccc::cccccccccccccccccccccccccccccc:cccccccccccccccccccccccccccccccccccccccclxKNNNNXOO0KOkO0KXXNXXXXK0kololllol:lO0kkxkOKXXXXK0kkxddxO000OOkOO0KKXNNNXOxl:::coolc::::::::ccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccc:::cccccc:::ccccccccccccccccccccccccccccc::::ccccccccc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc :ccc::::::::::cc:c:::cccccccccccccccccccccccccccc:ccccccccccccccccccccccccccccccccccccccccc:ccccccccccccccccccoOXNWWNN0dcllldO0KKXXXXXXXXKxllldkkkxxdlc;;lOK00OOOd:;;;:cdk0OxkxoodxxkOOOOO0Okl,cO0kl::::::::ccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccc::cccccc::ccccccccccccccccccccccccccccc:::cc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ::cc:::::::::c::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccldO00KNNNXKOddk0KXXXXXXXXXXKK0OOOOO0KKxc;,,.;oxxddxkOx:;,;loddxxolddlc::ccccc:;cooox0KKkl:::::ccccc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::ccccccccccccccccccccccccccccc:::::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c:cc:::c::cccccc::ccccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccldkkxdxkKK000kxkkOO0000OOOOOkdloOKKO0K0dc,...;ccc:lxO0dccclllccloolllc:;;;:cc:;.,:cok0Okkkl::::cccccc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::ccccccccccccccccccccccccccccc:::::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ccc::::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclx0KK0OxdxOOOxl::codxxdoooolccoxO0K00kdoodl;''',;;;:lxOOdollccc::cc:;;;;,'',;;;;'','';:coxxl::::cccccc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccc:cccccc::cccccccccccccccccccccccccccccc::c::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllccccccccccccccccccccc cc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccldxOKXNXKOxxdl:;;,,,,,,,ckOx;:k00Okkxolc;;;:;,''',,'',:ok0kocc::,.',,;;;;,''',;,;,...',;:ccc::::::ccccc:::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::ccccclcccclcccccccccccccccccc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllcc ccc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccooloOKKKK0Oo:;,''..'.. ;xO0OxxOkxdddddoll:;,..,,,,;'.'',:ll:,,,,'..'''',,;;,,,,;;;,',,;;::cc:cccc:ccccc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::ccccllccccccclcclllcccccccclcc::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllccccccccccccc cccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclollxOOOO0Oxl:;'''',;;;cdxodxdoolllldk00Od:,'..,;,;;;,,'.....'',,,,,'''.''''.....,,,''',::ccc:cccc::ccccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc:::cccclcccllcclcccllcccccccclcc:::ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllcclcclcccccccccccccccc ccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclxkxdddxO0Oxdoc;'',;:ldxdlc:coolcc::ldxolc;;::,..,,;;;;;;'...'';cccc::;,,,,,,,'....,',;:oolccccccccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::cclcccccllccccclccclcllllclccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllccccclllcccccccccccccccccccccl cc::cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclxOOxddxkkkdoool:;,.';ccc:::::;::::::cc;;;:cdddo:'''',;;;;;;,,;:codddolc:;;,,,;,,''',,,,;:llllc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllclllcccccccclllllllllllcccccccccccccccccccccccccccccccccccccccccccccccccccllccllccccccllccccccccccccllcccccccllcccccc ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclxkkkkO00koc;,'.';;,...;;::;::;,,;:::::::;;;::::::;'''',,,,;,;;:loxkkOkxdolc:;;;,,,'...',:lllclccccc::cccccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc:cclllllllclllcccclllllllllllcccccccccccccccccccccccccccccccccccccccccccccccccccllcclllcccccccccccccccccllclccllccclccccccc ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccldddkOOO00OOOOxl,.';;'...,;;;;;;,;;;;:::;;::;,'',;;,',,,,,,,,;;:loodxxxxxxxddoolc:;;,...;cdkkdlllc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllllcllllllccclllllllclllllcccccccccccccccccccccccllccccccccccclccclcccllccccllcccccccccccccccllcclllccclccllccccccccccc cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccloodOOkkkOOOOOOkxc,;:,. ..',;;;;;;'',,,,,,,;,...,;,,;;;,,,;;;:clllllcc::clodxxddolc;'',;:ccodlccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc:cclllllllllllllllllllllllllllc:ccccccccccccccccccccllccclllcccccccccllcccccccccccccccccclccccccllcccccccclccccccccccccccc ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclollxOkxxxxxxxddddoc;'.... ..,;;;,'....';,.','.',,;::::;;;;;::cloddddooc:;::cclddolc:;:::::cllllc:cccccccclcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclccccc:ccllllllllllllllllllllllllcllc:cccccccccccccccccccccccccllccccccccccccccccccccllccccccllllcccccccccccccccccccccccccccccll ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclccdkkxdooollccc:::;'.......... ..,,,;,''',;,.''',,,;:ccc:::::::::::cccclccc::::::cloolllc:ccccccllccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclccccc::cllllllllllllllllllllllllllllc:cccccccccccccccccccccccccccccccccccccccclllllccccclllccccccccccccccccccccccccccllllllllll cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccoxdxkdl::::::cc;,,''.. .....',,,;;,,,,,'',;;:cloodollc::;;;;:;;,,;;;;::::::c:looooolcclllllllcccccccccccccccccccccccccclllcccccccccccccccccccccccccccclccclccccccccccccccccccccccllllllllllllllllllllllllllllc:cccccccccccccccccccccccccccccclllcclllllllllllcccccccccccccccccccccccccclllllllllllllllll ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllllloooc:;,'',;;,,;:..... ....'',,,,,,,,;;;:ccooddxxxxxdol:;;;:ccc::;;;,;;;;;;clollllllllllllllcccccccccccccccccccclccccclccccccccccccccccccccccclcccccccccllcccclcccccccccccccccccllllllllllllllllllllllllllllccccccccccclcccccccccccclllclllllllllllccclllccccccccccccccccccccccllllllllllllllllllllllll cccccccccccccccclccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::;,,;:;.. ';;';c;'';;'. ....'',,,;;;;;;:clodddxxxxkkkkkkxdoddxxdol:,,,,;;,;:oxdlllllllllllllllcccccccccccccccclcccccccclccccclllcccccccccllcccclcccccccclllccccccccccccccccccccccclllllllllllllllllllllllllllcccccccccccccccllcccccccccccclcccccccccccccccccccccccccccccclllllllllllllllllllllllllllllll ccc::cccccccccccccccccccclccccllcccccccccccclcccccccccccccccccccccccccccccccccccccccccccccccccclccol:;,,''''....','';:;,:;. ...',,,,;;;;;:ccloddxxxxxkkOOOO000Okxddool;'',,;;:loxOkoclllllllllllllcccccccccclccccccccccccccccclccllllllcccccccclcccccccccccclllcccccccccccccccllccccccllllllllllllllllllllllllllllccccccccccccccllccccccccccccccccccccccccccccccccccllllllllllllllllllllllllllllllllllllllll ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclcccclllccccccccccccccccclllllo:;,,,',,,,,,,,'';::::,. ..',,;;;::::cccooddxxxxkkkOOO00000Okxolllc;;:::coxkO00dllllllllllllllccccccccccccccccccccccllllcclcclllllllcccccclllccccccccccclcccclcccccccccccccccccccclllllllllllllllllllllllllllllcccccccccllccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllll ccccccllccllccccccccccccccccccccccccccccccccccccccccclccccccccccccccccccccclllcclccccccllcccclcclccc:;;;,,,,,,;;;::;;:c::;'. .....',;;:::::ccllloddxxxxxkkkkOOO000000OkxdolooddxkOOO00Oolllllllllllllcccccccccccccccllllcclllllcllcclllllllcllccllllcccccccccccccccccccccccccccccclllcccccllllllllllllllllllllllllllllc:cccccccccccccccccccccccccccccccllllllllllllllllllllllllllllllllllllllllllllllllllllllll ccccccccccccccclccllccllllllcccclccccccccccccccccccccccccccccccccccccccclllllllllcccllcclllllllcllcc:::::;;,',;;;:cl::lc:;;,,,,'...,;:::ccclollllooddddddxxxxkkkkOO00000OOOOOOO00OOOOO00xlllllllllllllccccccccccclcccclllcclllclccccclccccccccccccccccccccccccccccccccccccccccllcllllllccccllllllllllllllllllllllllllllc:cccccccccccccccccccccllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll ccccccccccccccccccccclcccllcccclllclllclllllccccccccccccccccccccccccccccccccccccccccccccccccccccccccc:::;''...,;;;::;;:clcc:::::;;,;:::cccloooooooooooodddddddxxxkkOOOOO0000000KKKK00O0KOollllllllllllccccccccccccccccccccccccccccccccccccccccccccccccccllllllllllllllllllllcccccclcccccccclllllllllllllllllllllllllllllc:ccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll cccccccccccccccccccccccccccccccccccccccccclllclccclcccllllllllcclllccccccccccccccccccccccccccccccccccc:;,.....,;;;:;;,,;;::clllllcc::ccllllllloooooooooooddddddxxkkkOOOOOOOOOOOOOO00K0KKKxllllllllllllcccccccccccccccccccccccccllllcclllllllllllcclllllllllllllcccllccllllllclccccccccccccccllllllllllllllllllllllllllllccllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll cccccccccccccccccccccccccccccccccccccccccccccccccccccccllcccccccccccllcccccclllccllllllllllllllllllllcc:;;,..';:::;;,,'.'',,;::cc:::clloooolloddodddoooooddddddxxxkkOOOOOOOkxodxkO00KKKXXOolllllllllllccccccclccclllllllllllcclllcllllcllllllllllllcccccllllcccccccccccccccllcccccccccccc::clllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllool llllllllllllccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllllclllllllcc:;,..,;;;,,,,,''...',,;;;;;:loooooddddddddddddoooddddddxxxkkkkkkkkkxddOKXXXXKKXXXKxllllllllllllcccccccccccccccllcclllcllccclccccccccccccccccccccccccccccccccccccccccccccccccccccccccllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll llllllllllllllllllllllllllllcllccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccllccc:,'';:;,,,,,,;,'...',,,,;;;clooddddoodddddddooddooodddddxxxxkkkkkkkOkxkkxdxxkkxxkOkolllllllllllccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllolllllllllllllllllllllllllllooloooloo llllllllllllllllllllllllllllllllllllllllllllllcllcccccccccccccccccccccccccccccccccccccccccccccccccccccc:,.,cllc:;;;;;;;,,'',;;;;;;:llooddddodddddddddddooooodddddxxxkkkkkO0KOdc;..'',:cccoollllllllllllccccccccccccccccccccccccccccccccccccccccccccccccllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllloollllllllllllllollllolooooooooo llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllcccccccccccccccccccccccccccccccccc::;;cllol:;;;:::;,'',;;;;;;:clloooodddooddddddoooooodddddddxxxkkOOO00K0ko:;;;:;clllllllllllllllllc:ccccccccccccccccccccclccclllcllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllollllllllllllloooolllllloooooooooollolll llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllcllollcccc:;,'',;;,,,,;;;;;:::ccllloooooooooooooooooooodddddddxxkkkkkOO0KK0kdlclolllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllollllllllllllllllllllllllllllllllllllllllllllllllllllllllloooollllloollooolllloooooooollooooooloolllllcc llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllollool::;,;;;:;'..',,,,;;;::::::ccclllllllooooooooooooooddddddddxxxxxdddxO0000Oxllollllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllolllllllllllllllllllllllllllllllllllllllllllllllllllllllllllollllllloollloollloooooooooollooollolllllcccc:::::: lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllldo:;;:cllll:'...;;;::ccccccccccccccllllllllloooooooooooodddddddddolclooollloollllllllllllllllllllllollllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllooolllloooollllllloooollloooloooooolooooooooolllllcccc:::::::::::::: llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllc:clooooollcc:;..';::clllllolcccccccccccllllllllllllloooooooooddddxkkkxdxxxxoclllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllloollllloollllllolllllllllllllllollllllllloooolllooooooolllllllloolloolloooloooolllllcccc::::;::::::::::::::::: lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllc:::::::c:::;,',;;:clooooddolcccccccccccccccllllllllloooooooddddxkOOOkdlloddooloollllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllloollllllloolloollllollllllllllllllollooollllllllllloooloooooooolllloollllllloooooolllllllccc::::::;::::::::::::::::cccccccc lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllc:::::::::;;,'''.,:cloodddddolcccccccccccllcclccclllllloooooddxxkOOOOkdoccclooooolollllllllllolllllllllllllllllollllllllllllllllllllllllllllllllllllllllllllllllllllllllllloolllllllllllllollooooooooooooolllloooooollllooooollllloooollooooooooooooooooollllllccccc::::::::::::::::::::::::ccccccclllllllo lllllllllllllllllllllllllllllllllllllllllolllllllllllllllllllllllllllllllllllllllllllllllllllllllllllc::;:::;;::cc:;'.,:coodddxxddolc:cccllllllllllllclllllllllooodxxkO000K00kdoooolllllllllollllllllolllllllllolllloolloolllllllllllllllllllllllolllllllllllllllloolllllllllllllloollllooooolloooooooooooooooooooooooooolllloollooooooooooollooooooollllllcccc:::::::;:::::::::::::::::ccccccclllllllllllllllll ooollllllllloolllllllllllllllllllllllllooollllllllllllllllllllllllllllllllllllllllllllllllllllllllloolc:;;;:::cclllllc:clodddxxxxxdolc::cccllllllllllllllllllllllooddxkO00KKKKK0kdooollllooloooooollooollllllllloolllllllllllllllllllllllllllllllllllllllllllooolllollllllllllllllllolloollooolooloolooooooooooooooooooooooolooollooooooollllllllccccc::::::::;:::::::::::::::ccccccccllllllllloolllllllllccc::: lloooolllollloollllllllllllllllllllllllllooollllllllolllllllllllloolllllllllllolloolllllllllllllllllollc:;;:::::::::cclllodddxxxxxxddlc::::ccccccclllllllllllllllllloodxxkOOOOkkxdolooooooooooooolllooolllllllllooollloolllllllloolllooloollloolllllloollllooooollllollloollloolooloooooolloollooooooloooooooooooooooooooooooooollllllccccc:::::::;::::::::::::::::::::ccccccclllllllllloollllllllcccc::::cccccl ooooolllllllllollllllolllllllolllooolllllloooollllllllllllollllllllllolllllllllllllllllllllllllloolllllccc:c::::::;;::lloodddxxxxkkxxdolcc::ccccccclcclllllllllllllllllloooddddoddoooooooooooooooooooooooooooooooloooooollllllllooollllllooollooolllooooooooolllloooooolllllloolllooooolooooollooooooooooooooooooooooooooooolc:::;;;;;;:;;;::::::::::::::::cccccccclllllllllllloollllllllcccc:::::ccccclllllllll lllllllloooooolllooooollollooolllooooooolllooooolllllllllloooooooollooooooollooooollllooooolllooolloooolcccccc:::::::clooodddxxxkkkkkxdddoollllcccccclllllllllllloooollllloooooooooooooooooooooooooooooooooooooooollloooooooolllooolllllllllloooooooooooooooollooloooooooooooollloooooolooooooooooooooooooooooooooooooooooool:;,,;;;;;::::::::::cccccccccclllllllllloollolllllllccccc:::::ccccclllllllooolloollo :::c:cccccclllllllooooooooooooolloooooollooooooolooolllooooollooolllooooooolooooooolooooollooooooloooooolc::::::;;;:clooooodddxxxxkkkkkkxxxdddolllcccccclllllllloooooooooooddddoooooooooooooooooooooooooooooooooooooooooooooooolooooolooolloooooooooooooooooooooooooooooooooooolloooooooollllllloooooooooooooooooooooooooooolc:;;;;:::::cccccccllllllllllloollllllllllccccc::cc:cccccclllllllloooooooooooooooolo ::::::::::::::::::cccccccclllllllllllooooooooooooooollloooooolooooooooooooooooooooooooooolooooooooooooool:::::::;;;cllooooodddxxxxkkkkkkkkkxxxddoolllcccllllloooooooodddddddddoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooolllllllllllcccccc::::::cloooooooooooooooooooooooooooolcccccccllllllllllllooolllllllllccccc::::cccccclllllllllooooooooooooooooooooooolooo :::::::::::::::::::::::::::::::::::cccccccllllllllllllllllloooooooooooooooooooooooooooooooooooooooooooollc::c::::;:loooooooddddxxxkkkkkkkkkkxxxdddooollccccllllllooodddooooooooooooooooooooooooooooooooooooooooodxxkkOOOkkkkkxdooooooooooooooooooooooolollllllllllccccccc:::::::::::::::::::::::loooooooooooooooooooooooooooolllllllllllllolllllllccccccccccccccccclllllllooollooooolooooooooooooooooloooooooooo lllllccccccccc:c:::::::::::::::::::::::::::::::::::::::cccccccccccllllllllllooooooooooooooooooooooooooooolc:::::::clooooooooddddxxxkkkkkkkkkkxxxxxddddoollcclllloooooooooooooooooooooooooooooooooooooooooooodxkO0KKK000OOOOOOOOkkkxolllllllllccccccccc::::::::::::::::::::::::::::::::::::::::::loooooooooooooooooooooooooooollllllllllllool::::::cccccccllllllllooooooooloooooooooooooooooooooooooooooooooooooo loolooollollllllllllllccccccccc:::::::::::::::::::::::::::::::::::::::::::::cccccccccccccclllcllllllllllllc::::::cloooooooooddddxxxkkkkkkkkkxxxxxxxxxxxxxddollllooloooooooooooooooooooooooooooooooooooooolloO0OO0KNNNNNXK0OOOOOOOOkkxlc::::::::::::::::::::::::::::::::::::::cccccccccccllllllcclooooooooooooooooooooooooooooolllllllllollolccclllllllooolloooooollloolloooooolooooooooooooooooooooooooooooooooo llllooooooooooooooooooooolllllllllllllcccccccccccccccc::c::::::::::::::::::::::::::::::::::::::::::::::::::::::;;:loodddoooooddddxxxkkkkkkkxxxxxxxxxxxxxxxxdolllolcllccccclooooooooooooooooooooooooooooooloxkxdxkO0KXXNNNNX0OxxkOOOOO0ko::::::::::::::ccccccccccccccccccllllllllllllllllolooollllloooooooooooooooooooooooooooolllllllloollollllllloooloooooooooooooooooolooooooooooooooooooooooooooooooooooooooo ::::ccccccllllllllllllloooloooooooooooloooollllllllllllllllllcccccccccccccccccccccccccccc::::::::::::::::::::::::cloooddoooooddddxxxkkkkkkkxxxxxdxxxxxxxxddddoollccll:::::cooooooooooooooooooooooooooooodddollllodxkkO0KXXNNNK0xddxkO00Odccccccccclllllllllllllllllloooolloooooooolllollllloollllloooooooooooooooooooooooooooollllllllollooolllllloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo llccccccccccc::cccccccccccllllllllloooooooooooooooooooooooooooooooolllllllllllllllllllllllllcclccccccccccccccc:::clooddddoooooddddxxxkkkkkkxxxddddxxdddddddddooollclolc:::coooooooooooooooooooooooooooxkkxlllllllllooxkO00KXXNNX0xooddddxdollllllooooooooooooooooooooolloolllllllcclcccclllloollllooooooooooooooooooooooooooooolllloloooooooollooloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo lllloooolllllllllllccccccccccccccccccccccclllllllllllllooooooooooooooooooooooooooooooooooooooooooooollllllllollcccooddddddooooodddxxxkkkkkkkxxxxxxxxxdddddddoooollllodollclooooooooooooooooooooooooodxxxxollc:;,;:clllodxkO0KXXNNNKOxdoooxdoolooooooooollllllllllllcccccccc::cc:ccccccccloolllllllooooooooooooooooooooooooooooolllollooooooolllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo oooooooooooooooooooooooolllllllllllllcccccccccccccccccccccccccllllllllllllllllooooooooooooooooooooooooooooooooooloodddddddddoooddddxxkkkkkkkkkkkkkkkxxxddooooooolllldxdollloooooooooooooooooooooodddlclll:;:;,'''',;cclllodxO0KXNNNNXKOdoddlclccccccccccccccccccccccccccclllllllllllllllloooooollloooooooooooooooooooooooooooooollllooolooooollooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooolllollllllllllllccccccccccccccccccccccccccccccclllllllllllllllllllllloddddddddddddddddddxxkkkkkkkkkkkkkkkkkxxddoooooolllodkxolllooooooooooooooooooooddol::::::::cccc:;,'''',:llloxOO00KKXXXXKOxolcclcclclllllllllllllloooooooooooooooolllllllloooooollllooooooooooooooooooooooooooooolloooooooooooolooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooolllllllllllllllllllcccclccccccccccccccccldxxddxxxdddddddddddxxkkkkkkkkkkkxxxxxkkkxxddddooooodxkkdlllooooooooooooooooooddolc:ccc:::lxkOkkkxddoc:;,;codk000OOOKKKKXXKOdooooooooooooooooooooooooooooooooooooooooooollooooooolllooooooooooooooooooooooooooooolllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxxxxxxxxxxdddxddddddxkkOOOkkkxxxxxxxxxxxxxddxxddddddxkkdooodxxdooooooooooooddolcllllccccloxkkkkOO00KKKKK00KKK00000OOKKKKKXXX0kdoooooooooooooooooooooooooooooooooooooooollloooooolllooooooooooooooooooooooooooooolllooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooxkkkkkxxxxxxxxxxxxddddxkOOOOOkkxxxdddddddddddxxxxxddxxkkOOOkdxO00OOkxxdooooooolllllllllccclloddddxxxkOKKKXXXK0OOO0KKK00KXXKKXXXXKOxoooooooooooooooooooooooooooooooooooooollloooooollloooooooooooooooooooooooooooooollooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkOkkkkkkkkkkkkkkkxxdddxkOO0OOkxxddddddddooddxkkkkkxxkkkkOO00xloxkO0KKK0Okxollllllllllllcccclodooooooodxxxxk0KK0OOO00KK00KKKKKKXXXXX0kdoooooooooooooooooooooooooooooooooooolloooooollloooooooooooooooooooooooooooooollooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkOOOOOkkkkkkkOkkkkkkxxdddxOO00OOxxdddooooooddkkkOOOkkkkkOOOOO0Oolooddxkkxxddoollooollllllcc:::clllccclloolccldk0KK00000000O0KKKKKKKKKXX0xooooooooooooooooooooooooooooooooooollooooooollooooooooooooooooddoooooooooooollooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkOO00000OOOOOOOOOOOOOOkkxxddxkO000OkxddoooooddxkOOOOOOOOOOOOOOOO00d:cooooooooddooooooolllllllccc:::::,,,'':olccccldk00000O000OO00KKKKKKKKKX0xooooooooooooooooooooooooooooooooooolooooooolllooodoooooooodddddooooooooooooolooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooddxkOO00000000000OOOO0000000OOkkxxxxkO000OkdddddddxkkO00000OOOOOOO00OO000xl:cloddddddddoooollllllllllccc:cclcclodxxollllllodxO0000000OO0KKKKKKKXXXKKOdooooooooooooooooooooooooooooooooolooooooolllodoodoooooooddoooooooddddooooolloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkkO00KKKKKKKKKKK00000000000KKK00OkkkxxkO0KK0kxdddxxkOO00KKK00OOOO0000000KK0kdc::coddddddooooolllllllllllccccoddxkkkxxxdoodddddoxO0000000O0KKKKKKKXXXXXK0kdoooooooooooooooooooooooooooooooloooooooollooooddoodddddddddddoodoooooooolloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooood ooooooooooooooooooooooooooooooooodooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkkO0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKXKKK00OOkkxk0KKKOOkkOOO000KKXXKK0OO00KKKKK0KKXK0kol::clooooooolllllllllllllllllllodddoooddxdodddxxdddkO00000000KKKKKXXXXXXKXKOdooooooooooooooooooooooooooooooooooooooollooooodoodddddddddddoodooooodoolloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo oooooooooooooooooooooooodoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodxkO0KKKKKKKKKKKKKKKKKKKKXKKXXKKKKKKKXXXXXXXK00OkkkOKXK00000KKKKKXXXXKKKKKXXXXXXXXXXXXX0kdlccccllllllllllllllllllllllllllcclllllloddxxxdxdddddkO0000000KKKKKXXKXXXKKK0xoooooooooooooooooooooooooooooooooooooollooddddoodddddddddddddddddddddoolodooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodddoooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooddddddoodkO0KKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXXNNXNNNXKK00OkO0KXKKKXXXXXXXXNNXXXXNNNNNNNNXXXNNNNNX0kddocccccccccclllllllllllllloddoooollllllooxkxxxdddddxkO00000000KKKKKKKKKKKKOdoooooooooooooooooooooooooooooooooooooolooddddddddddooddddddddoddooddooloooooooooooooooooooooooooooooooooooooooooooooooooooooddddooodddooddooooooood oooooooooooooooooooooooooooooooooodooooooooooooooooooooooodddooododddddooddooddxkOKXXKXKKKKKKKKKKKKKXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNXXXKKKXXXXXXNNXXXXNNNNNNNNNNNNNNNNNNNNNNNNNXK0kdol:;;;:::::ccccccllcccldkkOkxddooooooodxkkkxxxxxxxxkOOOOO00000KKKKKKKKKKOdooooooooooooooooooooooooooooooooooooolloddddddodddooddddddddddddddddooooooooooooooooooooooooooooooooooooooooodddooodddoooddddddddddddddddddooooodd oooooodddddoooddoooooddooooooooooooooooodddoooddoooooooooooooddddddoooddxxkkO0KXXXXXXXXXXXXXXKXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNXXXNNNNNNNNNNNNNNNNNNNWWWNNNNNNNNNNNNNXX0Okdolc;;;;;;::::::ccccccldxxxdoooodddddddxxxxxxxxxxxdxxkkOOOOOOO00000KKKKK0kdoooooooooooooooooooooooooooooooooooolloddddddddddddddddddddddddodddooooooooooooodoooooooooooooddooddoooooooooodooodddooodddddddddddddddddddoodddo dooooddoodddddddoooooooodddddooooooooooooooooodoodddoodoodddddoooddxkO0KXXXXXXXXXXXXXXXXXXKKKK00KK00KKKKKXXXXXXXXXXXNNNNNNNNNNWWWWWWNNNNNNNNNNNNNWWNNNNNNNNNNWWNWWWWWWWNNWNNNNNNXK0kxdolc:;;;;;;;;;;:::ccc:codoooollooooddddddddddddddddddxxkkkkkkOOOOO000KKK0xoooooooooooooooooooooooooooooooodooooloddddddddddddddddddddddddddddoolodoooooddddooodoooooooooddoooooddoodoooddddodddddddddddddoddddddddddddddddo ddoooooddddddddddddddddddddddddddddddddddoooooddddddooddddddodxkO0KXXXXXXXXXXXXXXXXXXXXXXKKK0Okkkxxk00KKXXXXXXXXNNNNNNNNNWWNNNNWWWWWWWWWWWWWWWNWWWWWNNNNNWWWWNNNWWWWWWWWWWWNNNNNXK0Okxdoolc:;;,,,,,,;;:clldO00OdoooolllllllllllooooooooodoodddxxxxxkkkkOOO0KK0Oxoddooooooooooooooooooooooooooooooooolodddddddddddddddddddddddddddddoooddddddooddooddooooooooooooddodddodddddddddooddddddddddddodddddoddddddddddd odddddddddddddddddddddddddddddddddddoodddddddddddddddddddodxO0KXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKK0000KKKXXXXXXXXNNNNNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWNNNNNNNNNNNWWWWWWWWWWWWWWNNXXXK0Okxxddolc:;;,,,,;cdkO0KXXNX0xoooolllllllllllllllllllollllooddddxxxkkOO000000xoodddooooooooooooooooooooooodddddoolodddddddddddddddddddddddddddddoooddooooooddoooodooddddddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddoddddddddddddddoodddoddddddddddddddddddddk0KXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKXXXXXXXXXNNNNNNNNNNNNNNNNWWWWWWWWWWWNNNNWWWWWWWNNNNNNNNNNNNNNNWWWWWWNNNNNXXXK00Okkxxddolc:;;;;;:okOO0KXXXXKkdooollllllccccccccccccccclllllooddddxxkOOO000KKkdoddooodddoooodddooooooooooddooooolooddddddddddddddddddddddddddddoooddoooooddddooodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddddddddddddddddddoddddddddddddddddddddddk0KKKKXXKXXXXXXKXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKXXXXXXXXXXNNNNNNNNNNNNNNNNNWWWNNNNNNNNNNNNNNNNNNNNNNNXXXXXXNNNNNNNNNNNNNNNNXXXK00OOkkxxxdolc:::::lxkO0KKXXXXX0kdooolllllllccccccccccccccclllloooodddxkkOOO0KKOdoddoooddooooooooooooodoooodoodooooodddddddddddddddddddddddddddddooododddddddddoddddddddodddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddddddddxO0KKKKKKKKKKKKKKXXXXXXXXXKKXXXXXXXXKKXXXXXXXXKKXXXXXXXXNNNNNNNNNNNNNNNNWNNNWWWWWWWWWWWNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWNNNNXXKK000OOkkkxxddolcc:codxO0KKKXKXXKKkdoooollllllcccclccccccccccccclllloodxxkOOOOOOkdodddoddddoooodddooooooooodddddoooodddddddddddddddddddddddddddddooodddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddddddddddddddddddddddddddddddddddddddxO000KKKKKKKKKKKKKKKKKKKKKKKKXXXKKXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWNNNNNWWWWWWWWWWWNWWWWWWNNNNXXXKK000OOOOkkxxxddolclodxO00KKKKKKKK0kooooolllllcccccccccccccccc:ccllllodxxxxdoodxxdddooddddooddddddddoooooodddddoolodddddddddddddddddddddddddddddooodddddddddddoddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddddddxO0000KKKKKKKKKKKKKKKKKKKKKKKKKKKKKXKKXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWWWWNWWWWWWWWWWWWWWWWNNNNNNNXXXK0000OOOOkkkkxxddolloxkO0000KKKKKK0Oxdoloollllcccccccccccccc::::cccllllolllccclxxdododddooddddddddooddoooodddddolodddddddddddddddddddddddddddddoooddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddddddddddddddddddddddddddddddddddddxkOO00000KKKKKKKKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWWWNNNNNNNNNWWWWWWWWNNNNNNNXXXKK0000OOOOkkkkxxddooddxkO00000KKK00Okxxddolllllllllllcccccccccccclllcccc::cccccdkxddddddooodddddddddddoooddddddooodddddddddddddddddddddddddddddolodddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddddxkO000000KKKKKKKKKKKKKKK0KKKKKKKKKKKXXXXXXXXNNXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWNNNNNNNNNWWWWWWWWNNNNNNNNNNNNNNNNNNNNNNNNNNXXXXKK000OOOOOkkkkkxddoddxkOO00000KKKOkxxkOOkkxdoollllllccccccccccccccc::;;::ccllldxkxddddddodddddddddddddooddddddoooddddddddddddddddddddddddddddddooddddddddddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddddxkOO00000KKKKKKKKKKK0000000KKKKKKKXXXXXXXXXXXXXXXXXXNXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNWWNNNNNNNNNNNNNNNNNNNXXXXXXXXXXXXXXNNNNNNNNNNXXXXKKK0000OOOOOkkkkxdddddxkkOO00000KK0kxxkOO000KK0Oxdlllllcccc::::::::;;;;;;;:clooodxxddddddddddddddddddddooddddddoooodddddddddddddddddddddddddddddooddddddddddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddddddddddddddddddddddddddddddddddxkOOO000000000000000000000000000KKXXXXXXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNXXXXKKKKKKKKKKKKKKKKXXXXXNNNNNNNNNXXXXXXKKK000OOOOOOkkkxxddddxxkkOO000KKKKOxxkO00KKKKKXXK0kdlllcccc::::;;;;;;;;;;:cloodddxxdddddddddddddddddddooddddddoooodddddddddddddddddddddddddddddooddddddddddddodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddxkkOOO00000000000000000000000000KKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNXXXKK00O000000KKKXXXXXNNNNNNNNNNXXXXXXXKKK00000OOOkkkkxxxddddxxkOO0000KKK0kkO0KKKKKKXXXXXXKxllccc:::::::;;;;;;;;;:clodddxkkddddddddddddddddddooddddddooooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddxkOOOOO000000000000000000000000KKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXNXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNXKKK000KKXXXXNNNNNNNNNNNNNNXXXXXXKKKKK0000OOOOkkkxxxxxdddxkOOO00K000K0O0KKKKKKKXXXXXXXKxllccc::::::::;;;;;;;::coddxxkOkdddddddddddddddddoodddddddoooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddddddddddddddddddddddddddddddddxkkOOOOOOO0000000000000000000KKKKKKKKXXXXXXKKKKKKKXXXXXXXXXXXXNNXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWWWNNXXXXXXXXXXXNNNNNNNNNNNNXXXXXXXXKKKK0000OOOOkkkkxxxxdddxxkOO000000KK00KKKKKKKKKXXXXXNKxlccc:::::::::;;;;;;;:clodxxkkkkdddddddddddddddddoddddddddooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddddddddddddddddddddddddddddddddxkkkOOOOO0000000000000000000000000KKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWWWWNNNNNNXXXXXXXXXXNNNNNXXNNNXXXXXXXXKKKK0000OOOOkkkkkxxxxxxxxkOOO0000000000KKKKKKKKKXXXXXXKxllc:::::::::;;;:;;;;:codxkkkkkkddddddddddddddddoodddddddooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddx dddddddddddddddddddddddddddddddddddddddddddddxxkkkkOOOOO000000000000000OO000000000000000KKKKKKKKKKKKXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNXXKKKKXXXXXXXXLOVEXYOUXFOREVERK0000OOOOkkkkxxxxxxxxxkkOOOO000000000000KKKKKKXXXXXX0dllc:::::::::;;;;;;;;:lodxkkkkkkxddddddddddddddoodddddddooddddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddxxkkkkOOOOO000000000O0OOOOOOOOOOOOOOOOOO00000000000KKKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNXXNNNXKK00KKKXXXXXXXXXXXXXXXXKKKKKKKK0000OOOkkkkkxxxxxxxxkkOOOOO00000O0000000KKKKKKXXXXKxllcc::::;;;;::::;;;;::codxxxxkOkddddddddddddddoodddddddooddddddddddddddddddddddddddddddooddddddddddddddddddddddddddddddddddddddddddddddddddddddxxdddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddxxkkkOOOOO000000000OOOOOOOOkkkxxkkkkkkkkOOOOOOO000000KKKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNXNNNNXKK0KKKXXXXXXXXXXXXXXXXKKKKKKKK00000OOOOkkkkxxxxxxxxkkkOOOOOOOOkkOOOOO0000KKKKKKKKKxllccc::::;;:::;;;;;;;::cloodxxkkxddddddddddddddddddddddooodddddddddddddddddddddddddddddoodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddxxxkkOOOOOO0000000OOOOOOOOOkkxdddxxxxddxxkkkOOOOO000000KKKKKKKXXXXXKXXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNXXXXXXXXXXXXXXXXXXXXXXKKKKKKKKK0000OOOOkkkxxxxxxxxxxxkOOkkOkkkddxkkkkOO000000KKKKKklllccc::::::::;;;;;;;;;;:cloddxkkxddddddddddddddddddddddoodddddddddddddddddddddddddxddxdodddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddddddddddddddddddddddddddddddddddddxxkkkkOOOOOO000OOOOOOOOOOkkxollllooolodxxxxkkOOOOO000000KKKKKKKKXKKKKKXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNXXXXXXXXXXXXXXXXXXXKKKKKKKKK0000OOOOOkkkxxxxxdlccoxkkkkkkxxdlldxxxxkkOOO0000000Kkollccc:::::::::::;;;;;;;;;:codxkkkxddddddddddddoddddddddoodddddddddddddddddddddddddxxxxdooddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddddddddddddddddddddddddddddddddddddddxxkkkkOOOOOOOOO000000OOOOkkdlcccllccloddxxxkkkkOOO0000000KKKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNXXKKKXXXXXXXXXKKKKKKKKKKKKKKK0000OOOOkkkkxxxxdol:codxkkkkxdo:;clodddxxkkOO0000KKK0dlllcc:::;;:::::::;;;;;;;;;:cldxkOkxdddddddddddoddddddddoodddddddddddddddddddddddddxxxxdooddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd dddddddddddxxddddddddddddddddddddddddddddddddddxxxxkkkkOOOOOO000000OOOOOOOkdcccccccloodddxxxkkkkOOOOOO00000KKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXNNNNNNNNNNNNNNNNNNXXKKKKKKKKXKKKKKKKKKKKKKKKK0000000OOOkkkxxxxxddollodxxxxxdl:',;cclooddxxkkOO000KKKkollccc:::::;;;;::;;;;;;;,;;;cldxkOOkdddddddddddddddddddoodddddddddddddddddddddddddddxxdooddddddddddddddddddddddddddddddxxxxddddddddddddddddddddddddddddddddxxxxx ddddddddddddddxxdddddddddddxdddddddxdddddxdooddddxxxkkkkkOOOO000000OOO000OOkocccccclooddddxxxxkkkkkkOOOOO00KKKKKXKKKKKKKKKKKKKXXXXXXXXXXXXXXXXXXNNNNNNNNNNNNXXXKKKKKKKKKKKKKKKKKKKKK000000000OOOOkkkkxxxddxdoooddxxxxdl;'',;;:cclooodxxkkOO000K0dllcccc::::::::;;;;;;;;;;;;;;:ldkO00kddddddddddddddddddoodddxxdxdddddddddddddxxxdddddxdoodxdddddddddxddddddddddddddddddddddddddddddddddddddddddddxxddxxxxxxxxxxx dddddddddddddxxdddddddddddddddddddxxdddddddoooodddxxxkkkkkOOOO0000000000000Oxoccc:clllooodddxxxkkkkkkkkkkkkO0KK000000KKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXXNNXXXNNXKK000KKKKKKKKKKKKKKKK00000000OOOOOkkkkxxddddddoooddddddl;'.',,;::ccllooodxxkkkOO00koccccc::::::::;;;:;;;;;;;;;;;:ldkO00Oxddddddddddddddddooddddxxddddddxxddxddxxxxxxxxdxddodxxdddddddddddddddddddxddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxx xdddxdddxxddddddddddddddddddddxxxxxxdddddxdooooddddxxxxkkkOOOO00000000KKKK00Oxl::ccllllooooodddxxxxxxxkxxxdooooxkkOOO000000KKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXNNXXK0000000KKKKKKKKKKK00000OOOOOOOOkkkxxxdddddooooooodddl'...',;;;::cccllloodddxxkkOOdlcccc::::::;;;;:::;;;;;;;;;;;:ldkOO0Oxddddddddddddddddodxxdxxdxxxxxxxxxxxxxxxdxxxxxxddodxxddddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxdddxxxxxdddddddddddddddxxxddddddddddddooooodddddxxxkkOOOOO00000KKKKKKK00koc::ccllllllllooodddddxxxxxdoc::ldkOOO000000000KKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXNXXXK00000000000000000000OOOOOOkkkkkkxxdddddoooooooodool,....',;;;;::cccccclllooddxxkxocccc::::::;;;;::;;;;;;;;;;;;;:lxkOOOOkdddddddddddddddoodxdxxxxxdxxxxxxxxxxxxxxxxxxxxdodxxdddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ddddddxxxxxxxxxxxxdxxxxxxxxxxxxxxxxxxxxddddooooooodddxxxkkkkOOOO000KKKKKKKKK00xl:;:ccllccccclllooodddddxxxdlcldkOOO0O00OOOO000000000KKKKKKKKKKKKKKKKKKKKKKXXNNNXKK000OO00000000000000OOOOkkkkkkkxxxdddooooooooooooo:',;''',;;;;:::ccccccccllloddxxocccc::::::::;;:;::;;;;;;;;;;;;:lxkkkOOkxdddddddddddddoodxxxxxxdddxxxxxxxxxxxxxxxxxxxdooxxddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx dddddxxxddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdolloooooddxxxkkkkkOOO000KKKKKKKKKK0kd:,;:ccccccccccclloooodddddddodxkkkkOOOOOOOOOOOOOO000000000KKKKKKKKKKKKKKKKXXNNNNXXK00OOOOOOOO0OO000OOOOOOkkkkkxxxxdddooooollllloool;':oc;,,;;::::::cccccccccllooddocccc:::::::::;;;;;;;;;;;;;;;;;;coxxkkkOkxddddddddddddoodxdxxxxxxxxxxxxxxxxxxxxxxxxxxxddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxdxxxxxxxxxxxddxxxxxxxxxxxxxxdxxxdxxxxxxxdollooooooddxxxkkkkOOO00KKKKKKKKKKK0Oxc,',:ccccccccccccllllooooooodddxxxxkkkkkkkkkOOOOOO00000000000KKKKKKKKKKKKXNNNNNNNXXXK00OOOOOO0OOOO0000OOOOkkkkkxxxddddooooooolooodl,':ddl:;;;::::::ccccccccllllooolcccccc::::::;;;;;;;;;;;;;;;;;;;:loodxxkkkddxdddddddddoodddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxdxxxxxxxxxxxxxxxdxxxxxxxxxxxdolllooooooddxxxkkOO0000KKKKKKKKKKK0Oxl;..';::ccccccccccccllllloooooddddxxxxxxxkkkkkOOOOO0000000000KKKKKK0KKKXXNNNNNNNNNNNNNXXKKKKKKKKKKKKKK0000OOOOOkkxxddddddooooooodddc,cddddl:;:::::cccccccccclllloolcccccc::::::::;;;;;;;::;;;;;;;;;:ccloddxkxdddddddddddddxxxxxxxxxxxxxxxxxxxxxxxdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdxxxxxxxxxdollloooooooddxkOOO000KKKKKKKKKKKK00Oko:,..',;::::ccccccccccccllllooooddddxxxxxxxkkkkOOOOO0000KKKKKKKKXXXXXNNNNNWNNNNNNNNNNNNNXXXXXXXXXXXXXKKKKK0000OOOkkxxxdddddodddddxko:cdxdddoc::::cccccccccccllloddllccccc::::::::;;;;;;;::;;;;;;;;;;::cllodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxolllooodddddxkO0000KKKKKKKKKKKKKK00Oko:,...'',;;::ccccccccccccccllllooooddddxxxxxkkkOOOO000KKKKXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNXXXXXXXXXXXXKKKKKK00000OOkkkxxxxddddxxxkOklcdxdddddlc::cccccccccccclodxdlcccccc:::::::;;:;;;;;;;;;;;;;;;;;;;;:cloodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdooooddxxxkkOO000KKKKKKKKKKKK00K000Okdc,...'',,;;:cccccccccccccccccllllloodddxxxkkkOO000KKKKXXXXXXXXNNNNNNNNNNNNNNNNWWNNNNNNNNXXXXXXXXXXXXXKKKKKKK00000OOOkkkkxxxxxxxkkOOdldxdddddddlccccccccccccllodxdlccccc::::;;;;;::;;;;;;;;;;;;;;;;;;;;;:ccloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdddddxkkOO00000K0KKKKKKKKKK0K000000Oxdc,...',,,,;;::cccccccccccccccllloooddxxkkOOOO0000KKKKKKKKXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNNNXNNXXXXXXXXKKKKKKK000000OOOOkkkkkkkkkxkkkkxodxxxxxxxxxdlccllccccccllodddlcccccc::::::::::;;;;;;;;;;;;;;;::;;;;;;:codxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkOOO00K00KKKKKKKKKKKK00K00000OOxdc,..''',,,;;::cccccccccclllllooodxxkkOOOO000000000KKKKKKKXXXXNNNNNNNNNWWWWNNNNNNNNXXXNXXXXNNNNNXXXXKKKKKKK000000OOOOOOkkkkkxkkxxxkkddxxxxxxxxxxxxdollllcccccllooolccccccc:::::::::;;;;;;;;;;;;;;;;::;;,,;;:ldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkOOOO000000KKKKKKKK000000000000Okxoc'..''',,,;;::cccccclllllooodddxkkOOO0000000000KKKKKKXXXXXXNNNNNNNWNNNWWNNNNXXXXXXXKXXXXXXXXXXXXXXXKKKKKKK0000000OOOOOOkkkxxxxxxxxxxdxxxxxxxxxxxxxxdollllcccclllolcccccccc::::::::::::;;;;;;;;;;;;;::;;;;;:cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkOOOO00000000KKKKKKK0000000000000Okxo:'.''',,,;;:::ccclllooooodddxxkkOOOO000000000KKKKXXXXXXXXXXXNNNNNNNNNNNNNXXXXKKKKKKKK00KKKKKKKKKKKKKKKKKKKKK0000000OOOkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxdolllcccllllllccccccc:::::::::;;;;;;;;;;;;;;;;;::;;;;:cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkOO00000000000KKKKKKKKK0000000000OOkxo:'.''',,,;;::cclllooooddddxxxkkkkOOOO0000KKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXKK000OOOO000000K0000KKKKKKKKKKKKKK000000OOOkkkkxxxxxxxxxkkxxxxxxxxxxxxxxxxxxdollllccllllcccccccccc:::::::;;;;;;;;;;;;;;;;:::ccloodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkOO0000000000000000K00KK000000000OOkkxl;,'''',,;;::cclloooooddddxxxxxkkkkOOOO000KKKKKKKKXXXXXKKKKKKKKKKKKKKKKKKKKKK0000OOOOO00000KK0000000K000KKKK000000OOOOkkkkkxxxxxxxxxkkxxxxxxxxxxxxxxxxxxxxddolccclccccccccccc:::::::::;;;;;;;;;;;;;;;::clodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkOO000000000000000000000000000000OOOkdl;;,,,,,,;;::ccloooooodddddxxxxxxxkkOOOOO0000000KKKKKKKKKKKKKKKKKK0KKKKKKKK0000KKKK0000KKKKKKKK000000000000000000OOOOOkkkkkkxxxxxxxxkkxxxxxxxxxxxxxxxxxxxxxxxollcccccccccccc:::::::::::::;;;;;;;;;;;;::clloxkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkOO000000000000000000000000000000OOkxdl::;;,,,;;;::cloooooooddddddddxxxxkkkkkOOOO0000000000000000000000000KKKKKKKKKKKKXXKKKKKKKKKKKKKKKK0000000000OOOOOOOOkkkkkkkkkxxxxxxkkkxxxxxxxxxxxxxxxxxxxxxxxxdolccccccccc::::::::::::::::;;;;;;;;;;;;::ccloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddoool xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkOOO000OOOOO00000000000000000000OOOkxdlc::;;;;;;;::clooooooooddddddddddxxkkkkkOOOO0000OOOOOOOOOOOOO000000KKKKKKKXXXXXXXXXXKKKKKKKKKKKKKKKK0000000OOOOOkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdocccccccc::cccc:::::::::;;;;;;;;;;;;;;;;:cloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddooollllllcccc xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddxkkOOOOOOOOOOO0000000000000000000OOOkxdolcc::;;;;;:cllloooddddddxxxxxxxxxxxxkkkkOO0000OOOOOOkkkkkkOOO0000KKKKKKKXXXXXXXXXXXKKKKKKKKKKKKKKKKK0000000OOOOOkkOkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxolccccccccc::::::::::::;;;;;;;;;;,;;;;;;:clodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddoooolllllcccccccccccccl xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdddxkkkOOOOOOOOOO00000000000K000000OOOkxdxdlcc::;;;;:cclloooddddxxxxkkxxxxxxxxkkkOOOOOO000OOOOOkkkOOOO0000KKKKKKKKKKXXXXXXXKKKKKKKKKKK0000000000000000OOOOOOOOOkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdlccccccc::::::::::::;;;;;;;;;;;;;;;;;;;:coxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkxxxxxxxxxddddoooolllllllccccccccccclllllllloooo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxooodxxkkkOkkkkOOOOO00000000KK000000OOkxdxxxdocc:::;::ccclllooddxxxkkkkkkkkkkkkkkkkOOOOOOOOOOOOOOOOOO000000000KKKKKKKKKKKKKKKKKKK000000000000000000000OOOOOOkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdllccccccc:::::;;;::::;;;;;;;;;;;;;;;;;cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkxxxxxxxdddddoooolllllccccccccccccllllllllllooooodddooooooo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxooodxxkkkkkkkkkOOOOO00000000000000OOOkxdxxxxxolc::::::ccclloodddxxkkkkkkOOOOOkkkkkkkkkkkkkkkkkkkOOOO00000000000000000KKKKKKKKKK00000000000000000000OOOOOOkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdollccc::::::::;;:;:;;;;;;;;;;;;;;;;cdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdddddooooolllllllcccccccccccllllllllooooddddddooooooooooooodddd kkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxooodxxkkkkkkkkkOOOOO00000000000000OOOkxdxxxxxxdlc:c:::cccclloodddxxxxkkkOOOOOkkxxxxxxxxxkxxxxxxxkkOOOOOOOOOOO000000000000000000OOkOOOOOOO00000000OOOOOkkkkkxxxxxxxxxxxdddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxolccc::::::::::::::;;;;;;;;;;::;cdxxxxxxxxxxxxxxxxxxxxxxxkxxxxkxxxxxxxxxxddddooooolllllllcccccccccccllllllllloooooodddddooooooooooooooodddxxxkkkkkkkk xxxxxxxkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxdoddxxkkkkkkkkkOOOOO0000000000000OOOkkxdxxxxxxxxolcc::ccllllloooodddxxxkkkOOOkkxxxddddddddddddxxxkkOOOOOOOOOOOOOOOOOOOOOO000OOkkkxxkkkkOOOOOOOOOOOOOOkkkxxxddoooodddddddoooodddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdolccc::::::::::::;;;;;;;;;;::cdxxxxxxxxxkxxxxxxxxxxxxxddddddooooollllllllccccccccccclccllllllllloooodddddddooooooooooooodddddxxxxkkkkkkkkkkkkkkkkkk llllooooooddddddddxxxxxxxxkkkkkkkkxkkkxdddxxxkkkkkkkkkOOOOOOO00000000000OOkkxdxxxxxxxxxxdocc:clllllllllooooddxxxxkkkkkxxddoooooooooddxxxkkkkkkkkkkOOOOOkkOOOOOOOOOOOkkkxxxxxxxkkkkkkOOOOOkkkkxxxdddoollooooooooollooodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdolc::::::::::::::;;;;;;;;:coxxxxxxxxddddoooooooollllllllcccccccccccclclllllllllllooooddddddooooooooooooooooddddxxxkkkkkkkkkkkkkkkkkkkkkkkkkkxxxdd llllllcccclllllllllllooooooooddddddddddddxxxkkkkkkkkkkOOOOOOOO00000000OOOkkkxdxxxxxxxxxxxxoccccllllllllllllooodddxxxxxddooolllllllooddxxxkkkkkkkkkkkkkkkkkkkkkkkOOOOOkkxxxxxxxkkkkkkkkkkkkxxxxdddoooolllllloooooooooodxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxolc:::::::::::::;;::;;;:cloooolllllllccccccccclcclcllccllllllllloooooodddddddooooooooooooooddddddxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxddooollcccc dddddooooooooollllllllllcccccllllllllooddxxxxkkkkkkkkkOOOOOOOOOO000000OOOOkxdxxxxkkkkkkkkkxoccclcccccllllllllllooooooooolllllcccclloddxxxxkkkkkkkkkkkkkkkkkkkkkkkOOOOOkkkkkkkkkkkkkOOkkkkkxxxdddddoooolllllloooooodddxkkxxxkkxxkkxkkkkxxxxxxxxxxxxxxxxxddddddddddoolcccc::::::::::;;;;;;:cccccccccllllllllllloooooooooodddddddddoooooooooooooodddddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxddddooolllccc:::::::::: ddddddddxxxxxxxdddddooooooooooooooollodddxxxxkkkkkkkkkOOOOOOOOOOOO000OOkkkxxdoodddddddddddddlcccclcclllllllllllllllllollllllccccclooddxxxxkkkkkkkkkkxkkkkkkkkOOOOOOOOOOOkkkOOOOOOOOOOOOkkkkxxxxdddooodddoooooodddddddxxxxxxxxdddddddddoooooooooooolllllllllllllllclccllcccccccccc:;;::cllooooooooooodddddddddddddooooooooodddodddddddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxddddoolllccc:::::::::::::::::::::: dddddddddddddddddoooodddddddddxxddxxddddxxxxkkkkkkkkkkkOkOOOOOOOOO000OOkkkxxolcllllllllllllllcccclllllllllllooooooooooooooolllcclloodddxxxkkkkkkkkkkkkkOOOOOOOOOOOOOOOOOOOOO000000OOOOOOOOOkkkxxddooddxxxxxdddddddddollllllllllllllllllllllccccllcccllllllllllllloolooooooooooodoooooodxxxddddooooooooooodddddddddddddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxdddoolllcccc::::::::::::::::::::::::::::::::: kkkkkkkkkkkkkxxxxxdddddddddddddddddddoddxxxxkkkkkkkkkkkkkkOOOOOOOOOOOOkkxxxddoooooooooooooollllccclllllllllooooooooddddxddddoolloooddddxxxxxxxkkkkkkkOOOO00O000000000000000KKK000000000OOOOOkkkkxxddddddxxxxddddddddollllllllllooooooooooooooooooooooooodddddddddddddddddddooooooooooooddddddddddddddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxdddooolllccccc:::::::::::::::::::::::::::::::::::::::::::: kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxdddxxxxkkkkOOOkkxxxkkkkkOOOOOOOkkkkxxddddxdddxxxxxxxxxdddolccllllllllooooooodddddddxxdddddddddddddxxxxxxxxkkkOOOO0000000000KK00KKKKKKKKKKKKKKK000000O0OOOOkkkxxddooddddddddddooddddddxxddxxxxxxxxxxddddddddoooddoooooooooodddddddddddddddddddxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxdddoooolllcccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::c: xxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdddxxxkkkOOOOOkkkxxxxxkkkkkkkkkkkkkxxddddddddddddddddddddoolccllllllloooooodddddddddddddddddddddddddxxxxxkkOO0000KKKKKKKKKKKKKKKXXKKKKKKKKKKKKK0000000OOOOkkkkxxdololloooooooloddoooooooododdddddddddddddddddddddddxdxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxdddoooollllcccccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ccccccc:: cllllloooodddddxxxxxkkkkkkkkkkkkkkkkxdddxxxkkOOOOOOOkkkxxxxxkkkkkkkkxxxxdddxkkkkkkkkkkxxxxxxxxxxdllllloooooooddddddddddddddoooooododdddddxxkkOO00KKKKKKXXXXXXXXXXXXXXXXKKKKKKKKKKKKK000000OOOOOkkkkxxdolllclclllllodddddddddxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxdddddooollllccccccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::c:::cccccccccccccccccc::cc:::: ::::::::::::ccccccccclllloooooddddxxdoddxxkOOOOO000OOOkxxxxxxkkkkkkxxxxddddkkkkkkkkkkkkkkkkkkkkkkdolloodddxxxxxxddddddddooooooooddoodddxkkOO00KKKKKKXXXXXXXXXXXXXXXXKKKKKKKXKKKKKKKKK00000OOOOOkkkxxxddoollccccclloxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxddddoooolllllcccccccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::cccccccccccccccccc::::::::::ccccccccc ::::::::::::::::::::::::::::::::::cclodxxkkOO00000000OOkxxxxxxxkkkkxxxddodxkkkkkkkkkkkkkkkkkkkkkkkxoloddxxxkkkOOkkkkkkkxxxxxxxxxxxxxxkkOO00KKKKKKKKKKKKKXXXXXXXXXXKKKKKKKKKKKKKKKKKKK00000OOOOkkkkxxddoooollccllllokkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxddddddoooooolllllccccccc:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ccc::ccccccccccccccccccccc::::::::cc:::cccccccccccccccc :::::::::::::::::::::::::::::::::::clddxkkOO000KKKKKK00OkxxxxxxkkkxxxddolllloolooooooodddddddddxxxxdoodxxkkkO000000000000000000OOkkkkOO000KKKKKKKKKKKKKKKKXXXXXXKKKKKKKKKKKKKKKKKKKKKK0000OOOOkkkxxxddoooolllllllloxxxxxxdddxdddddddddoooolllllllllllccccccccccc:::::::::::::::::c:c::::::::::::::::::::::::::::::::::::::::::::::::::::cccccccccccccccccccccccccccccccc:::::::::::cccccccccccccccccccccccccc::: :::::::::::::::::::::::::::::::::::clddxkO000KKKKKKKKKK0Okxxxxxxkkkxxddocc:::::::::::::::::cccccccccldxxkOOO00KKKKKKKKKXXXXXXXKK00OOOO000KKKKKKKXKKKXXXXKKKXXXXKKKKKKKKKKKKKKKKKKKKKKKKK000OOkkkxxxxxdddooollllclccccccccccc:::::cc:::::::::::::c::::::::::::::::cc::::::::::::::::::::::::::::::::::::::::::::::cc:::::::ccccc:ccccccccccccccccccccccccc::::::::ccc::ccccccccccccccccccccccccccc::::::::::::::: ccccccccccc::::::c::::::::::::::::ccodxkO0000KKKKKKXXXKK0Okxxxxxxkkxddol:::::::::::::::::::::::::cc:ldxkOO000KKKKKKXXXXXXXXXXXXXKK00000KKKKXXXXXXXXXXXXXXXXXXXXKKKK000KKXXXXXXXXXXXXXKKKKK00Okkkxxxxxxxxxddoollllc::::::ccc::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::cc::c::::cc:ccccccccccccccccccccccccccccccccccc:::ccccccccccccccccccccccccccccccccccccc::cc::::::::::::cccccccccccc ccccccccccccccccccccccccccccccccccccodkO00KKKKKXXXXXXXXKK0Okxxxxxkxxdolc:::::::::::::::::::::::::c::lxkOO000KKKKKKXXXXXXXXNNNNNXXXKKKKKKXXXXXXXXXXXXXXXXXXXXXKKKK00000KXXXXXNNNNXXXXXXXXKKK00OOkxxxxkkkkkkxxddoolc:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ccccccccccccccccccccccccccccccccccccccccccc::ccccccccccccccccccccccccccccccccccccc:cc:ccc:c::::::::cccccccccllllllllllllooo cccccc:ccccccc::cccccccccccccccccccloxO0KKKKKXXXXXXXXXXXKKOkkkkkkkxxdoccc:::::::c::c:::::::::::ccc:lxOOO00KKKKKKKXXXXXXXXXXNNNNXXXKKKXXXXXXXXXXXXXXXXNNNNNNNXXXXK0OO0KKXXXXNNNNNNXXXXXXXXKKK00OOkkkkOOOOkkkkxxddocc:::::::::::::c::::::::::::::::c::::::::cc::cccccccccccccccccccccccccccccccccccccccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccc:cc:::c::::::::ccccccccclllclllllllloooooooooooooodd cccccccccccccccccccccccccccccccccccldkO00KKKKXXXXXXXXXXXXK0kkkkkkkxxolccccccccccccccccccccccccccccokO0000KKKKKKKXXXXXXXXXXXXNNXXXXKKKKKKXXXXXXXXXXXXXXNNNNNNNNNXXXXXXXXXXXXXNNNNNNNXXXXXXXKKK00OOkkOOOOOOOOkkkxxoccccccccc:ccccccccccccccccccccccccccccccccccccccccccccccccccccccccc::cccccccccccccccccccccccccccccccccccccclcccccccccccccc::cc::c:::cccc::ccccccccccclllllllllllllooooooooooooooddddddddddddddd ccccccccccclccllcccllcccccccccccccloxkO000KKKKXXXXXXXXXXXKK0OOOOOkkxocccccccccccccccccccccccccccldkO0000KKKKKKKKXXXXXXXXXXXXXXXXXKKKKKKKKKKKKXXXXXXXXXXXXXNNNXXXXK0OOOO0KXXXXXXXXXXXXXXXXKKKK000OOOO000000OOOkkkdlcccccccccccccccccccccccccccccccccccccccc:cccccccccccccccccccccccccccccccccccccccccllcccccccccccccccccccccccccccccccccccccccc::cccccccclccllllllllloooooooooooooodddddddddddddddddddddxxxxxxxxx cccccccccccccccccccccccccccccclcclloxkOO00KKKKXXXXNNXXXXXXXKK0000OOxlcccccccccccccccccccccccccccok0000KKKKKKKKKKXXXXXXXXXXXXXXXXXKKK0KKKKKKKKKKKKKKKXXXXXXXXXXXXXNXOddxkOKXXXXXXXXXXKXXXKKKKK000OOO0000000OOOOOOxocccccccccccccccccccccc:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccclclllllllllllloooooooooooodddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxx llllllllllcccccccccccccccccccccccclooxkO000KKKXXXXXXXXXXXXXXXXXXK0Odlllllcccccccccccccccccccccldk0000KKKKKKKKKXXXXXXXXXXXXXXXXXXXKKKK00KKKK0000KKKKKKKKKKXXXXXNNNNNXKKXNNNNXXXXKKKKKKKKKKKKKK000OOO0000000OOOOOOkdlcccccccclcccccccccccccccccccccccccccclccccccclllcccccccccccccccccccccccccccccccccccccccccccccccllllcclllllllllloooooooooooooooooddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxk ddddddddoooooollolllllllllllllllllllloxkO00KKKKKXXXXXXXXXXXXNNXXK0OdlllllccllccllllllllllllllldO00KKKKKKKXXXXXXXXXXXXXXXXNNNNNXXXXKKKKKK000000000KKKKKKKKKKXXXXXNNNNNNNNNNXXXKKKKKKKKKKKKKKK000OOOO0000000OOOOOOOkolllllllcllllcclllccclcccccccccccccccccccccccccccccccccccccccccccccccccccccccclllllllllllllloooooooooodddooodddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkxxxxxkkkkkkkkkkkkkkkkk kkxxxxxxxxxxxxxxxxxxdddddddddddoooollldxkO000KKKXXXXXXXXXXXXXXKKKK0dllllllllllllllllllllllllldOO00KKKKKKXXXXXXXXXXXXXNXXNNNNNNNXXXXKKKKK000000000000KKKKKKKKKKXXXXXNNNXXXXXKKKKKKK0KKKKKK00000OOOOO00000000OOOOOkkdlccccccccccccccccccccccccccccccccccccccccccccclllllllllllllllllllllllloooooooooooooooddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkxxxkkkkkxkkkkxxkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkxkxkxxxxxxxxxxxxdollodxO0000KKKXXXXXXXXXXXXXXKKK0xoooooooollollllllllllllldkO000000KKKKXXXXXXXXNNNNNNNNNNNNNXXXXXXKKKKK00000000000000000KKKKKKXXXXXXXXXKKKKKKK0000000000000OOOOOO00K00000OOOOOkkxolllllllllllllllllllllllllllllllllllloooooooooooooooooooooddddddddddddddddddddddddxdddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkxxxxxkkxxkkkkkkxkkkkkkkkkxxxxkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdooodxkOO000KKKXXXXXXXXXXXXXXKKKOxxxxxxxxddddddddddddddodxkO0000000KKKKXXXXXXXXXNNNNNNNNNNNXXXXXXXKKKKK000000000000000000KKKKKKKKKKKKKKKKK0000000000000OOOOOOOOO00KK0000OOOOOOkkdoooooooooooooooooooodddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdoodxxkkkO000KKKXXXXXXXXXXXXXXKK0OkkkkkkkkxkkxxxxxxxxxxxxkkOOO00000000KKKKKKXXXXXXXXNNNNNNXXXXXXXKKKKKK0000000000000000000000000KKKKKK0000000OOOOOO0OOOOOOOOOOO00KKKK00000OOOOOkxxxxdddddxxxxdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkkkkxxkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkddddxxxkkkOO000KKXXXXXXXXXXXXXXXKKOkkkkkkkkkkkkkkkkkkkkkkxkkkOOOOOO000000000KKKKKXXXXXXXXXXXXXXKKKKKKKKKK0000000000000000000000000KK000000000OOOOOOOOOOOOOOOOOOO00KKKKK000000OOOkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkx kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdddxxkkkOOO0000KKKKKXXXXXXXXXXXXXK0kkkkkkkkkkkkkkkkkkkkkxxxkkkkkkkOOOOOOOOOO000KKKKKKKKKXXXXXKKKKKKKKKKKKKKKK00000000000000000000000000000OOOOOOOOOOOOOOOOOOOOOO0KKKKKKK000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxdd kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxddxkkkOOOO0000KKKKKKXXXXXXXXXXXXK0kkkkkkkkkkkkkkkkkkkkkxxxxxkkkkkkkOOOOOOOOOOO0000KKKKKKXXXKKKKKKKKKKKKKKKKK0000000000000OO00000000000000OOOOOOOOOOOOOOOOOOOOO000KKKKKK0000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxdddooooooooooo kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxddxkkOOOO00000KKKKKXXXKKXXXXXXXXXKOkkkkkkkkkkkkkkkkkkkkxddxxxkkkkkkkkkkkkkkkkOOOO00000KKKKKKKK000000000K00000000000000000O00000000000000OOOOOOOOOOOOOOOOOOO000000KKKK000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxdddooooooooooooooooddddxxxx kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdxxkkOOO000000KKKKKKKKXKKXXXXXXXXK0OkkkkkkkkkkkkkkkkkkkxxxxxkkkkkkkkkkkkkkkkkkkkkOOOOOO000000000000000000000000000000000000000000000000OOOOOOOOOOOOOOOOOOO000000KKKK0000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxddddoooooooooooooooooddddxxxxxxxxxxkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdddxkkOOO0000KKKKKKKXXXXKKKKXXXXXXK0OkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkxxxdxxxxxxkkkOOOOOOOOOOOO00000000000000OO0000000000000000OOOOOOOOOOOOOOOOOOOO00000000KKK0000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxddddddooooooddooooooooooddddxxxxxxxxxxxxkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdodxkkOOOOOO000KKKKXXXXXXKKKKKXXKKKK0kkkkkkkkkkkkkkkkkkkkxkkkkOOOkkkkkkkkkkkkkkxxxdddodddddxxxxkkkOOOOOO00OOOO0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO00000000000000000000000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxddddddddooooodddddddddddddddddddxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk xxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkxdodxxkkOOOOO0000KKKXXXXXKKKKKKKKKKKK0OkkkkkkkkkkkkkkkkkkxxkkOOOOOOOOOOOkkkkkkkkkkkxxdoooooddxxxxxkkkkkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO000000000000000000000000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkxxxxxxxxxxxxddddddddoooodddddddddddddddddddddxxxxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk ddddddddddddddddddddxxxxxxxxxxxxxxdoddxkOOOOOO0000KKKKXXXKKKKKKKKKKKKK0OkkkkkkkkkkkkkkkkkkkkOOOO000000000OOOkkxxxxxxxxddooodddxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkOOOOOOOOOOOOOkkOOOOOOOOOOOOO00000000OOOOOOO0000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxkxxxxxxxxxxxxxdddddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk xxxxxxxxxxxxxxxdddddddddddddddxdddooodxkOOOOOOO0000KKKKKKKKKKKKKKKKKKK0OkkkkkkkkkkkkkkkkkkkOOOO000000000000OOOkkxxxdddddddddddxxxkkxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOOOOO0000OOOOOOOO00000000000OOOkxkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxddddddddddddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxkxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkx kkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxdoodxkkOOO0OO000000KKKKKKKKKKKKKKKK00kdddddddddddddxxxxkOOO00000000000000000000OOkkxxxxxxxxxxddxkkxxxxxxxxxxxxxkkkkkxxkxxxxkkkkxxxkkkkkkkkkkkkkkkkkkkOOOOOOOOOOkkkkOO0000000000OOOkkxxxxxxxxdxxxxdddddddddddddddddddddddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxddddoooolloo kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdooodxkOOOOOO000000KKKKKKKKKKKKKKK000kxxxxdddddddddddxkkOOO0000000000000000KKKKKK000OOOOOkkkxxddxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkxxkkkkkkkkOOOOOOOkkkkkkOO000000000OOOkkxxdddddddddddddddddddddddddddddxxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxddooooollllllloooodddxxx kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxolodxkkOOOO0000000KKKKKKKKKKKKKKK000OkkkkkkkkxkxxkxxxkkOOO0000000000000KKKKKKKKKKKKKKKK000OkxxddddxxddxxddxxxxxxddddddddxddddddxxxxxdddxxxxxxxkkkkkkkkkkkkkkkxxxkkkOOOOOOOOOOkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxdddooooooolllloooooodddxxxxkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxooodxxkkOOO0000000KKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkOOO00000000000K0000KKKKKKKKKKKKKKKKK0Okxddddddddddddddddddddddddddddddddddddddddddddxxxxxkkkkkkkkkkkkkxxxkkxxxkkkkkkxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxddddooooooooooolooooooodddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdooodxxkOOO000000KKKKKXXXXKKKKKK000OkkkkkkkkkkkkkkkkkkkOOO0000000000K00000000KKKKKKKKKKKKKKK0Okddooooooooooodddddddddddddddddddddddddddddddddddxxxxxxkkkkkkkkxxxxxdoooooddxxxxxkkxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxdddddoooooooooooooooooddddxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk dxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkdoooodxkkOOO0000KKKKXXXXXXXKKKKK00OOkkkkkkkkkkkkkkxkkkkOOO000000000KKKKK0000000KKKKKKKKKKKKKK0Okdooooooodoooooddoooodddddddoooooooooooooooooddddddxxxxxxxxxxxxxxxdollllodxkkkkkxxddxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxddddddoooooooollooooooooddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk ddddddddddddddddddddddddxxxxxxkkkkkxoooodxxkkOOO000KKKKXXXXXXXKKKKK00OkkkkkkkkkkkkkkkxxkkOOOO000000000KKKKKK00000KKKKKKKKKKKKKKKK0Okddooooooooooooooooooooooooddoooooooooooooooooooddddddxxxxxxdddddollloodxxxxxddddddxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxddddddoooooooooooolloooooooooddddxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkxxxxxxxddddddddddddddddooooddxxkkOO0000KKKKXXXKKKKKKKK00OkkkkkkkkkkkkkkkxxkkOOOO0000000000KKK0000KKKKKXXKKKKK000KKKKK00kxdoooooooooooooooolooooodkOOOOOkkxoooooooooooooooodddddddddddoolllodddddddddxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxdddddddddooooooooooooooooooooooooooodddddxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdoooodxxkkkOO0000KKKKKKKKKKKKKK0OxddddddddddddddddxkkkOOOO0000000000000000KKKKXXXXXXXXKKKKKKKKK00OxooooooolllllllllloodxkkxO00000kkxdoooooooooooloooooooooooooooodxkkkkkkkkkkkOOOOkxxxdddddddddddddoooooooooooooooooooooooooooooooooddddddddxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoooodxxxkkkOOO000KKKKKKKKKKKKK00kkkkkkxxxxxxxxxxdxxkkOOOOO0000000000000000KKKKXXXXXXXXKKKKKKK0000kxoloollllllllllooodxO00kxdodddkkO0Okxolooooollllllllloooooooxk000000O00000000O0Oxdoooooooddddddddddddxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdlooddxxxkkkkOO00KKKKKKKKKKKK00OkkkkkkkkkkkkkkOkxxkkkOOOOO000000000000000KKKKKKKKKKKKKXXKXXKK0000OkxolllllllllloodxkxdxkOkdlcccldkO0OOOxdoooollllllclllooooodxO000000000000KKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoloddxxkkkkkkOO000KKKKKKKKKK00OkkkkkkkkkkkkkkkkxxkkkOOOOOOO000000000000KK00KKKKKKKKKKKKKXXXXK0OkkOkxdlccccloddxxdoodxxk0KKKkddO0KK0OkxxxddoolllllllllllloodxO0000000000KKKKKK0000Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdloodxxkkkkkkOOO00KKKKKKKKKK00OkkkkkkkkkkkkkkkkkkkkkkOOOOOO000000000000000KKKKKKKKK0KKKKKXXXXK0kxkkkkxolcclodxkkkkkkkO0OkkkkxxO000kdodkkkxoollllllllllloodxO0000KKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxooodxkkkkkOOOOOO000KKKKKKKK0OOkkkkkkkkkkkkkkkkkkOOkkkkOOO0000000000000000KKKKKKKKKK000KKKKKXXXKkddxkxxdlclllll::ododxxdolllllodddxddxdodooollllllllllloxkOO000KKKKKKKKKKKKKKK0000Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdlllodkkkOOOOOOOO000KKKKKKK0OOkkkkkkkkkkkkkkkkkOOOOkkkOOO0000000000000000KKKKKKKKKK00KKKKKKKKXXKOxddddddoloooolloolllcccccccclllloocloodxdollllllllodxkOOO0000KKKKKKKKKKKKKKK0000Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkoloodkkOOO00000000KKKKKKKK0OkkkkkkkkkkkkkkkkkOOOOOkkkOOO0000000000000000000KKKKKKKKKKKKKKKKKXXXXKOdooooolclloolc:ccccc::;;;::ccccllooolollccclllodxkOOO00000KKKKKKKKKKKKKKKKK000Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOxloodxkO000KKKK00KKKKKKKXK0OkkkkkkkkkkkkkkkkkOOOOOkkkOOOO000000000000000000KKKKKKKKKKKKKKKKKKKXKKK0kolllccccccc:::cc:;,,,,,,,:ccclcccccclcllodxxkOOOO0000KKKKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkdoodxkOO00KKKKKKKKKKKXXXK0OkkkkkkkkkkkkkkkkkkkkkkkkkkOOOO0000000000000000000KKKKKKKKKKKKKKKKKKKKKKKOdlccccccccc:::;;,;loxxo:;::ccccc::clodxkkOkkkOO000KKKXXXKKKKKKKKKKKKKKK0000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxx kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoodxxkO00KKKKKKKKKKKKXXXKOkkkkkkkkkkkkkxxkkkkkkkkkkkkOOOO0000000000000000000KKKKKKKKKKKKKKKKKKKKKK00xl:ccccccccc:::ldOKXXK0xlc:cccc:cldxkkkkkkkkOO00KXXXXXXXXXXXKKKKKKKKK00000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxdd kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdodkkkO00KKKKKKKKKKKKXXXK0kkkkkkkkkkkkkxxxkkkkkkkkkkkkOOOO0000000000000000000KKKKKKKKKKKKKKKKKKKKKKK0kl::ccccccccclokOKXXXXXOocccccloddxxxddddkO0KKXXXXNNXXXXXXXXXXXXXKKKK0000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxddddddddddooooo kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxodkkkO00KKKKKKKKKKKKKXXXKOkkkkkkkkkkkxddxxxxkxxxxxkkkkOOOOOO00000O000000000000KKKKKKKKK00KKKKKKKKKKK0ko:;:c::clldooxO0KXXXKKkllccloodooooodkOKXXXNXXXNNNNNNXXNXXXXXXXXKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxdddddddddddddoooollllllllllooll kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdoxkkOO000KKKKKXXXKKKKXXX0OkkkkkkkkkkxdddxxxxxxxxxxxkkkOOOOOOOOOOOO000OOO00000KK00KKKKKK00KKKKKKKKKKK0Oo;,;:::ldOxldkO0XXXKKOxolllooolodxOKKKXXXNNNNNNNNNNNNNNNXXXXXXXKKKK0000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxddddddddddoodoooolllllllllllloollloooooodddddd kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkdoxkOOO00OO0KKKKKKKKK0KKXX0OkkkkkkkkkdoodddxxxxxxxxxkkkOOOOOOOOOOO00OOOOO0000000000KKKK0000K000KKKKKK00Oo;',;:okKOllxO0KKXXK00kllolloxO0KKKKXXXXXXNNNNNNNNNNNNNNNXXXXXXKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkddddddddddddddddddooooolllllllllllllloooooooooddddddddddddoooooooolo xxxxxxxxxxxxxxxxxxkkkkkkkkkkkOOkkkkkkkOOkkkxddkkkOOOOOOOO0000000000KKKOkkkkkkkkkdooodddddddddxxxkkkkOOOOOOOOO0OOOOO000000000000KKK000000000KKKKK000ko,..:x0XKdlodxk0KK0OO0klloxO0KKKKKXXXXXXXNNNNNNNNNNNNNNNXXXXXXXKKKK00OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkxoooolllllllllloooolooooooooodddddddddddooooooooooooooooooooddddxx ddddddddddddddddxdxxxxxxxxxxxxxxxxxxxxxxxxxxxdddxxkkkxxxxkkkOOOOkkOO0K0OOOkkkkOkdlooooddddddddxxxkkkkOOOOOOOOOOOOOO000000000O00000000000K000000K000Oko,'d0KKKKkolloxxxkxOXKxxO000KKKKKXXXXXXXNNNNNNNNNNNNNNNXXXXXXXKKKK00OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddddddddxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkdoooloooooddddddddddooooooooooooooooooooooodddddxxxkkkkkkkkkkkkkk lllllllllllllllloooooooodddddddddddddddddddxxxdoooddddddddddxxkOOOOkO00OkxxxxxxxolloooddddddddxxxxkkkkkkkkOOOOOOOOO0000000O0000000000000000000000000OkooO00KKX0o;::ccccoOXX0000KKKKKKKXXXXXNNNNNNNXXXNNNNNNNNXXXXXXKKKKK00Okkkxxkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxddddddddddddddddddddddddddoooooolllllllldkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxdoooooooooooooooooooodddddxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkk xxdddddddddoooooooooolooolllllllllllllllllloodoollooooddddodddxxkkOOkO00OxddddddollllooodddddddddxxkkkkkkkkkOOOOOOOOOO000O000000000000000000000000000OkkkkkOO0Odc,'''';dOKK000KKKKKKXXXXXXXNNNNNNXXXXXXNNNNNNXXXXXXKKKKKK0Okxxxxdddddddddddddddddddddddddddddddddoooooooollllllllllllllllllllllllloolloooooddxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxdooodddddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk oooooooodooodddddddddxxxxxxxxxxxxddddddoooolccccccllooodddddxxxxxxkkkkO0KOxdollllllllooodddddddddxxxxkkkkkkkkkkOOOOOOOOOOOOOOO00000000000000000000000Oxxddodddddo:;,,:lxkOOOO0KKKXXXXXXXXXXXNNNNNXXXXXXXXXXNNXXXXXXXXXKKK00koooooooollllllllllllllllllllllllllllollloooolloooooooooodddddddddddddddddddddxkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk xxxxxxxxddddddddddoodddddooooooooooooooooool:::::cccllloddddxxkkkkkkOOOO0KXKK0kxollllooodddddxddxxxxxxkkkkkkkkkkOOOOOOOOOOOOOO0000000000000000000000Oxooolcccclll:;;;clooddddk0KKXXXXXXXXXXXXNNNNXXXXXXXXXXXNXXXXXXXXXXKKK0Odoooooooooooooooddoddddddddddddxxddddddddddddoooooooooooooooooddddddddddddddddxkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxdddddddddddoc::::::::cclooooodxkkOOO00O00KKKXXK0xollloooddxxxxxxxxxxxkkkxkkkkkkkOOOOOOOOOOOOOO00000000000OOOO00000OOxccllc:ccccc:::::ccccllok0KKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKK0Oxdddddddddoodoooooooooooddoodddddoooodddoddddddddddddddxxxxxxkkkkkkkkkkkkkkkxkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkoc::;;;;;:::cllcccodxkkOO00000KKKKKXK0xoloooddxxxxxxxxxxxkkkkkkkkkkkkkkOOOOOOOOOOOO0000000000OOOOOOOOOOOxc,;c:::cccc:::::::ccclokO0KKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKK0kdxdddddddddddddxxdxxxxxxxxxkkkkkkkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxc::;;:;;;:::clllcclodxkOOOO000KKKKKKKKOdoooddxxxxxkkkkkkkkkkkkkkkkkkkkkkkkkOOkOOOOO000000000OOOOOOOOOOkxl,',:::::::::::::ccccloxO0KKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKK0OOOOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkl:::;;;;;::::ccloollodxxkkOO000KKKKKKKKKkdoddxxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOO00000000OOOkkkOOkkkxo:'.....''',;:::ccc::cdxO0KKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKK0Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::;;;;;;;;:::ccllcclodxxkOO0000000K00KK0kdddxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOO000000000OOkkkkkkkkxoc;. ...',,;,;,,:oxkO0000KKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKKKKOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::;;;;;;;;;;:::ccclllodxxxkOOOOO0000000KK0xdxxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOO000000000OOOOkkkkkkxoc;' ...'''',,,:odxkOO0000KKKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKXKKKOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::;;;;;;;;;;::::::cllooddxxxkkOOO000OO00KKKOxxxkkxkkkkkkkkkkkkkkkkkkkkkkkkkkOOOOOOO000000000OOOkkkkkxdol:,. ..'',,,,;;:ldxkkOO000KKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKKK0OkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdcc::;;;;;;;;;;:::::::looodddddxkOOO0OO0OO00O0OxxxxxkkkkkkkkkkxxkkkkkkkkkkkkkkkkkOOOOOOOO000000OOkkkkxxdol:,. ..',,,;;;:lddxxkOO0000KKKKKKKKKKKKXXXKXXXXXXXXXXXXXXXXXXXXXXXXXKKKK0OkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxlc::;;;;;;;;;;;:::::::cloooooodxkOOOO0OkkkkkOOkxxxxkkkkkkkkkkxxxkkkxxxkkkkkkkkkkkOOOOOOOOO000OOOkkkxxddolc;. ..',,;:::lodxxxkOOO00000000KKKKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXXXK0Okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoc::;;;;;;;;;;;:::::::::llooolloxxkkOOdloxxkO0OkxxkkkkkkkkkkxxxxxxxxxxxxxxxkkkkkkOOOOOOOOOO00OOOkxxxxddolc:' ..'',;;::clodxxxkkOOO0000000KKKKKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXXKK0OkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdl:::;;;;;;;;;;;;::::::;:clddddooddxOOdcldxxkO0OkkkkkkkkkkkkxxxxxxxxxxxxxxxxkkkkkOOkkOOOOOOO00OOkxxxdddolc:' .'',;;:cloddxxxkkOOOO0000000K00KKK00KKKKKKKKKKKKKKKXXXXXXXXXXXXXXXXKKK0OkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkoc::;;;;;;;;;;;;;;::cccccloddoooddxkOkolodxkO00OkkkkOkkkkkkxxxxxxxxxxxxxxxxxxkkkkkkkkOOOOOO0OOOkkxddddolc:,. ..',;;:cllodddxxkkkOOOO0000000000000000KKKKKKKKKKKKKXXXXXXXXXXXXXXXXXXKK0OkkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoc::;;;;;;;;;;;;:c::ccc::::cllodxkOO0kocodxkO00OkkkkkkkkxxxxxxxxxxxxxxxxxxxkkkkkkkkkOOOOOOOOOOOkxxddoolc:,. .',;;:cllodddxxxkkkOOOO000000000000000KKKKKKKKKKKKXXXXXXXXXXXXXXXXXXXXK0OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl:::;;;;;;;;;;;;::;;;;;;;::ldkkkkOKKKOocldxkO00OkkkkkkxxxxxxxxxxxxxxxxxxxxkkkkkkkkOOOOOOOOOOOOkxxddoolc:,. .',;:ccllloddddxxkkkOOOOO00000000000000KKKKKKKKKKKKKKXXXXXXXXXXXXXXXXXKK0OkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc:::;;;;;;;;;;;::::::::::::ldkOOOO000OocdkkO000OkxxxxxxxxxxxxxxxxxxxxxxxxxxxkkkkOOOOOOOOOOOOOkxxddoolc:,. ..,;:cclllodddxxxxxkOOOOOOO000000000000KKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXKKK0OOOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkOko:::;;;;;;;;;;;:cccccc::::::coxxxxkkO0xcokkxxkkkkxxxxxxxxddxxxdxxxxxxxkkxxxxxkkkkOOOOOOOOOOOkkxxddoolc:,. ..';::cllloodddxxxxkkkOOOOOOOOOO00000000KKKKKKKKKKKKKKKKXXXXXXXXXXXXXXKKK0OOOOkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::::::;;;;;;;;:lloolc:;::;;:loxxxkkOkccoxxolodxxxdddddddxxxxxxxxxxxxxkkkkkkkkkkkkkOOOOOOOOkkxxdoooll:;. ..';::clllloodddxxxxkkkkkOOOOOOOOO0000000000KKKKKKKKKKKKKXXXXXXXXXXXXXXKKK0OOkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkdc::::::;;;;;;;;:cccclc;;;;;;;:ldxxkOOkl:loxxxxxxxxdddddddxxxxddddxxxxxxkkkkkkkkkkkkkkkOOkkkkkxxdoollc:;. ..,;::ccllloooddddxxxxkkkkkkOOOOOOO0000000000KKKKKKKKKKKKKKKXXXXXXXXXXXXKK00OkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkxxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdc::::::;;;;;;;;;:::::;;;,,,;:coxkOO0Oo;:lodxddxxxddddddddxxdddddxxxxxxxkkkkkkkkkkkkkkkkkkkkxxddoollc:;. ..';::cccllloodddddxxxxxkkkkkOOOOOO0000000000000000KKKKKKKKKKKKXXXXXXXXXKKK0OOOOkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxdolc::::;;;;;;;;;;;;;;,'..,;:oxOO00KOl;:codxkOOxdxdddddddddddddxxxxxxxkkkkkkkkkkkkkkkkkkkkxxddoollc:;'....',::cccllllooodddddxxxxxkkkkkkkOOOOO0000000000000KKKKKKKKKKKKKKKKXXXXXKKK00OOOkOOkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkOOkkkkkkkkkkkkkkkkOOkkkxkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxoc:::;;;;;;;;;:::;;'..';cldkOO00Kx:;ccoxkOOkxdddddddddddddddxxxxxxxxxkkkkkkkkkkkkkkxxxxxddoolcc::;'...',;:ccclllllooodddddxxxxxxxxxkkkOOOOO0000000000000KKKKKKKKKKKKXXKKXKKKKKKK0OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkxxkkkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkxdolc:::;;;:::ccc:,...,:codxOO000l,;:codxxdolodddddddddddddddxxxxxxxxkxxxxxkkkkkkkxxxxxddollcc:coc''',,;::cclllllooooodddddxxxxxxxxkkkkOOO00OOOOO0000000KKKKKKKKKKXXXKKXKKKKKKK00OOOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl:::;;::::cc:,....;:cldkkO00x;',,;:c:;:clooooooddddddddddxxxxxxxxxxxkkkkkkkkxxxxxxdoollcc:cxkl,,,,;:::ccccllllooooddddddxxxxxxxkkkkOOOOOOOOO00000000KKKKKKKKKXXKKXXXKKKKK000OOOkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkxkkkOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl:::;:::::c:;,'..';:clodkOOOd:;'....';clooooooddddddddddxxxxxxxxxxxkkkkkkkxxxxxxxddoolcc:lkOxl;,;;:::ccccllllllooooddddddxxxxxkkkkOOOOOOO00000000000KKKKKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxkkkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOxoc::::::::::;,..',;:clldxkkOOOkdl,',:clooooooooodddddddxxxxxxxxxxxxkkkkxxxxxxxxxddoolc::dOkkxc;;;:::cccccllllloooooodddddxxxxxkkkkOOOOOOOOO000000000KKKKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkdl:::::;:::;,''.,;;:codxkkxkOO00x:,:cllloooooooooooddddxxxxxxxxxxxkkkxxxxxxxxxdddolcc;cxOkkkxc;;::::cccccclllllloooodddddxxxxxkkkkOOOOOOOOO00000000000KKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkocc::::::;;;,,;:::clooodooodddl;,;:cllloooooooooddddddddddxxxxxxxkkxxxxxxxxxxddooc::okkkkkkxl::::ccccccccccllllooooodddddxxxkkkOOOOOOOOOO0000000000KKKKKKKKKKKKKKKKKKKK000OkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkxkkkkkOkkkkkkkOkkkkkkkkkkOOOkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkkkkkkkkOOOkkkkkOkkkkkkkkkkkkkkkkkkkOkkxxdl::::;;;,;cc:;;;::::::::;;,,;::cclllooooooooodddddddddxxxxxxxxxxxxxxxxxxxddoc:cxkkkkkkkxl:::ccccccccccclllloooooddddxxxxkkOOOkkkOOOO00000000000KKKKKKKKKKKKKKKKKKKK00OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkOkkkkkxkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkOkkkkkOkkkkkkkOOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl:::::;;;:c::;;;;;;;,'.'',;;::ccllllloollooooooooodddddxxxxxxxxxxxxxxxxxxdoc:okkkkkkkkOxl:ccccccccccccllllllloooooddxxxkkOOOkkOOOOOOOO00000000KKKKKKKKKKKKKKKKKKK000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkOOkkkkkkkkkkkkkkkkkkkOkkkkkkkkkOOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkOkkkkkOOOOkkkkOkkkkkkkkkkOOOkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkkkdl::::::::::;;,,,,,,'...',;;;:ccclllllllllooooooooodddddddxxxxxxxxxxxxkxxdoccdOkkkkkkkkkxlcccccccccccccllllllooooodddxxkkkOOOOOOOOOOOO00000000000KKKKKKKKKKKKKKK0000OOOOkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkOOkkkkkOkkkkkkkkkkkkkkOOkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkOOOOOkkkOOOOkkkkOOOOkkkOOOkkOOkkkkkOOkkkkkkkkOOkkkkOkkkkkkkkkOOOkkkkOkkkxdoc:::::::::;;;;;;,,,,;;;;::ccllllllllllllooooooodddddddddxxxddxxxkkkxxoclkOkkkkkkkkkOkoccccccccccccclllllllloodddxxkkkOOOOOOOO0OO0000000000000KKKKKKKKKKKKKK00000OOOkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkkkOkkkkkkkkkOOkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk OkkkkkOOOkkkkOOkkkkkkkOOkkkOOOOOkkkkkkkkOkkkkkkOOOOOOOOOOOOOOkkkOkkOOkkkkkkkkkOkkxdolc:::cllllc::;;;::;;:::cccccllllllllooooooooddddddddxxxxxxxxkkkkxocdkkkkkkkOkkOkkkdcccccc::ccccccllllllooooddxxkkkOOOOOOO0000000000000000KKKKKKKKKKKKKK00000OOOkkOOOOOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkOOOkkkkkkkkkkkkkkkkkkkOOOOOOOOkkkkkkOOOOOkkkkkkOOOOkkkkkOOOOkkkkOOkkkkkkkkkkkkkkkxxxxkkkkxoc:::::::;:::ccccccllllllllooooooooooddddddddxxxxkkkkkkdlxOkkkkkkkkkOkkkkxlcccccc:ccccccclllllooooddxxkkkkOOOOO00000000000000000KKKKKKKKKKKK0000000OOkOOkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkOOOOOOkkkOOOOOOOkkkkOOOOkkkkkkkOkkkkkOOOkkkkkOOOkkkkkkkOkkOkkkkdlc::c:::;:::::cccclllllllllllllloooooodddddddxxxkkkkkkdoxOkkkkkkkkkkkkkOkxocccccccccccccclllloooooddxxkkkkkOOO0000000000000000KKK00KKKKKKKKK000000OOOOOkkkOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkOOkkkkkkkkOOOOOOkkkOOOkkkkkkkkkkOkkkOkkkkkkkkkkkkkkkOkkkOkkkkkxlccc:::::::::cccccclllllllllllllllloooooodddddxxkkkkkkxdkkkkkkkkkkkkkOOOkkkdlccccccccccccccllllloooddxxkkkkkOOOO00000000000KKKKK0KKKKKKKKKKK0000000OOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkxkkkkkkkOOkkkkkkkkkkkOOOkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkdccc:::::::::cccccccclllclllllllllllooooodddddxxxkkkkkxxkkkkkkkkkkkkkkkOkkkkxoccccccccccccccllllloooddxxkkkkOOOOOO00000000K0000K0KKKKKKKK0000000000OOkkkkkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxocc::::::::::ccccccccccccllllllllllllooooooddxxxkkkkkkkkkkkkkkkkkkkkkkOkkkkkxolclcccccccccccllllloooddxxxkkkkkOOO0000000000KKKKKKKKKKK000000000000OOOkkkkkkkkkkkkkkkkkkkkkkOOOOOkkkkkkkkkkOOkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkOOOkkkOOkkkkkkkkkkkkkkkkOkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkdlcc::::::::::cccccccccccccccclllllllllooooddxxxkkkkkxxkkkkkkkkkkkkkkkOkkkkkxkxocccccccccccccllllllooddxxxkkkkkOO000000000KKKKKKKKKK000000000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kOOkkkOkkkkkkkkkkkkkkkkkkkkkkkkkOkkOOOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkoccc:::::::::cc::ccccccccccccccccllllllloooddxxxxxkkxxkkkOkkkkkkkkOkkOkkkkkxkkxdllccccccc:ccclllllloodddxxkkkkOOO00000000KKKKKKKKK0000000000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkOkkkkkkkkkkkkkkkkkkkkkkkkdcccc::::::::ccc:cccccccccccccccccclllllloodddxxxxkkxxxkkkOOkkkOkOOOOOkkkkkxkkkkxolccccccccccccclllloodddxxkkkOOOO000000000KKKKK000000000000000000OOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkxkkkkOkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkkkkkkkkOOOOOOOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkOkkOkkkkkkkkkkkkkkkkkkkkOkocccc::::::::::::::::::::ccccccccccclllloooddxxxxkkxxxkkkkkkkkkkkkOOOkxkkkxkkxkOkxolcccccccccccclllooodddxxkkkOOOO00000000KKKKKKKK00000000000000OOOOkkkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkkkOOkkkkkkkOOOOOOOkkkkkkkkkkkkkkkkkOOkkkkkkkkkkkOOOkkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkxlcccc::::::::::::::::::::::::c::ccccllloooddddxxxxxxxkkkkkkkkkkkkkkOkkkkkxkkkkkOOkdlcccccccccccclllooooddxxkkOOOO00000000KKKKKKKK00000000000000OOOkkOkkkkkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkkkkkkkkkkkkOOOkkkkkkkOOOOOOOOOOkkkkkkkkkkkkkkkkkOOOOOkkkkOOOOOkkkOkkkOkkkkkkkkkkkkkkkkkkkkkkkkkOkoccccc::::::::::::::::::::::::::ccccllllooddddxxxxxdxxkkkkkkkkkkkkOOkkkkkxkkkkkOOOOxllcccccccccccllloooddxxkkOOOO0000000000000KKK00000000OOOOOOOOkkkOkkkkkkkOOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkkxkkkkkkkkkkOkkkkkOkkkkkkkkkkkkkkkkkkOkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk */ ================================================ FILE: errors.generated.go ================================================ package core import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: features/dns/client.go ================================================ package dns import ( "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features" ) // IPOption is an object for IP query options. type IPOption struct { IPv4Enable bool IPv6Enable bool FakeEnable bool } func (opt IPOption) With(other IPOption) IPOption { return IPOption{ IPv4Enable: opt.IPv4Enable && other.IPv4Enable, IPv6Enable: opt.IPv6Enable && other.IPv6Enable, FakeEnable: opt.FakeEnable && other.FakeEnable, } } func (opt IPOption) IsValid() bool { return opt.IPv4Enable || opt.IPv6Enable } // Client is a V2Ray feature for querying DNS information. // // v2ray:api:stable type Client interface { features.Feature // LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses. LookupIP(domain string) ([]net.IP, error) } // IPv4Lookup is an optional feature for querying IPv4 addresses only. // // v2ray:api:beta type IPv4Lookup interface { LookupIPv4(domain string) ([]net.IP, error) } // IPv6Lookup is an optional feature for querying IPv6 addresses only. // // v2ray:api:beta type IPv6Lookup interface { LookupIPv6(domain string) ([]net.IP, error) } // LookupIPWithOption is a helper function for querying DNS information from a dns.Client with dns.IPOption. // // v2ray:api:beta func LookupIPWithOption(client Client, domain string, option IPOption) ([]net.IP, error) { if option.FakeEnable { if clientWithFakeDNS, ok := client.(ClientWithFakeDNS); ok { client = clientWithFakeDNS.AsFakeDNSClient() } } if option.IPv4Enable && !option.IPv6Enable { if ipv4Lookup, ok := client.(IPv4Lookup); ok { return ipv4Lookup.LookupIPv4(domain) } else { return nil, errors.New("dns.Client doesn't implement IPv4Lookup") } } if option.IPv6Enable && !option.IPv4Enable { if ipv6Lookup, ok := client.(IPv6Lookup); ok { return ipv6Lookup.LookupIPv6(domain) } else { return nil, errors.New("dns.Client doesn't implement IPv6Lookup") } } return client.LookupIP(domain) } // ClientType returns the type of Client interface. Can be used for implementing common.HasType. // // v2ray:api:beta func ClientType() interface{} { return (*Client)(nil) } // ErrEmptyResponse indicates that DNS query succeeded but no answer was returned. var ErrEmptyResponse = errors.New("empty response") type RCodeError uint16 func (e RCodeError) Error() string { return serial.Concat("rcode: ", uint16(e)) } func RCodeFromError(err error) uint16 { if err == nil { return 0 } cause := errors.Cause(err) if r, ok := cause.(RCodeError); ok { return uint16(r) } return 0 } ================================================ FILE: features/dns/fakedns.go ================================================ package dns import ( "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features" ) // FakeDNSEngine is a V2Ray feature for converting between domain and fake IPs. // // v2ray:api:beta type FakeDNSEngine interface { features.Feature // GetFakeIPForDomain returns fake IP addresses for the given domain, and registers the domain with the returned IPs. GetFakeIPForDomain(domain string) []net.Address // GetDomainFromFakeDNS returns the bound domain name for the given fake IP. GetDomainFromFakeDNS(ip net.Address) string } // FakeDNSEngineRev0 adds additional APIs for FakeDNSEngine. // // v2ray:api:beta type FakeDNSEngineRev0 interface { FakeDNSEngine // IsIPInIPPool tests whether the given IP address resides in managed fake IP pools. IsIPInIPPool(ip net.Address) bool // GetFakeIPForDomain3 registers and returns fake IP addresses for the given domain in IPv4 and/or IPv6. GetFakeIPForDomain3(domain string, IPv4 bool, IPv6 bool) []net.Address } // ClientWithFakeDNS is an optional feature for utilizing FakeDNS feature of DNS client. // // v2ray:api:beta type ClientWithFakeDNS interface { // AsFakeDNSClient converts the client to dns.Client that enables FakeDNS querying option. AsFakeDNSClient() Client // AsFakeDNSEngine converts the client to dns.FakeDNSEngine that could serve FakeDNSEngine feature. AsFakeDNSEngine() FakeDNSEngine } // FakeDNSEngineType returns the type of FakeDNSEngine interface. Can be used for implementing common.HasType. // // v2ray:api:beta func FakeDNSEngineType() interface{} { return (*FakeDNSEngine)(nil) } ================================================ FILE: features/dns/localdns/client.go ================================================ package localdns import ( "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/dns" ) // Client is an implementation of dns.Client, which queries localhost for DNS. type Client struct{} // Type implements common.HasType. func (*Client) Type() interface{} { return dns.ClientType() } // Start implements common.Runnable. func (*Client) Start() error { return nil } // Close implements common.Closable. func (*Client) Close() error { return nil } // LookupIP implements Client. func (*Client) LookupIP(host string) ([]net.IP, error) { ips, err := net.LookupIP(host) if err != nil { return nil, err } parsedIPs := make([]net.IP, 0, len(ips)) for _, ip := range ips { parsed := net.IPAddress(ip) if parsed != nil { parsedIPs = append(parsedIPs, parsed.IP()) } } if len(parsedIPs) == 0 { return nil, dns.ErrEmptyResponse } return parsedIPs, nil } // LookupIPv4 implements IPv4Lookup. func (c *Client) LookupIPv4(host string) ([]net.IP, error) { ips, err := c.LookupIP(host) if err != nil { return nil, err } ipv4 := make([]net.IP, 0, len(ips)) for _, ip := range ips { if len(ip) == net.IPv4len { ipv4 = append(ipv4, ip) } } if len(ipv4) == 0 { return nil, dns.ErrEmptyResponse } return ipv4, nil } // LookupIPv6 implements IPv6Lookup. func (c *Client) LookupIPv6(host string) ([]net.IP, error) { ips, err := c.LookupIP(host) if err != nil { return nil, err } ipv6 := make([]net.IP, 0, len(ips)) for _, ip := range ips { if len(ip) == net.IPv6len { ipv6 = append(ipv6, ip) } } if len(ipv6) == 0 { return nil, dns.ErrEmptyResponse } return ipv6, nil } // New create a new dns.Client that queries localhost for DNS. func New() *Client { return &Client{} } ================================================ FILE: features/dns/localdns/errors.generated.go ================================================ package localdns import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: features/errors.generated.go ================================================ package features import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: features/extension/browser.go ================================================ package extension import ( "io" "net/http" ) type BrowserForwarder interface { DialWebsocket(url string, header http.Header) (io.ReadWriteCloser, error) } func BrowserForwarderType() interface{} { return (*BrowserForwarder)(nil) } ================================================ FILE: features/extension/contextreceiver.go ================================================ package extension import "context" type ContextReceiver interface { InjectContext(ctx context.Context) } ================================================ FILE: features/extension/instance.go ================================================ package extension import ( "context" "github.com/v2fly/v2ray-core/v5/features" ) // InstanceManagement : unstable type InstanceManagement interface { features.Feature ListInstance(ctx context.Context) ([]string, error) AddInstance(ctx context.Context, name string, config []byte, configType string) error StartInstance(ctx context.Context, name string) error StopInstance(ctx context.Context, name string) error UntrackInstance(ctx context.Context, name string) error } func InstanceManagementType() interface{} { return (*InstanceManagement)(nil) } ================================================ FILE: features/extension/observatory.go ================================================ package extension import ( "context" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/features" ) type Observatory interface { features.Feature GetObservation(ctx context.Context) (proto.Message, error) } func ObservatoryType() interface{} { return (*Observatory)(nil) } ================================================ FILE: features/extension/storage/storage.go ================================================ package storage import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features" ) type ScopedPersistentStorage interface { ScopedPersistentStorageEngine() Put(ctx context.Context, key []byte, value []byte) error Get(ctx context.Context, key []byte) ([]byte, error) List(ctx context.Context, keyPrefix []byte) ([][]byte, error) Clear(ctx context.Context) NarrowScope(ctx context.Context, key []byte) (ScopedPersistentStorage, error) DropScope(ctx context.Context, key []byte) error } type ScopedTransientStorage interface { ScopedTransientStorage() Put(ctx context.Context, key string, value interface{}) error Get(ctx context.Context, key string) (interface{}, error) List(ctx context.Context, keyPrefix string) ([]string, error) Clear(ctx context.Context) NarrowScope(ctx context.Context, key string) (ScopedTransientStorage, error) DropScope(ctx context.Context, key string) error } type ScopedPersistentStorageService interface { ScopedPersistentStorage features.Feature } var ScopedPersistentStorageServiceType = (*ScopedPersistentStorageService)(nil) type TransientStorageLifecycleReceiver interface { IsTransientStorageLifecycleReceiver() common.Closable } ================================================ FILE: features/extension/storage.go ================================================ package extension import ( "context" "github.com/v2fly/v2ray-core/v5/features" ) type PersistentStorageEngine interface { features.Feature PersistentStorageEngine() Put(ctx context.Context, key []byte, value []byte) error Get(ctx context.Context, key []byte) ([]byte, error) List(ctx context.Context, keyPrefix []byte) ([][]byte, error) } ================================================ FILE: features/feature.go ================================================ package features import "github.com/v2fly/v2ray-core/v5/common" //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen // Feature is the interface for V2Ray features. All features must implement this interface. // All existing features have an implementation in app directory. These features can be replaced by third-party ones. type Feature interface { common.HasType common.Runnable } // PrintDeprecatedFeatureWarning prints a warning for deprecated feature. func PrintDeprecatedFeatureWarning(feature string) { newError("You are using a deprecated feature: " + feature + ". Please update your config file with latest configuration format, or update your client software.").WriteToLog() } // TaggedFeatures unstable type TaggedFeatures interface { GetFeaturesByTag(tag string) (Feature, error) common.Runnable } ================================================ FILE: features/inbound/inbound.go ================================================ package inbound import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features" ) // Handler is the interface for handlers that process inbound connections. // // v2ray:api:stable type Handler interface { common.Runnable // The tag of this handler. Tag() string // Deprecated: Do not use in new code. GetRandomInboundProxy() (interface{}, net.Port, int) } // Manager is a feature that manages InboundHandlers. // // v2ray:api:stable type Manager interface { features.Feature // GetHandlers returns an InboundHandler for the given tag. GetHandler(ctx context.Context, tag string) (Handler, error) // AddHandler adds the given handler into this Manager. AddHandler(ctx context.Context, handler Handler) error // RemoveHandler removes a handler from Manager. RemoveHandler(ctx context.Context, tag string) error } // ManagerType returns the type of Manager interface. Can be used for implementing common.HasType. // // v2ray:api:stable func ManagerType() interface{} { return (*Manager)(nil) } ================================================ FILE: features/outbound/outbound.go ================================================ package outbound import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/transport" ) // Handler is the interface for handlers that process outbound connections. // // v2ray:api:stable type Handler interface { common.Runnable Tag() string Dispatch(ctx context.Context, link *transport.Link) } type HandlerSelector interface { Select([]string) []string } // Manager is a feature that manages outbound.Handlers. // // v2ray:api:stable type Manager interface { features.Feature // GetHandler returns an outbound.Handler for the given tag. GetHandler(tag string) Handler // GetDefaultHandler returns the default outbound.Handler. It is usually the first outbound.Handler specified in the configuration. GetDefaultHandler() Handler // AddHandler adds a handler into this outbound.Manager. AddHandler(ctx context.Context, handler Handler) error // RemoveHandler removes a handler from outbound.Manager. RemoveHandler(ctx context.Context, tag string) error } // ManagerType returns the type of Manager interface. Can be used to implement common.HasType. // // v2ray:api:stable func ManagerType() interface{} { return (*Manager)(nil) } ================================================ FILE: features/policy/default.go ================================================ package policy import ( "time" ) // DefaultManager is the implementation of the Manager. type DefaultManager struct{} // Type implements common.HasType. func (DefaultManager) Type() interface{} { return ManagerType() } // ForLevel implements Manager. func (DefaultManager) ForLevel(level uint32) Session { p := SessionDefault() if level == 1 { p.Timeouts.ConnectionIdle = time.Second * 600 } return p } // ForSystem implements Manager. func (DefaultManager) ForSystem() System { return System{} } // Start implements common.Runnable. func (DefaultManager) Start() error { return nil } // Close implements common.Closable. func (DefaultManager) Close() error { return nil } ================================================ FILE: features/policy/policy.go ================================================ package policy import ( "context" "runtime" "time" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/features" ) // Timeout contains limits for connection timeout. type Timeout struct { // Timeout for handshake phase in a connection. Handshake time.Duration // Timeout for connection being idle, i.e., there is no egress or ingress traffic in this connection. ConnectionIdle time.Duration // Timeout for an uplink only connection, i.e., the downlink of the connection has been closed. UplinkOnly time.Duration // Timeout for an downlink only connection, i.e., the uplink of the connection has been closed. DownlinkOnly time.Duration } // Stats contains settings for stats counters. type Stats struct { // Whether or not to enable stat counter for user uplink traffic. UserUplink bool // Whether or not to enable stat counter for user downlink traffic. UserDownlink bool } // Buffer contains settings for internal buffer. type Buffer struct { // Size of buffer per connection, in bytes. -1 for unlimited buffer. PerConnection int32 } // SystemStats contains stat policy settings on system level. type SystemStats struct { // Whether or not to enable stat counter for uplink traffic in inbound handlers. InboundUplink bool // Whether or not to enable stat counter for downlink traffic in inbound handlers. InboundDownlink bool // Whether or not to enable stat counter for uplink traffic in outbound handlers. OutboundUplink bool // Whether or not to enable stat counter for downlink traffic in outbound handlers. OutboundDownlink bool } // System contains policy settings at system level. type System struct { Stats SystemStats OverrideAccessLogDest bool Buffer Buffer } // Session is session based settings for controlling V2Ray requests. It contains various settings (or limits) that may differ for different users in the context. type Session struct { Timeouts Timeout // Timeout settings Stats Stats Buffer Buffer } // Manager is a feature that provides Policy for the given user by its id or level. // // v2ray:api:stable type Manager interface { features.Feature // ForLevel returns the Session policy for the given user level. ForLevel(level uint32) Session // ForSystem returns the System policy for V2Ray system. ForSystem() System } // ManagerType returns the type of Manager interface. Can be used to implement common.HasType. // // v2ray:api:stable func ManagerType() interface{} { return (*Manager)(nil) } var defaultBufferSize int32 func init() { const key = "v2ray.ray.buffer.size" const defaultValue = -17 size := platform.EnvFlag{ Name: key, AltName: platform.NormalizeEnvName(key), }.GetValueAsInt(defaultValue) switch size { case 0: defaultBufferSize = -1 // For pipe to use unlimited size case defaultValue: // Env flag not defined. Use default values per CPU-arch. switch runtime.GOARCH { case "arm", "mips", "mipsle": defaultBufferSize = 0 case "arm64", "mips64", "mips64le": defaultBufferSize = 4 * 1024 // 4k cache for low-end devices default: defaultBufferSize = 512 * 1024 } default: defaultBufferSize = int32(size) * 1024 * 1024 } } func defaultBufferPolicy() Buffer { return Buffer{ PerConnection: defaultBufferSize, } } // SessionDefault returns the Policy when user is not specified. func SessionDefault() Session { return Session{ Timeouts: Timeout{ // Align Handshake timeout with nginx client_header_timeout // So that this value will not indicate server identity Handshake: time.Second * 60, ConnectionIdle: time.Second * 300, UplinkOnly: time.Second * 1, DownlinkOnly: time.Second * 1, }, Stats: Stats{ UserUplink: false, UserDownlink: false, }, Buffer: defaultBufferPolicy(), } } type policyKey int32 const ( bufferPolicyKey policyKey = 0 ) func ContextWithBufferPolicy(ctx context.Context, p Buffer) context.Context { return context.WithValue(ctx, bufferPolicyKey, p) } func BufferPolicyFromContext(ctx context.Context) Buffer { pPolicy := ctx.Value(bufferPolicyKey) if pPolicy == nil { return defaultBufferPolicy() } return pPolicy.(Buffer) } ================================================ FILE: features/routing/balancer.go ================================================ package routing type BalancerOverrider interface { SetOverrideTarget(tag, target string) error GetOverrideTarget(tag string) (string, error) } type BalancerPrincipleTarget interface { GetPrincipleTarget(tag string) ([]string, error) } ================================================ FILE: features/routing/context.go ================================================ package routing import ( "github.com/v2fly/v2ray-core/v5/common/net" ) // Context is a feature to store connection information for routing. // // v2ray:api:stable type Context interface { // GetInboundTag returns the tag of the inbound the connection was from. GetInboundTag() string // GetSourcesIPs returns the source IPs bound to the connection. GetSourceIPs() []net.IP // GetSourcePort returns the source port of the connection. GetSourcePort() net.Port // GetTargetIPs returns the target IP of the connection or resolved IPs of target domain. GetTargetIPs() []net.IP // GetTargetPort returns the target port of the connection. GetTargetPort() net.Port // GetTargetDomain returns the target domain of the connection, if exists. GetTargetDomain() string // GetNetwork returns the network type of the connection. GetNetwork() net.Network // GetProtocol returns the protocol from the connection content, if sniffed out. GetProtocol() string // GetUser returns the user email from the connection content, if exists. GetUser() string // GetAttributes returns extra attributes from the connection content. GetAttributes() map[string]string // GetSkipDNSResolve returns a flag switch for weather skip dns resolve during route pick. GetSkipDNSResolve() bool } ================================================ FILE: features/routing/dispatcher.go ================================================ package routing import ( "context" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/transport" ) // Dispatcher is a feature that dispatches inbound requests to outbound handlers based on rules. // Dispatcher is required to be registered in a V2Ray instance to make V2Ray function properly. // // v2ray:api:stable type Dispatcher interface { features.Feature // Dispatch returns a Ray for transporting data for the given request. Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) } // DispatcherType returns the type of Dispatcher interface. Can be used to implement common.HasType. // // v2ray:api:stable func DispatcherType() interface{} { return (*Dispatcher)(nil) } ================================================ FILE: features/routing/dns/context.go ================================================ package dns //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/routing" ) // ResolvableContext is an implementation of routing.Context, with domain resolving capability. type ResolvableContext struct { routing.Context dnsClient dns.Client resolvedIPs []net.IP } // GetTargetIPs overrides original routing.Context's implementation. func (ctx *ResolvableContext) GetTargetIPs() []net.IP { if ips := ctx.Context.GetTargetIPs(); len(ips) != 0 { return ips } if len(ctx.resolvedIPs) > 0 { return ctx.resolvedIPs } if domain := ctx.GetTargetDomain(); len(domain) != 0 { ips, err := ctx.dnsClient.LookupIP(domain) if err == nil { ctx.resolvedIPs = ips return ips } newError("resolve ip for ", domain).Base(err).WriteToLog() } return nil } // ContextWithDNSClient creates a new routing context with domain resolving capability. // Resolved domain IPs can be retrieved by GetTargetIPs(). func ContextWithDNSClient(ctx routing.Context, client dns.Client) routing.Context { return &ResolvableContext{Context: ctx, dnsClient: client} } ================================================ FILE: features/routing/dns/errors.generated.go ================================================ package dns import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: features/routing/router.go ================================================ package routing import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features" ) // Router is a feature to choose an outbound tag for the given request. // // v2ray:api:stable type Router interface { features.Feature // PickRoute returns a route decision based on the given routing context. PickRoute(ctx Context) (Route, error) } // Route is the routing result of Router feature. // // v2ray:api:stable type Route interface { // A Route is also a routing context. Context // GetOutboundGroupTags returns the detoured outbound group tags in sequence before a final outbound is chosen. GetOutboundGroupTags() []string // GetOutboundTag returns the tag of the outbound the connection was dispatched to. GetOutboundTag() string } // RouterType return the type of Router interface. Can be used to implement common.HasType. // // v2ray:api:stable func RouterType() interface{} { return (*Router)(nil) } // DefaultRouter is an implementation of Router, which always returns ErrNoClue for routing decisions. type DefaultRouter struct{} // Type implements common.HasType. func (DefaultRouter) Type() interface{} { return RouterType() } // PickRoute implements Router. func (DefaultRouter) PickRoute(ctx Context) (Route, error) { return nil, common.ErrNoClue } // Start implements common.Runnable. func (DefaultRouter) Start() error { return nil } // Close implements common.Closable. func (DefaultRouter) Close() error { return nil } ================================================ FILE: features/routing/session/context.go ================================================ package session import ( "context" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/routing" ) // Context is an implementation of routing.Context, which is a wrapper of context.context with session info. type Context struct { Inbound *session.Inbound Outbound *session.Outbound Content *session.Content } // GetInboundTag implements routing.Context. func (ctx *Context) GetInboundTag() string { if ctx.Inbound == nil { return "" } return ctx.Inbound.Tag } // GetSourceIPs implements routing.Context. func (ctx *Context) GetSourceIPs() []net.IP { if ctx.Inbound == nil || !ctx.Inbound.Source.IsValid() { return nil } dest := ctx.Inbound.Source if dest.Address.Family().IsDomain() { return nil } return []net.IP{dest.Address.IP()} } // GetSourcePort implements routing.Context. func (ctx *Context) GetSourcePort() net.Port { if ctx.Inbound == nil || !ctx.Inbound.Source.IsValid() { return 0 } return ctx.Inbound.Source.Port } // GetTargetIPs implements routing.Context. func (ctx *Context) GetTargetIPs() []net.IP { if ctx.Outbound == nil || !ctx.Outbound.Target.IsValid() { return nil } if ctx.Outbound.Target.Address.Family().IsIP() { return []net.IP{ctx.Outbound.Target.Address.IP()} } return nil } // GetTargetPort implements routing.Context. func (ctx *Context) GetTargetPort() net.Port { if ctx.Outbound == nil || !ctx.Outbound.Target.IsValid() { return 0 } return ctx.Outbound.Target.Port } // GetTargetDomain implements routing.Context. func (ctx *Context) GetTargetDomain() string { if ctx.Outbound == nil || !ctx.Outbound.Target.IsValid() { return "" } dest := ctx.Outbound.Target if !dest.Address.Family().IsDomain() { return "" } return dest.Address.Domain() } // GetNetwork implements routing.Context. func (ctx *Context) GetNetwork() net.Network { if ctx.Outbound == nil { return net.Network_Unknown } return ctx.Outbound.Target.Network } // GetProtocol implements routing.Context. func (ctx *Context) GetProtocol() string { if ctx.Content == nil { return "" } return ctx.Content.Protocol } // GetUser implements routing.Context. func (ctx *Context) GetUser() string { if ctx.Inbound == nil || ctx.Inbound.User == nil { return "" } return ctx.Inbound.User.Email } // GetAttributes implements routing.Context. func (ctx *Context) GetAttributes() map[string]string { if ctx.Content == nil { return nil } return ctx.Content.Attributes } // GetSkipDNSResolve implements routing.Context. func (ctx *Context) GetSkipDNSResolve() bool { if ctx.Content == nil { return false } return ctx.Content.SkipDNSResolve } // AsRoutingContext creates a context from context.context with session info. func AsRoutingContext(ctx context.Context) routing.Context { return &Context{ Inbound: session.InboundFromContext(ctx), Outbound: session.OutboundFromContext(ctx), Content: session.ContentFromContext(ctx), } } ================================================ FILE: features/stats/errors.generated.go ================================================ package stats import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: features/stats/stats.go ================================================ package stats //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features" ) // Counter is the interface for stats counters. // // v2ray:api:stable type Counter interface { // Value is the current value of the counter. Value() int64 // Set sets a new value to the counter, and returns the previous one. Set(int64) int64 // Add adds a value to the current counter value, and returns the previous value. Add(int64) int64 } // Channel is the interface for stats channel. // // v2ray:api:stable type Channel interface { // Channel is a runnable unit. common.Runnable // Publish broadcasts a message through the channel with a controlling context. Publish(context.Context, interface{}) // SubscriberCount returns the number of the subscribers. Subscribers() []chan interface{} // Subscribe registers for listening to channel stream and returns a new listener channel. Subscribe() (chan interface{}, error) // Unsubscribe unregisters a listener channel from current Channel object. Unsubscribe(chan interface{}) error } // SubscribeRunnableChannel subscribes the channel and starts it if there is first subscriber coming. func SubscribeRunnableChannel(c Channel) (chan interface{}, error) { if len(c.Subscribers()) == 0 { if err := c.Start(); err != nil { return nil, err } } return c.Subscribe() } // UnsubscribeClosableChannel unsubscribes the channel and close it if there is no more subscriber. func UnsubscribeClosableChannel(c Channel, sub chan interface{}) error { if err := c.Unsubscribe(sub); err != nil { return err } if len(c.Subscribers()) == 0 { return c.Close() } return nil } // Manager is the interface for stats manager. // // v2ray:api:stable type Manager interface { features.Feature // RegisterCounter registers a new counter to the manager. The identifier string must not be empty, and unique among other counters. RegisterCounter(string) (Counter, error) // UnregisterCounter unregisters a counter from the manager by its identifier. UnregisterCounter(string) error // GetCounter returns a counter by its identifier. GetCounter(string) Counter // RegisterChannel registers a new channel to the manager. The identifier string must not be empty, and unique among other channels. RegisterChannel(string) (Channel, error) // UnregisterCounter unregisters a channel from the manager by its identifier. UnregisterChannel(string) error // GetChannel returns a channel by its identifier. GetChannel(string) Channel } // GetOrRegisterCounter tries to get the StatCounter first. If not exist, it then tries to create a new counter. func GetOrRegisterCounter(m Manager, name string) (Counter, error) { counter := m.GetCounter(name) if counter != nil { return counter, nil } return m.RegisterCounter(name) } // GetOrRegisterChannel tries to get the StatChannel first. If not exist, it then tries to create a new channel. func GetOrRegisterChannel(m Manager, name string) (Channel, error) { channel := m.GetChannel(name) if channel != nil { return channel, nil } return m.RegisterChannel(name) } // ManagerType returns the type of Manager interface. Can be used to implement common.HasType. // // v2ray:api:stable func ManagerType() interface{} { return (*Manager)(nil) } // NoopManager is an implementation of Manager, which doesn't has actual functionalities. type NoopManager struct{} // Type implements common.HasType. func (NoopManager) Type() interface{} { return ManagerType() } // RegisterCounter implements Manager. func (NoopManager) RegisterCounter(string) (Counter, error) { return nil, newError("not implemented") } // UnregisterCounter implements Manager. func (NoopManager) UnregisterCounter(string) error { return nil } // GetCounter implements Manager. func (NoopManager) GetCounter(string) Counter { return nil } // RegisterChannel implements Manager. func (NoopManager) RegisterChannel(string) (Channel, error) { return nil, newError("not implemented") } // UnregisterChannel implements Manager. func (NoopManager) UnregisterChannel(string) error { return nil } // GetChannel implements Manager. func (NoopManager) GetChannel(string) Channel { return nil } // Start implements common.Runnable. func (NoopManager) Start() error { return nil } // Close implements common.Closable. func (NoopManager) Close() error { return nil } ================================================ FILE: format.go ================================================ package core //go:generate go install -v github.com/daixiang0/gci@latest //go:generate go run ./infra/vformat/ ================================================ FILE: functions.go ================================================ package core import ( "bytes" "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) // CreateObject creates a new object based on the given V2Ray instance and config. The V2Ray instance may be nil. func CreateObject(v *Instance, config interface{}) (interface{}, error) { return CreateObjectWithEnvironment(v, config, nil) } func CreateObjectWithEnvironment(v *Instance, config, environment interface{}) (interface{}, error) { var ctx context.Context if v != nil { ctx = toContext(v.ctx, v) } ctx = envctx.ContextWithEnvironment(ctx, environment) return common.CreateObject(ctx, config) } // StartInstance starts a new V2Ray instance with given serialized config. // By default V2Ray only support config in protobuf format, i.e., configFormat = "protobuf". Caller need to load other packages to add JSON support. // // v2ray:api:stable func StartInstance(configFormat string, configBytes []byte) (*Instance, error) { config, err := LoadConfig(configFormat, bytes.NewReader(configBytes)) if err != nil { return nil, err } instance, err := New(config) if err != nil { return nil, err } if err := instance.Start(); err != nil { return nil, err } return instance, nil } // Dial provides an easy way for upstream caller to create net.Conn through V2Ray. // It dispatches the request to the given destination by the given V2Ray instance. // Since it is under a proxy context, the LocalAddr() and RemoteAddr() in returned net.Conn // will not show real addresses being used for communication. // // v2ray:api:stable func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, error) { ctx = toContext(ctx, v) dispatcher := v.GetFeature(routing.DispatcherType()) if dispatcher == nil { return nil, newError("routing.Dispatcher is not registered in V2Ray core") } r, err := dispatcher.(routing.Dispatcher).Dispatch(ctx, dest) if err != nil { return nil, err } var readerOpt net.ConnectionOption if dest.Network == net.Network_TCP { readerOpt = net.ConnectionOutputMulti(r.Reader) } else { readerOpt = net.ConnectionOutputMultiUDP(r.Reader) } return net.NewConnection(net.ConnectionInputMulti(r.Writer), readerOpt), nil } // DialUDP provides a way to exchange UDP packets through V2Ray instance to remote servers. // Since it is under a proxy context, the LocalAddr() in returned PacketConn will not show the real address. // // TODO: SetDeadline() / SetReadDeadline() / SetWriteDeadline() are not implemented. // // v2ray:api:beta func DialUDP(ctx context.Context, v *Instance) (net.PacketConn, error) { ctx = toContext(ctx, v) dispatcher := v.GetFeature(routing.DispatcherType()) if dispatcher == nil { return nil, newError("routing.Dispatcher is not registered in V2Ray core") } return udp.DialDispatcher(ctx, dispatcher.(routing.Dispatcher)) } ================================================ FILE: functions_test.go ================================================ package core_test import ( "context" "crypto/rand" "io" "testing" "time" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" ) func xor(b []byte) []byte { r := make([]byte, len(b)) for i, v := range b { r[i] = v ^ 'c' } return r } func xor2(b []byte) []byte { r := make([]byte, len(b)) for i, v := range b { r[i] = v ^ 'd' } return r } func TestV2RayDial(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } cfgBytes, err := proto.Marshal(config) common.Must(err) server, err := core.StartInstance(core.FormatProtobuf, cfgBytes) common.Must(err) defer server.Close() conn, err := core.Dial(context.Background(), server, dest) common.Must(err) defer conn.Close() const size = 10240 * 1024 payload := make([]byte, size) common.Must2(rand.Read(payload)) if _, err := conn.Write(payload); err != nil { t.Fatal(err) } receive := make([]byte, size) if _, err := io.ReadFull(conn, receive); err != nil { t.Fatal("failed to read all response: ", err) } if r := cmp.Diff(xor(receive), payload); r != "" { t.Error(r) } } func TestV2RayDialUDPConn(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, } dest, err := udpServer.Start() common.Must(err) defer udpServer.Close() config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } cfgBytes, err := proto.Marshal(config) common.Must(err) server, err := core.StartInstance(core.FormatProtobuf, cfgBytes) common.Must(err) defer server.Close() conn, err := core.Dial(context.Background(), server, dest) common.Must(err) defer conn.Close() const size = 1024 payload := make([]byte, size) common.Must2(rand.Read(payload)) for i := 0; i < 2; i++ { if _, err := conn.Write(payload); err != nil { t.Fatal(err) } } time.Sleep(time.Millisecond * 500) receive := make([]byte, size*2) for i := 0; i < 2; i++ { n, err := conn.Read(receive) if err != nil { t.Fatal("expect no error, but got ", err) } if n != size { t.Fatal("expect read size ", size, " but got ", n) } if r := cmp.Diff(xor(receive[:n]), payload); r != "" { t.Fatal(r) } } } func TestV2RayDialUDP(t *testing.T) { udpServer1 := udp.Server{ MsgProcessor: xor, } dest1, err := udpServer1.Start() common.Must(err) defer udpServer1.Close() udpServer2 := udp.Server{ MsgProcessor: xor2, } dest2, err := udpServer2.Start() common.Must(err) defer udpServer2.Close() config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } cfgBytes, err := proto.Marshal(config) common.Must(err) server, err := core.StartInstance(core.FormatProtobuf, cfgBytes) common.Must(err) defer server.Close() conn, err := core.DialUDP(context.Background(), server) common.Must(err) defer conn.Close() const size = 1024 { payload := make([]byte, size) common.Must2(rand.Read(payload)) if _, err := conn.WriteTo(payload, &net.UDPAddr{ IP: dest1.Address.IP(), Port: int(dest1.Port), }); err != nil { t.Fatal(err) } receive := make([]byte, size) if _, _, err := conn.ReadFrom(receive); err != nil { t.Fatal(err) } if r := cmp.Diff(xor(receive), payload); r != "" { t.Error(r) } } { payload := make([]byte, size) common.Must2(rand.Read(payload)) if _, err := conn.WriteTo(payload, &net.UDPAddr{ IP: dest2.Address.IP(), Port: int(dest2.Port), }); err != nil { t.Fatal(err) } receive := make([]byte, size) if _, _, err := conn.ReadFrom(receive); err != nil { t.Fatal(err) } if r := cmp.Diff(xor2(receive), payload); r != "" { t.Error(r) } } } ================================================ FILE: go.mod ================================================ module github.com/v2fly/v2ray-core/v5 go 1.25.5 toolchain go1.26.1 require ( github.com/adrg/xdg v0.5.3 github.com/apernet/quic-go v0.59.1-0.20260217092621-db4786c77a22 github.com/go-chi/chi/v5 v5.2.5 github.com/go-chi/render v1.0.3 github.com/go-playground/validator/v10 v10.30.1 github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.7.0 github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.3 github.com/improbable-eng/grpc-web v0.15.0 github.com/jhump/protoreflect v1.18.0 github.com/miekg/dns v1.1.72 github.com/mustafaturan/bus v1.0.2 github.com/pelletier/go-toml v1.9.5 github.com/pion/dtls/v2 v2.2.12 github.com/pion/stun/v3 v3.1.1 github.com/pion/transport/v2 v2.2.10 github.com/pires/go-proxyproto v0.11.0 github.com/quic-go/quic-go v0.59.0 github.com/refraction-networking/utls v1.8.2 github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb github.com/stretchr/testify v1.11.1 github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08 github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848 github.com/v2fly/hysteria/core/v2 v2.0.0-20260220231229-39018a43855e github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e github.com/v2fly/struc v0.0.0-20241227015403-8e8fa1badfd6 github.com/vincent-petithory/dataurl v1.0.0 github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 go.starlark.net v0.0.0-20230612165344-9532f5667272 go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 golang.org/x/crypto v0.49.0 golang.org/x/net v0.52.0 golang.org/x/sync v0.20.0 golang.org/x/sys v0.42.0 golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb google.golang.org/grpc v1.79.2 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 gvisor.dev/gvisor v0.0.0-20260218152508-eed1cebf761e h12.io/socks v1.0.3 lukechampine.com/blake3 v1.4.1 ) require ( github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 // indirect github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.6 // indirect github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/ebfe/bcrypt_pbkdf v0.0.0-20140212075826-3c8d2dcb253a // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gobwas/ws v1.2.1 // indirect github.com/google/btree v1.1.2 // indirect github.com/jhump/protoreflect/v2 v2.0.0-beta.1 // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/reedsolomon v1.11.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/mustafaturan/monoton v1.0.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pion/dtls/v3 v3.0.11 // indirect github.com/pion/logging v0.2.4 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/sctp v1.8.7 // indirect github.com/pion/transport/v3 v3.0.7 // indirect github.com/pion/transport/v4 v4.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/rs/cors v1.7.0 // indirect github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/wlynxg/anet v0.0.5 // indirect github.com/xtaci/smux v1.5.24 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/text v0.35.0 // indirect golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect nhooyr.io/websocket v1.8.6 // indirect ) ================================================ FILE: go.sum ================================================ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/FlowerWrong/water v0.0.0-20180301012659-01a4eaa1f6f2/go.mod h1:xrG5L7lq7T2DLnPr2frMnL906CNEoKRwLB+VYFhPq2w= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 h1:+JkXLHME8vLJafGhOH4aoV2Iu8bR55nU6iKMVfYVLjY= github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1/go.mod h1:nuudZmJhzWtx2212z+pkuy7B6nkBqa+xwNXZHL1j8cg= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apernet/quic-go v0.59.1-0.20260217092621-db4786c77a22 h1:00ziBGnLWQEcR9LThDwvxOznJJquJ9bYUdmBFnawLMU= github.com/apernet/quic-go v0.59.1-0.20260217092621-db4786c77a22/go.mod h1:Npbg8qBtAZlsAB3FWmqwlVh5jtVG6a4DlYsOylUpvzA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/ebfe/bcrypt_pbkdf v0.0.0-20140212075826-3c8d2dcb253a h1:YtdtTUN1iH97s+6PUjLnaiKSQj4oG1/EZ3N9bx6g4kU= github.com/ebfe/bcrypt_pbkdf v0.0.0-20140212075826-3c8d2dcb253a/go.mod h1:/CZpbhAusDOobpcb9yubw46kdYjq0zRC0Wpg9a9zFQM= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259 h1:ZHJ7+IGpuOXtVf6Zk/a3WuHQgkC+vXwaqfUBDFwahtI= github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259/go.mod h1:9Qcha0gTWLw//0VNka1Cbnjvg3pNKGFdAm7E9sBabxE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20210420193930-a4630ec28c79/go.mod h1:Opf9rtYVq0eTyX+aRVmRO9hE8ERAozcdrBxWG9Q6mkQ= github.com/gopherjs/websocket v0.0.0-20191103002815-9a42957e2b3a/go.mod h1:jd+zY81Fx2lC4bfw58+Rflg1srqmedQjbBUejKOjYNY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w= github.com/jhump/protoreflect v1.18.0/go.mod h1:ezWcltJIVF4zYdIFM+D/sHV4Oh5LNU08ORzCGfwvTz8= github.com/jhump/protoreflect/v2 v2.0.0-beta.1 h1:Dw1rslK/VotaUGYsv53XVWITr+5RCPXfvvlGrM/+B6w= github.com/jhump/protoreflect/v2 v2.0.0-beta.1/go.mod h1:D9LBEowZyv8/iSu97FU2zmXG3JxVTmNw21mu63niFzU= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU= github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lunixbochs/struc v0.0.0-20190916212049-a5c72983bc42/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mustafaturan/bus v1.0.2 h1:2x3ErwZ0uUPwwZ5ZZoknEQprdaxr68Yl3mY8jDye1Ws= github.com/mustafaturan/bus v1.0.2/go.mod h1:h7gfehm8TThv4Dcaa+wDQG7r7j6p74v+7ftr0Rq9i1Q= github.com/mustafaturan/monoton v0.3.1/go.mod h1:FOnE7NV3s3EWPXb8/7+/OSdiMBbdlkV0Lz8p1dc+vy8= github.com/mustafaturan/monoton v1.0.0 h1:8SCej+JiNn0lyps7V+Jzc1CRAkDR4EZPWrTupQ61YCQ= github.com/mustafaturan/monoton v1.0.0/go.mod h1:FOnE7NV3s3EWPXb8/7+/OSdiMBbdlkV0Lz8p1dc+vy8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14= github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pion/dtls/v2 v2.0.0-rc.7/go.mod h1:U199DvHpRBN0muE9+tVN4TMy1jvEhZIZ63lk4xkvVSk= github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/dtls/v3 v3.0.11 h1:zqn8YhoAU7d9whsWLhNiQlbB8QdpJj8XQVSc5ImUons= github.com/pion/dtls/v3 v3.0.11/go.mod h1:YEmmBYIoBsY3jmG56dsziTv/Lca9y4Om83370CXfqJ8= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/sctp v1.7.6/go.mod h1:ichkYQ5tlgCQwEwvgfdcAolqx1nHbYCxo4D7zK/K0X8= github.com/pion/sctp v1.8.7 h1:JnABvFakZueGAn4KU/4PSKg+GWbF6QWbKTWZOSGJjXw= github.com/pion/sctp v1.8.7/go.mod h1:g1Ul+ARqZq5JEmoFy87Q/4CePtKnTJ1QCL9dBBdN6AU= github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw= github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM= github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8= github.com/pion/transport/v2 v2.1.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o= github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM= github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4= github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo= github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 h1:zOjq+1/uLzn/Xo40stbvjIY/yehG0+mfmlsiEmc0xmQ= github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4/go.mod h1:aI+8yClBW+1uovkHw6HM01YXnYB8vohtB9C83wzx34E= github.com/seiflotfy/cuckoofilter v0.0.0-20200416141329-862a88987de7/go.mod h1:ET5mVvNjwaGXRgZxO9UZr7X+8eAf87AfIYNwRSp9s4Y= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/txthinking/runnergroup v0.0.0-20200327135940-540a793bb997/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= github.com/txthinking/socks5 v0.0.0-20200327133705-caf148ab5e9d/go.mod h1:d3n8NJ6QMRb6I/WAlp4z5ZPAoaeqDmX5NgVZA0mhe+I= github.com/txthinking/x v0.0.0-20200330144832-5ad2416896a9/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08 h1:4Yh46CVE3k/lPq6hUbEdbB1u1anRBXLewm3k+L0iOMc= github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08/go.mod h1:KAuQNm+LWQCOFqdBcUgihPzRpVXRKzGbTNhfEfRZ4wY= github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848 h1:p1UzXK6VAutXFFQMnre66h7g1BjRKUnLv0HfmmRoz7w= github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848/go.mod h1:p80Bv154ZtrGpXMN15slDCqc9UGmfBuUzheDFBYaW/M= github.com/v2fly/hysteria/core/v2 v2.0.0-20260220231229-39018a43855e h1:0vxrC4Rn4t421ecsY7nlMG5L7/1LJzcWUuyB3q7nnuc= github.com/v2fly/hysteria/core/v2 v2.0.0-20260220231229-39018a43855e/go.mod h1:onOGso2sRgruR/bUD1Vl39o+B4HVOdv+v7mS6E7pbn4= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= github.com/v2fly/struc v0.0.0-20241227015403-8e8fa1badfd6 h1:Qea2jW7g1hvQ9TkYq3aT2h0NDWjPQHtvDfmKXoWgJ9E= github.com/v2fly/struc v0.0.0-20241227015403-8e8fa1badfd6/go.mod h1:a/FYYQz8bW7wh2jmI+DVsbVYwLkgmgpml+GrJwV+eIo= github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 h1:I/ATawgO2RerCq9ACwL0wBB8xNXZdE3J+93MCEHReRs= github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432/go.mod h1:QN7Go2ftTVfx0aCTh9RXHV8pkpi0FtmbwQw40dy61wQ= github.com/xtaci/smux v1.5.12/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= github.com/xtaci/smux v1.5.15/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY= github.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.starlark.net v0.0.0-20230612165344-9532f5667272 h1:2/wtqS591wZyD2OsClsVBKRPEvBsQt/Js+fsCiYhwu8= go.starlark.net v0.0.0-20230612165344-9532f5667272/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 h1:nJAwRlGWZZDOD+6wni9KVUNHMpHko/OnRwsrCYeAzPo= go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gvisor.dev/gvisor v0.0.0-20260218152508-eed1cebf761e h1:OaBM9m99p7sXzsj1A7lrwLAdWZKCwlA8jBn3BXFO+dE= gvisor.dev/gvisor v0.0.0-20260218152508-eed1cebf761e/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q= h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo= h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= ================================================ FILE: infra/conf/cfgcommon/buildable.go ================================================ package cfgcommon import ( "context" "github.com/golang/protobuf/proto" ) type Buildable interface { Build() (proto.Message, error) } type BuildableV5 interface { BuildV5(ctx context.Context) (proto.Message, error) } ================================================ FILE: infra/conf/cfgcommon/common.go ================================================ package cfgcommon import ( "encoding/json" "os" "strings" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type StringList []string func NewStringList(raw []string) *StringList { list := StringList(raw) return &list } func (v StringList) Len() int { return len(v) } func (v *StringList) UnmarshalJSON(data []byte) error { var strarray []string if err := json.Unmarshal(data, &strarray); err == nil { *v = *NewStringList(strarray) return nil } var rawstr string if err := json.Unmarshal(data, &rawstr); err == nil { strlist := strings.Split(rawstr, ",") *v = *NewStringList(strlist) return nil } return newError("unknown format of a string list: " + string(data)) } type Address struct { net.Address } func (v *Address) UnmarshalJSON(data []byte) error { var rawStr string if err := json.Unmarshal(data, &rawStr); err != nil { return newError("invalid address: ", string(data)).Base(err) } v.Address = net.ParseAddress(rawStr) return nil } func (v *Address) Build() *net.IPOrDomain { return net.NewIPOrDomain(v.Address) } type Network string func (v Network) Build() net.Network { return net.ParseNetwork(string(v)) } type NetworkList []Network func (v *NetworkList) UnmarshalJSON(data []byte) error { var strarray []Network if err := json.Unmarshal(data, &strarray); err == nil { nl := NetworkList(strarray) *v = nl return nil } var rawstr Network if err := json.Unmarshal(data, &rawstr); err == nil { strlist := strings.Split(string(rawstr), ",") nl := make([]Network, len(strlist)) for idx, network := range strlist { nl[idx] = Network(network) } *v = nl return nil } return newError("unknown format of a string list: " + string(data)) } func (v *NetworkList) Build() []net.Network { if v == nil { return []net.Network{net.Network_TCP} } list := make([]net.Network, 0, len(*v)) for _, network := range *v { list = append(list, network.Build()) } return list } func parseIntPort(data []byte) (net.Port, error) { var intPort uint32 err := json.Unmarshal(data, &intPort) if err != nil { return net.Port(0), err } return net.PortFromInt(intPort) } func parseStringPort(s string) (net.Port, net.Port, error) { if strings.HasPrefix(s, "env:") { s = s[4:] s = os.Getenv(s) } pair := strings.SplitN(s, "-", 2) if len(pair) == 0 { return net.Port(0), net.Port(0), newError("invalid port range: ", s) } if len(pair) == 1 { port, err := net.PortFromString(pair[0]) return port, port, err } fromPort, err := net.PortFromString(pair[0]) if err != nil { return net.Port(0), net.Port(0), err } toPort, err := net.PortFromString(pair[1]) if err != nil { return net.Port(0), net.Port(0), err } return fromPort, toPort, nil } func parseJSONStringPort(data []byte) (net.Port, net.Port, error) { var s string err := json.Unmarshal(data, &s) if err != nil { return net.Port(0), net.Port(0), err } return parseStringPort(s) } type PortRange struct { From uint32 To uint32 } func (v *PortRange) Build() *net.PortRange { return &net.PortRange{ From: v.From, To: v.To, } } // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (v *PortRange) UnmarshalJSON(data []byte) error { port, err := parseIntPort(data) if err == nil { v.From = uint32(port) v.To = uint32(port) return nil } from, to, err := parseJSONStringPort(data) if err == nil { v.From = uint32(from) v.To = uint32(to) if v.From > v.To { return newError("invalid port range ", v.From, " -> ", v.To) } return nil } return newError("invalid port range: ", string(data)) } type PortList struct { Range []PortRange } func (list *PortList) Build() *net.PortList { portList := new(net.PortList) for _, r := range list.Range { portList.Range = append(portList.Range, r.Build()) } return portList } // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (list *PortList) UnmarshalJSON(data []byte) error { var listStr string var number uint32 if err := json.Unmarshal(data, &listStr); err != nil { if err2 := json.Unmarshal(data, &number); err2 != nil { return newError("invalid port: ", string(data)).Base(err2) } } err := list.UnmarshalText(listStr) if err != nil { return err } if number != 0 { list.Range = append(list.Range, PortRange{From: number, To: number}) } return nil } func (list *PortList) UnmarshalText(listStr string) error { rangelist := strings.Split(listStr, ",") for _, rangeStr := range rangelist { trimmed := strings.TrimSpace(rangeStr) if len(trimmed) > 0 { if strings.Contains(trimmed, "-") { from, to, err := parseStringPort(trimmed) if err != nil { return newError("invalid port range: ", trimmed).Base(err) } list.Range = append(list.Range, PortRange{From: uint32(from), To: uint32(to)}) } else { port, err := parseIntPort([]byte(trimmed)) if err != nil { return newError("invalid port: ", trimmed).Base(err) } list.Range = append(list.Range, PortRange{From: uint32(port), To: uint32(port)}) } } } return nil } type User struct { EmailString string `json:"email"` LevelByte byte `json:"level"` } func (v *User) Build() *protocol.User { return &protocol.User{ Email: v.EmailString, Level: uint32(v.LevelByte), } } ================================================ FILE: infra/conf/cfgcommon/common_test.go ================================================ package cfgcommon_test import ( "encoding/json" "os" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" ) func TestStringListUnmarshalError(t *testing.T) { rawJSON := `1234` list := new(cfgcommon.StringList) err := json.Unmarshal([]byte(rawJSON), list) if err == nil { t.Error("expected error, but got nil") } } func TestStringListLen(t *testing.T) { rawJSON := `"a, b, c, d"` var list cfgcommon.StringList err := json.Unmarshal([]byte(rawJSON), &list) common.Must(err) if r := cmp.Diff([]string(list), []string{"a", " b", " c", " d"}); r != "" { t.Error(r) } } func TestIPParsing(t *testing.T) { rawJSON := "\"8.8.8.8\"" var address cfgcommon.Address err := json.Unmarshal([]byte(rawJSON), &address) common.Must(err) if r := cmp.Diff(address.IP(), net.IP{8, 8, 8, 8}); r != "" { t.Error(r) } } func TestDomainParsing(t *testing.T) { rawJSON := "\"v2fly.org\"" var address cfgcommon.Address common.Must(json.Unmarshal([]byte(rawJSON), &address)) if address.Domain() != "v2fly.org" { t.Error("domain: ", address.Domain()) } } func TestURLParsing(t *testing.T) { { rawJSON := "\"https://dns.google/dns-query\"" var address cfgcommon.Address common.Must(json.Unmarshal([]byte(rawJSON), &address)) if address.Domain() != "https://dns.google/dns-query" { t.Error("URL: ", address.Domain()) } } { rawJSON := "\"https+local://dns.google/dns-query\"" var address cfgcommon.Address common.Must(json.Unmarshal([]byte(rawJSON), &address)) if address.Domain() != "https+local://dns.google/dns-query" { t.Error("URL: ", address.Domain()) } } } func TestInvalidAddressJson(t *testing.T) { rawJSON := "1234" var address cfgcommon.Address err := json.Unmarshal([]byte(rawJSON), &address) if err == nil { t.Error("nil error") } } func TestStringNetwork(t *testing.T) { var network cfgcommon.Network common.Must(json.Unmarshal([]byte(`"tcp"`), &network)) if v := network.Build(); v != net.Network_TCP { t.Error("network: ", v) } } func TestArrayNetworkList(t *testing.T) { var list cfgcommon.NetworkList common.Must(json.Unmarshal([]byte("[\"Tcp\"]"), &list)) nlist := list.Build() if !net.HasNetwork(nlist, net.Network_TCP) { t.Error("no tcp network") } if net.HasNetwork(nlist, net.Network_UDP) { t.Error("has udp network") } } func TestStringNetworkList(t *testing.T) { var list cfgcommon.NetworkList common.Must(json.Unmarshal([]byte("\"TCP, ip\""), &list)) nlist := list.Build() if !net.HasNetwork(nlist, net.Network_TCP) { t.Error("no tcp network") } if net.HasNetwork(nlist, net.Network_UDP) { t.Error("has udp network") } } func TestInvalidNetworkJson(t *testing.T) { var list cfgcommon.NetworkList err := json.Unmarshal([]byte("0"), &list) if err == nil { t.Error("nil error") } } func TestIntPort(t *testing.T) { var portRange cfgcommon.PortRange common.Must(json.Unmarshal([]byte("1234"), &portRange)) if r := cmp.Diff(portRange, cfgcommon.PortRange{ From: 1234, To: 1234, }); r != "" { t.Error(r) } } func TestOverRangeIntPort(t *testing.T) { var portRange cfgcommon.PortRange err := json.Unmarshal([]byte("70000"), &portRange) if err == nil { t.Error("nil error") } err = json.Unmarshal([]byte("-1"), &portRange) if err == nil { t.Error("nil error") } } func TestEnvPort(t *testing.T) { common.Must(os.Setenv("PORT", "1234")) var portRange cfgcommon.PortRange common.Must(json.Unmarshal([]byte("\"env:PORT\""), &portRange)) if r := cmp.Diff(portRange, cfgcommon.PortRange{ From: 1234, To: 1234, }); r != "" { t.Error(r) } } func TestSingleStringPort(t *testing.T) { var portRange cfgcommon.PortRange common.Must(json.Unmarshal([]byte("\"1234\""), &portRange)) if r := cmp.Diff(portRange, cfgcommon.PortRange{ From: 1234, To: 1234, }); r != "" { t.Error(r) } } func TestStringPairPort(t *testing.T) { var portRange cfgcommon.PortRange common.Must(json.Unmarshal([]byte("\"1234-5678\""), &portRange)) if r := cmp.Diff(portRange, cfgcommon.PortRange{ From: 1234, To: 5678, }); r != "" { t.Error(r) } } func TestOverRangeStringPort(t *testing.T) { var portRange cfgcommon.PortRange err := json.Unmarshal([]byte("\"65536\""), &portRange) if err == nil { t.Error("nil error") } err = json.Unmarshal([]byte("\"70000-80000\""), &portRange) if err == nil { t.Error("nil error") } err = json.Unmarshal([]byte("\"1-90000\""), &portRange) if err == nil { t.Error("nil error") } err = json.Unmarshal([]byte("\"700-600\""), &portRange) if err == nil { t.Error("nil error") } } func TestUserParsing(t *testing.T) { user := new(cfgcommon.User) common.Must(json.Unmarshal([]byte(`{ "id": "96edb838-6d68-42ef-a933-25f7ac3a9d09", "email": "love@v2fly.org", "level": 1, "alterId": 100 }`), user)) nUser := user.Build() if r := cmp.Diff(nUser, &protocol.User{ Level: 1, Email: "love@v2fly.org", }, cmpopts.IgnoreUnexported(protocol.User{})); r != "" { t.Error(r) } } func TestInvalidUserJson(t *testing.T) { user := new(cfgcommon.User) err := json.Unmarshal([]byte(`{"email": 1234}`), user) if err == nil { t.Error("nil error") } } ================================================ FILE: infra/conf/cfgcommon/duration/duration.go ================================================ package duration import ( "encoding/json" "fmt" "time" ) type Duration int64 func (d *Duration) MarshalJSON() ([]byte, error) { dr := time.Duration(*d) return json.Marshal(dr.String()) } func (d *Duration) UnmarshalJSON(b []byte) error { var v interface{} if err := json.Unmarshal(b, &v); err != nil { return err } switch value := v.(type) { case string: var err error dr, err := time.ParseDuration(value) if err != nil { return err } *d = Duration(dr) return nil default: return fmt.Errorf("invalid duration: %v", v) } } ================================================ FILE: infra/conf/cfgcommon/duration/duration_test.go ================================================ package duration_test import ( "encoding/json" "testing" "time" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/duration" ) type testWithDuration struct { Duration duration.Duration } func TestDurationJSON(t *testing.T) { expected := &testWithDuration{ Duration: duration.Duration(time.Hour), } data, err := json.Marshal(expected) if err != nil { t.Error(err) return } actual := &testWithDuration{} err = json.Unmarshal(data, &actual) if err != nil { t.Error(err) return } if actual.Duration != expected.Duration { t.Errorf("expected: %s, actual: %s", time.Duration(expected.Duration), time.Duration(actual.Duration)) } } ================================================ FILE: infra/conf/cfgcommon/errors.generated.go ================================================ package cfgcommon import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/cfgcommon/loader/errors.generated.go ================================================ package loader import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/cfgcommon/loader/loader.go ================================================ package loader import ( "encoding/json" "strings" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type ConfigCreator func() interface{} type ConfigCreatorCache map[string]ConfigCreator func (v ConfigCreatorCache) RegisterCreator(id string, creator ConfigCreator) error { if _, found := v[id]; found { return newError(id, " already registered.").AtError() } v[id] = creator return nil } func (v ConfigCreatorCache) CreateConfig(id string) (interface{}, error) { creator, found := v[id] if !found { return nil, newError("unknown config id: ", id) } return creator(), nil } type JSONConfigLoader struct { cache ConfigCreatorCache idKey string configKey string } func NewJSONConfigLoader(cache ConfigCreatorCache, idKey string, configKey string) *JSONConfigLoader { return &JSONConfigLoader{ idKey: idKey, configKey: configKey, cache: cache, } } func (v *JSONConfigLoader) LoadWithID(raw []byte, id string) (interface{}, error) { id = strings.ToLower(id) config, err := v.cache.CreateConfig(id) if err != nil { return nil, err } if err := json.Unmarshal(raw, config); err != nil { return nil, err } return config, nil } func (v *JSONConfigLoader) Load(raw []byte) (interface{}, string, error) { var obj map[string]json.RawMessage if err := json.Unmarshal(raw, &obj); err != nil { return nil, "", err } rawID, found := obj[v.idKey] if !found { return nil, "", newError(v.idKey, " not found in JSON context").AtError() } var id string if err := json.Unmarshal(rawID, &id); err != nil { return nil, "", err } rawConfig := json.RawMessage(raw) if len(v.configKey) > 0 { configValue, found := obj[v.configKey] if found { rawConfig = configValue } else { // Default to empty json object. rawConfig = json.RawMessage([]byte("{}")) } } config, err := v.LoadWithID([]byte(rawConfig), id) if err != nil { return nil, id, err } return config, id, nil } ================================================ FILE: infra/conf/cfgcommon/muxcfg/mux.go ================================================ package muxcfg import "github.com/v2fly/v2ray-core/v5/app/proxyman" type MuxConfig struct { Enabled bool `json:"enabled"` Concurrency int16 `json:"concurrency"` } // Build creates MultiplexingConfig, Concurrency < 0 completely disables mux. func (m *MuxConfig) Build() *proxyman.MultiplexingConfig { if m.Concurrency < 0 { return nil } var con uint32 = 8 if m.Concurrency > 0 { con = uint32(m.Concurrency) } return &proxyman.MultiplexingConfig{ Enabled: m.Enabled, Concurrency: con, } } ================================================ FILE: infra/conf/cfgcommon/proxycfg/errors.generated.go ================================================ package proxycfg import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/cfgcommon/proxycfg/proxy.go ================================================ package proxycfg import "github.com/v2fly/v2ray-core/v5/transport/internet" type ProxyConfig struct { Tag string `json:"tag"` TransportLayerProxy bool `json:"transportLayer"` } //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen // Build implements Buildable. func (v *ProxyConfig) Build() (*internet.ProxyConfig, error) { if v.Tag == "" { return nil, newError("Proxy tag is not set.") } return &internet.ProxyConfig{ Tag: v.Tag, TransportLayerProxy: v.TransportLayerProxy, }, nil } ================================================ FILE: infra/conf/cfgcommon/session.go ================================================ package cfgcommon import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" ) type configureLoadingContext int const confContextKey = configureLoadingContext(1) type configureLoadingEnvironment struct { geoLoader geodata.Loader } func (c *configureLoadingEnvironment) GetGeoLoader() geodata.Loader { if c.geoLoader == nil { var err error c.geoLoader, err = geodata.GetGeoDataLoader("standard") common.Must(err) } return c.geoLoader } func (c *configureLoadingEnvironment) doNotImpl() {} type ConfigureLoadingEnvironmentCapabilitySet interface { GetGeoLoader() geodata.Loader } type ConfigureLoadingEnvironment interface { ConfigureLoadingEnvironmentCapabilitySet doNotImpl() // TODO environment.BaseEnvironmentCapabilitySet // TODO environment.FileSystemCapabilitySet } func NewConfigureLoadingContext(ctx context.Context) context.Context { environment := &configureLoadingEnvironment{} return context.WithValue(ctx, confContextKey, environment) } func GetConfigureLoadingEnvironment(ctx context.Context) ConfigureLoadingEnvironment { return ctx.Value(confContextKey).(ConfigureLoadingEnvironment) } func SetGeoDataLoader(ctx context.Context, loader geodata.Loader) { GetConfigureLoadingEnvironment(ctx).(*configureLoadingEnvironment).geoLoader = loader } ================================================ FILE: infra/conf/cfgcommon/sniffer/errors.generated.go ================================================ package sniffer import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/cfgcommon/sniffer/sniffer.go ================================================ package sniffer import ( "strings" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type SniffingConfig struct { Enabled bool `json:"enabled"` DestOverride *cfgcommon.StringList `json:"destOverride"` MetadataOnly bool `json:"metadataOnly"` } // Build implements Buildable. func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) { var p []string if c.DestOverride != nil { for _, domainOverride := range *c.DestOverride { switch strings.ToLower(domainOverride) { case "http": p = append(p, "http") case "tls", "https", "ssl": p = append(p, "tls") case "quic": p = append(p, "quic") case "fakedns": p = append(p, "fakedns") case "fakedns+others": p = append(p, "fakedns+others") default: return nil, newError("unknown protocol: ", domainOverride) } } } return &proxyman.SniffingConfig{ Enabled: c.Enabled, DestinationOverride: p, MetadataOnly: c.MetadataOnly, }, nil } ================================================ FILE: infra/conf/cfgcommon/socketcfg/socket.go ================================================ package socketcfg import ( "strings" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type SocketConfig struct { Mark uint32 `json:"mark"` TFO *bool `json:"tcpFastOpen"` TProxy string `json:"tproxy"` AcceptProxyProtocol bool `json:"acceptProxyProtocol"` TCPKeepAliveInterval int32 `json:"tcpKeepAliveInterval"` TCPKeepAliveIdle int32 `json:"tcpKeepAliveIdle"` TFOQueueLength uint32 `json:"tcpFastOpenQueueLength"` BindToDevice string `json:"bindToDevice"` RxBufSize uint64 `json:"rxBufSize"` TxBufSize uint64 `json:"txBufSize"` ForceBufSize bool `json:"forceBufSize"` MPTCP *bool `json:"mptcp"` } // Build implements Buildable. func (c *SocketConfig) Build() (*internet.SocketConfig, error) { var tfoSettings internet.SocketConfig_TCPFastOpenState if c.TFO != nil { if *c.TFO { tfoSettings = internet.SocketConfig_Enable } else { tfoSettings = internet.SocketConfig_Disable } } tfoQueueLength := c.TFOQueueLength if tfoQueueLength == 0 { tfoQueueLength = 4096 } var tproxy internet.SocketConfig_TProxyMode switch strings.ToLower(c.TProxy) { case "tproxy": tproxy = internet.SocketConfig_TProxy case "redirect": tproxy = internet.SocketConfig_Redirect default: tproxy = internet.SocketConfig_Off } var mptcpSettings internet.MPTCPState if c.MPTCP != nil { if *c.MPTCP { mptcpSettings = internet.MPTCPState_Enable } else { mptcpSettings = internet.MPTCPState_Disable } } return &internet.SocketConfig{ Mark: c.Mark, Tfo: tfoSettings, TfoQueueLength: tfoQueueLength, Tproxy: tproxy, AcceptProxyProtocol: c.AcceptProxyProtocol, TcpKeepAliveInterval: c.TCPKeepAliveInterval, TcpKeepAliveIdle: c.TCPKeepAliveIdle, RxBufSize: int64(c.RxBufSize), TxBufSize: int64(c.TxBufSize), ForceBufSize: c.ForceBufSize, BindToDevice: c.BindToDevice, Mptcp: mptcpSettings, }, nil } ================================================ FILE: infra/conf/cfgcommon/testassist/general.go ================================================ package testassist import ( "encoding/json" "testing" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" ) func LoadJSON(creator func() cfgcommon.Buildable) func(string) (proto.Message, error) { return func(s string) (proto.Message, error) { instance := creator() if err := json.Unmarshal([]byte(s), instance); err != nil { return nil, err } return instance.Build() } } type TestCase struct { Input string Parser func(string) (proto.Message, error) Output proto.Message } func RunMultiTestCase(t *testing.T, testCases []TestCase) { for _, testCase := range testCases { actual, err := testCase.Parser(testCase.Input) common.Must(err) if !proto.Equal(actual, testCase.Output) { t.Fatalf("Failed in test case:\n%s\nActual:\n%v\nExpected:\n%v", testCase.Input, actual, testCase.Output) } } } ================================================ FILE: infra/conf/cfgcommon/tlscfg/errors.generated.go ================================================ package tlscfg import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/cfgcommon/tlscfg/tls.go ================================================ package tlscfg import ( "encoding/base64" "strings" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type TLSConfig struct { Insecure bool `json:"allowInsecure"` Certs []*TLSCertConfig `json:"certificates"` ServerName string `json:"serverName"` ALPN *cfgcommon.StringList `json:"alpn"` EnableSessionResumption bool `json:"enableSessionResumption"` DisableSystemRoot bool `json:"disableSystemRoot"` PinnedPeerCertificateChainSha256 *[]string `json:"pinnedPeerCertificateChainSha256"` VerifyClientCertificate bool `json:"verifyClientCertificate"` ECHConfig string `json:"echConfig"` ECHDOHServer string `json:"echDohServer"` } // Build implements Buildable. func (c *TLSConfig) Build() (proto.Message, error) { config := new(tls.Config) config.Certificate = make([]*tls.Certificate, len(c.Certs)) for idx, certConf := range c.Certs { cert, err := certConf.Build() if err != nil { return nil, err } config.Certificate[idx] = cert } serverName := c.ServerName config.AllowInsecure = c.Insecure config.VerifyClientCertificate = c.VerifyClientCertificate if len(c.ServerName) > 0 { config.ServerName = serverName } if c.ALPN != nil && len(*c.ALPN) > 0 { config.NextProtocol = []string(*c.ALPN) } config.EnableSessionResumption = c.EnableSessionResumption config.DisableSystemRoot = c.DisableSystemRoot if c.PinnedPeerCertificateChainSha256 != nil { config.PinnedPeerCertificateChainSha256 = [][]byte{} for _, v := range *c.PinnedPeerCertificateChainSha256 { hashValue, err := base64.StdEncoding.DecodeString(v) if err != nil { return nil, err } config.PinnedPeerCertificateChainSha256 = append(config.PinnedPeerCertificateChainSha256, hashValue) } } if c.ECHConfig != "" { ECHConfig, err := base64.StdEncoding.DecodeString(c.ECHConfig) if err != nil { return nil, newError("invalid ECH Config", c.ECHConfig) } config.EchConfig = ECHConfig } config.Ech_DOHserver = c.ECHDOHServer return config, nil } type TLSCertConfig struct { CertFile string `json:"certificateFile"` CertStr []string `json:"certificate"` KeyFile string `json:"keyFile"` KeyStr []string `json:"key"` Usage string `json:"usage"` } // Build implements Buildable. func (c *TLSCertConfig) Build() (*tls.Certificate, error) { certificate := new(tls.Certificate) cert, err := readFileOrString(c.CertFile, c.CertStr) if err != nil { return nil, newError("failed to parse certificate").Base(err) } certificate.Certificate = cert if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 { key, err := readFileOrString(c.KeyFile, c.KeyStr) if err != nil { return nil, newError("failed to parse key").Base(err) } certificate.Key = key } switch strings.ToLower(c.Usage) { case "encipherment": certificate.Usage = tls.Certificate_ENCIPHERMENT case "verify": certificate.Usage = tls.Certificate_AUTHORITY_VERIFY case "verifyclient": certificate.Usage = tls.Certificate_AUTHORITY_VERIFY_CLIENT case "issue": certificate.Usage = tls.Certificate_AUTHORITY_ISSUE default: certificate.Usage = tls.Certificate_ENCIPHERMENT } return certificate, nil } func readFileOrString(f string, s []string) ([]byte, error) { if len(f) > 0 { return filesystem.ReadFile(f) } if len(s) > 0 { return []byte(strings.Join(s, "\n")), nil } return nil, newError("both file and bytes are empty.") } ================================================ FILE: infra/conf/geodata/attr.go ================================================ package geodata import ( "strings" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" ) type AttributeList struct { matcher []AttributeMatcher } func (al *AttributeList) Match(domain *routercommon.Domain) bool { for _, matcher := range al.matcher { if !matcher.Match(domain) { return false } } return true } func (al *AttributeList) IsEmpty() bool { return len(al.matcher) == 0 } func parseAttrs(attrs []string) *AttributeList { al := new(AttributeList) for _, attr := range attrs { trimmedAttr := strings.ToLower(strings.TrimSpace(attr)) if len(trimmedAttr) == 0 { continue } al.matcher = append(al.matcher, BooleanMatcher(trimmedAttr)) } return al } type AttributeMatcher interface { Match(*routercommon.Domain) bool } type BooleanMatcher string func (m BooleanMatcher) Match(domain *routercommon.Domain) bool { for _, attr := range domain.Attribute { if strings.EqualFold(attr.GetKey(), string(m)) { return true } } return false } ================================================ FILE: infra/conf/geodata/errors.generated.go ================================================ package geodata import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/geodata/geodata.go ================================================ package geodata import ( "strings" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" ) type loader struct { LoaderImplementation } func (l *loader) LoadGeoSite(list string) ([]*routercommon.Domain, error) { return l.LoadGeoSiteWithAttr("geosite.dat", list) } func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*routercommon.Domain, error) { parts := strings.Split(siteWithAttr, "@") if len(parts) == 0 { return nil, newError("empty rule") } list := strings.TrimSpace(parts[0]) attrVal := parts[1:] if len(list) == 0 { return nil, newError("empty listname in rule: ", siteWithAttr) } domains, err := l.LoadSite(file, list) if err != nil { return nil, err } attrs := parseAttrs(attrVal) if attrs.IsEmpty() { if strings.Contains(siteWithAttr, "@") { newError("empty attribute list: ", siteWithAttr) } return domains, nil } filteredDomains := make([]*routercommon.Domain, 0, len(domains)) hasAttrMatched := false for _, domain := range domains { if attrs.Match(domain) { hasAttrMatched = true filteredDomains = append(filteredDomains, domain) } } if !hasAttrMatched { newError("attribute match no rule: geosite:", siteWithAttr) } return filteredDomains, nil } func (l *loader) LoadGeoIP(country string) ([]*routercommon.CIDR, error) { return l.LoadIP("geoip.dat", country) } var loaders map[string]func() LoaderImplementation func RegisterGeoDataLoaderImplementationCreator(name string, loader func() LoaderImplementation) { if loaders == nil { loaders = map[string]func() LoaderImplementation{} } loaders[name] = loader } func getGeoDataLoaderImplementation(name string) (LoaderImplementation, error) { if geoLoader, ok := loaders[name]; ok { return geoLoader(), nil } return nil, newError("unable to locate GeoData loader ", name) } func GetGeoDataLoader(name string) (Loader, error) { loadImpl, err := getGeoDataLoaderImplementation(name) if err == nil { return &loader{loadImpl}, nil } return nil, err } ================================================ FILE: infra/conf/geodata/geodata_test.go ================================================ package geodata_test import ( "errors" "io/fs" "os" "path/filepath" "runtime" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/memconservative" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/standard" ) func init() { const ( geoipURL = "https://raw.githubusercontent.com/v2fly/geoip/release/geoip.dat" geositeURL = "https://raw.githubusercontent.com/v2fly/domain-list-community/release/dlc.dat" ) wd, err := os.Getwd() common.Must(err) tempPath := filepath.Join(wd, "..", "..", "..", "testing", "temp") geoipPath := filepath.Join(tempPath, "geoip.dat") geositePath := filepath.Join(tempPath, "geosite.dat") os.Setenv("v2ray.location.asset", tempPath) if _, err := os.Stat(geoipPath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geoipBytes, err := common.FetchHTTPContent(geoipURL) common.Must(err) common.Must(filesystem.WriteFile(geoipPath, geoipBytes)) } if _, err := os.Stat(geositePath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geositeBytes, err := common.FetchHTTPContent(geositeURL) common.Must(err) common.Must(filesystem.WriteFile(geositePath, geositeBytes)) } } func BenchmarkStandardLoaderGeoIP(b *testing.B) { standardLoader, err := geodata.GetGeoDataLoader("standard") common.Must(err) m1 := runtime.MemStats{} m2 := runtime.MemStats{} runtime.ReadMemStats(&m1) standardLoader.LoadGeoIP("cn") standardLoader.LoadGeoIP("us") standardLoader.LoadGeoIP("private") runtime.ReadMemStats(&m2) b.ReportMetric(float64(m2.Alloc-m1.Alloc)/1024/1024, "MiB(GeoIP-Alloc)") b.ReportMetric(float64(m2.TotalAlloc-m1.TotalAlloc)/1024/1024, "MiB(GeoIP-TotalAlloc)") } func BenchmarkStandardLoaderGeoSite(b *testing.B) { standardLoader, err := geodata.GetGeoDataLoader("standard") common.Must(err) m3 := runtime.MemStats{} m4 := runtime.MemStats{} runtime.ReadMemStats(&m3) standardLoader.LoadGeoSite("cn") standardLoader.LoadGeoSite("geolocation-!cn") standardLoader.LoadGeoSite("private") runtime.ReadMemStats(&m4) b.ReportMetric(float64(m4.Alloc-m3.Alloc)/1024/1024, "MiB(GeoSite-Alloc)") b.ReportMetric(float64(m4.TotalAlloc-m3.TotalAlloc)/1024/1024, "MiB(GeoSite-TotalAlloc)") } func BenchmarkMemconservativeLoaderGeoIP(b *testing.B) { standardLoader, err := geodata.GetGeoDataLoader("memconservative") common.Must(err) m1 := runtime.MemStats{} m2 := runtime.MemStats{} runtime.ReadMemStats(&m1) standardLoader.LoadGeoIP("cn") standardLoader.LoadGeoIP("us") standardLoader.LoadGeoIP("private") runtime.ReadMemStats(&m2) b.ReportMetric(float64(m2.Alloc-m1.Alloc)/1024, "KiB(GeoIP-Alloc)") b.ReportMetric(float64(m2.TotalAlloc-m1.TotalAlloc)/1024/1024, "MiB(GeoIP-TotalAlloc)") } func BenchmarkMemconservativeLoaderGeoSite(b *testing.B) { standardLoader, err := geodata.GetGeoDataLoader("memconservative") common.Must(err) m3 := runtime.MemStats{} m4 := runtime.MemStats{} runtime.ReadMemStats(&m3) standardLoader.LoadGeoSite("cn") standardLoader.LoadGeoSite("geolocation-!cn") standardLoader.LoadGeoSite("private") runtime.ReadMemStats(&m4) b.ReportMetric(float64(m4.Alloc-m3.Alloc)/1024, "KiB(GeoSite-Alloc)") b.ReportMetric(float64(m4.TotalAlloc-m3.TotalAlloc)/1024/1024, "MiB(GeoSite-TotalAlloc)") } func BenchmarkAllLoader(b *testing.B) { type testingProfileForLoader struct { name string } testCase := []testingProfileForLoader{ {"standard"}, {"memconservative"}, } for _, v := range testCase { b.Run(v.name, func(b *testing.B) { b.Run("Geosite", func(b *testing.B) { loader, err := geodata.GetGeoDataLoader(v.name) if err != nil { b.Fatal(err) } m3 := runtime.MemStats{} m4 := runtime.MemStats{} runtime.ReadMemStats(&m3) loader.LoadGeoSite("cn") loader.LoadGeoSite("geolocation-!cn") loader.LoadGeoSite("private") runtime.ReadMemStats(&m4) b.ReportMetric(float64(m4.Alloc-m3.Alloc)/1024, "KiB(GeoSite-Alloc)") b.ReportMetric(float64(m4.TotalAlloc-m3.TotalAlloc)/1024/1024, "MiB(GeoSite-TotalAlloc)") }) b.Run("GeoIP", func(b *testing.B) { loader, err := geodata.GetGeoDataLoader(v.name) if err != nil { b.Fatal(err) } m1 := runtime.MemStats{} m2 := runtime.MemStats{} runtime.ReadMemStats(&m1) loader.LoadGeoIP("cn") loader.LoadGeoIP("us") loader.LoadGeoIP("private") runtime.ReadMemStats(&m2) b.ReportMetric(float64(m2.Alloc-m1.Alloc)/1024/1024, "MiB(GeoIP-Alloc)") b.ReportMetric(float64(m2.TotalAlloc-m1.TotalAlloc)/1024/1024, "MiB(GeoIP-TotalAlloc)") }) }) } } ================================================ FILE: infra/conf/geodata/geodataproto.go ================================================ package geodata import ( "github.com/v2fly/v2ray-core/v5/app/router/routercommon" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type LoaderImplementation interface { LoadSite(filename, list string) ([]*routercommon.Domain, error) LoadIP(filename, country string) ([]*routercommon.CIDR, error) } type Loader interface { LoaderImplementation LoadGeoSite(list string) ([]*routercommon.Domain, error) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*routercommon.Domain, error) LoadGeoIP(country string) ([]*routercommon.CIDR, error) } ================================================ FILE: infra/conf/geodata/memconservative/cache.go ================================================ package memconservative import ( "os" "strings" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common/platform" ) type GeoIPCache map[string]*routercommon.GeoIP func (g GeoIPCache) Has(key string) bool { return !(g.Get(key) == nil) } func (g GeoIPCache) Get(key string) *routercommon.GeoIP { if g == nil { return nil } return g[key] } func (g GeoIPCache) Set(key string, value *routercommon.GeoIP) { if g == nil { g = make(map[string]*routercommon.GeoIP) } g[key] = value } func (g GeoIPCache) Unmarshal(filename, code string) (*routercommon.GeoIP, error) { asset := platform.GetAssetLocation(filename) idx := strings.ToLower(asset + ":" + code) if g.Has(idx) { return g.Get(idx), nil } geoipBytes, err := Decode(asset, code) switch err { case nil: var geoip routercommon.GeoIP if err := proto.Unmarshal(geoipBytes, &geoip); err != nil { return nil, err } g.Set(idx, &geoip) return &geoip, nil case errCodeNotFound: return nil, newError("country code ", code, " not found in ", filename) case errFailedToReadBytes, errFailedToReadExpectedLenBytes, errInvalidGeodataFile, errInvalidGeodataVarintLength: newError("failed to decode geoip file: ", filename, ", fallback to the original ReadFile method") geoipBytes, err = os.ReadFile(asset) if err != nil { return nil, err } var geoipList routercommon.GeoIPList if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { return nil, err } for _, geoip := range geoipList.GetEntry() { if strings.EqualFold(code, geoip.GetCountryCode()) { g.Set(idx, geoip) return geoip, nil } } default: return nil, err } return nil, newError("country code ", code, " not found in ", filename) } type GeoSiteCache map[string]*routercommon.GeoSite func (g GeoSiteCache) Has(key string) bool { return !(g.Get(key) == nil) } func (g GeoSiteCache) Get(key string) *routercommon.GeoSite { if g == nil { return nil } return g[key] } func (g GeoSiteCache) Set(key string, value *routercommon.GeoSite) { if g == nil { g = make(map[string]*routercommon.GeoSite) } g[key] = value } func (g GeoSiteCache) Unmarshal(filename, code string) (*routercommon.GeoSite, error) { asset := platform.GetAssetLocation(filename) idx := strings.ToLower(asset + ":" + code) if g.Has(idx) { return g.Get(idx), nil } geositeBytes, err := Decode(asset, code) switch err { case nil: var geosite routercommon.GeoSite if err := proto.Unmarshal(geositeBytes, &geosite); err != nil { return nil, err } g.Set(idx, &geosite) return &geosite, nil case errCodeNotFound: return nil, newError("list ", code, " not found in ", filename) case errFailedToReadBytes, errFailedToReadExpectedLenBytes, errInvalidGeodataFile, errInvalidGeodataVarintLength: newError("failed to decode geoip file: ", filename, ", fallback to the original ReadFile method") geositeBytes, err = os.ReadFile(asset) if err != nil { return nil, err } var geositeList routercommon.GeoSiteList if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { return nil, err } for _, geosite := range geositeList.GetEntry() { if strings.EqualFold(code, geosite.GetCountryCode()) { g.Set(idx, geosite) return geosite, nil } } default: return nil, err } return nil, newError("list ", code, " not found in ", filename) } ================================================ FILE: infra/conf/geodata/memconservative/decode.go ================================================ package memconservative import ( "errors" "io" "strings" "google.golang.org/protobuf/encoding/protowire" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" ) var ( errFailedToReadBytes = errors.New("failed to read bytes") errFailedToReadExpectedLenBytes = errors.New("failed to read expected length of bytes") errInvalidGeodataFile = errors.New("invalid geodata file") errInvalidGeodataVarintLength = errors.New("invalid geodata varint length") errCodeNotFound = errors.New("code not found") ) func emitBytes(f io.ReadSeeker, code string) ([]byte, error) { count := 1 isInner := false tempContainer := make([]byte, 0, 5) var result []byte var advancedN uint64 = 1 var geoDataVarintLength, codeVarintLength, varintLenByteLen uint64 = 0, 0, 0 Loop: for { container := make([]byte, advancedN) bytesRead, err := f.Read(container) if err == io.EOF { return nil, errCodeNotFound } if err != nil { return nil, errFailedToReadBytes } if bytesRead != len(container) { return nil, errFailedToReadExpectedLenBytes } switch count { case 1, 3: // data type ((field_number << 3) | wire_type) if container[0] != 10 { // byte `0A` equals to `10` in decimal return nil, errInvalidGeodataFile } advancedN = 1 count++ case 2, 4: // data length tempContainer = append(tempContainer, container...) if container[0] > 127 { // max one-byte-length byte `7F`(0FFF FFFF) equals to `127` in decimal advancedN = 1 goto Loop } lenVarint, n := protowire.ConsumeVarint(tempContainer) if n < 0 { return nil, errInvalidGeodataVarintLength } tempContainer = nil if !isInner { isInner = true geoDataVarintLength = lenVarint advancedN = 1 } else { isInner = false codeVarintLength = lenVarint varintLenByteLen = uint64(n) advancedN = codeVarintLength } count++ case 5: // data value if strings.EqualFold(string(container), code) { count++ offset := -(1 + int64(varintLenByteLen) + int64(codeVarintLength)) f.Seek(offset, 1) // back to the start of GeoIP or GeoSite varint advancedN = geoDataVarintLength // the number of bytes to be read in next round } else { count = 1 offset := int64(geoDataVarintLength) - int64(codeVarintLength) - int64(varintLenByteLen) - 1 f.Seek(offset, 1) // skip the unmatched GeoIP or GeoSite varint advancedN = 1 // the next round will be the start of another GeoIPList or GeoSiteList } case 6: // matched GeoIP or GeoSite varint result = container break Loop } } return result, nil } func Decode(filename, code string) ([]byte, error) { f, err := filesystem.NewFileSeeker(filename) if err != nil { return nil, newError("failed to open file: ", filename).Base(err) } defer f.Close() geoBytes, err := emitBytes(f, code) if err != nil { return nil, err } return geoBytes, nil } ================================================ FILE: infra/conf/geodata/memconservative/decode_test.go ================================================ package memconservative import ( "bytes" "errors" "io/fs" "os" "path/filepath" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" ) func init() { const ( geoipURL = "https://raw.githubusercontent.com/v2fly/geoip/release/geoip.dat" geositeURL = "https://raw.githubusercontent.com/v2fly/domain-list-community/release/dlc.dat" ) wd, err := os.Getwd() common.Must(err) tempPath := filepath.Join(wd, "..", "..", "..", "..", "testing", "temp") geoipPath := filepath.Join(tempPath, "geoip.dat") geositePath := filepath.Join(tempPath, "geosite.dat") os.Setenv("v2ray.location.asset", tempPath) if _, err := os.Stat(geoipPath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geoipBytes, err := common.FetchHTTPContent(geoipURL) common.Must(err) common.Must(filesystem.WriteFile(geoipPath, geoipBytes)) } if _, err := os.Stat(geositePath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geositeBytes, err := common.FetchHTTPContent(geositeURL) common.Must(err) common.Must(filesystem.WriteFile(geositePath, geositeBytes)) } } func TestDecodeGeoIP(t *testing.T) { filename := platform.GetAssetLocation("geoip.dat") result, err := Decode(filename, "test") if err != nil { t.Error(err) } expected := []byte{10, 4, 84, 69, 83, 84, 18, 8, 10, 4, 127, 0, 0, 0, 16, 8} if !bytes.Equal(result, expected) { t.Errorf("failed to load geoip:test, expected: %v, got: %v", expected, result) } } func TestDecodeGeoSite(t *testing.T) { filename := platform.GetAssetLocation("geosite.dat") result, err := Decode(filename, "test") if err != nil { t.Error(err) } expected := []byte{10, 4, 84, 69, 83, 84, 18, 20, 8, 3, 18, 16, 116, 101, 115, 116, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109} if !bytes.Equal(result, expected) { t.Errorf("failed to load geosite:test, expected: %v, got: %v", expected, result) } } ================================================ FILE: infra/conf/geodata/memconservative/errors.generated.go ================================================ package memconservative import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/geodata/memconservative/memc.go ================================================ package memconservative import ( "runtime" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type memConservativeLoader struct { geoipcache GeoIPCache geositecache GeoSiteCache } func (m *memConservativeLoader) LoadIP(filename, country string) ([]*routercommon.CIDR, error) { defer runtime.GC() geoip, err := m.geoipcache.Unmarshal(filename, country) if err != nil { return nil, newError("failed to decode geodata file: ", filename).Base(err) } return geoip.Cidr, nil } func (m *memConservativeLoader) LoadSite(filename, list string) ([]*routercommon.Domain, error) { defer runtime.GC() geosite, err := m.geositecache.Unmarshal(filename, list) if err != nil { return nil, newError("failed to decode geodata file: ", filename).Base(err) } return geosite.Domain, nil } func newMemConservativeLoader() geodata.LoaderImplementation { return &memConservativeLoader{make(map[string]*routercommon.GeoIP), make(map[string]*routercommon.GeoSite)} } func init() { geodata.RegisterGeoDataLoaderImplementationCreator("memconservative", newMemConservativeLoader) } ================================================ FILE: infra/conf/geodata/standard/errors.generated.go ================================================ package standard import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/geodata/standard/standard.go ================================================ package standard import ( "strings" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func loadIP(filename, country string) ([]*routercommon.CIDR, error) { geoipBytes, err := filesystem.ReadAsset(filename) if err != nil { return nil, newError("failed to open file: ", filename).Base(err) } var geoipList routercommon.GeoIPList if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { return nil, err } for _, geoip := range geoipList.Entry { if strings.EqualFold(geoip.CountryCode, country) { return geoip.Cidr, nil } } return nil, newError("country not found in ", filename, ": ", country) } func loadSite(filename, list string) ([]*routercommon.Domain, error) { geositeBytes, err := filesystem.ReadAsset(filename) if err != nil { return nil, newError("failed to open file: ", filename).Base(err) } var geositeList routercommon.GeoSiteList if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { return nil, err } for _, site := range geositeList.Entry { if strings.EqualFold(site.CountryCode, list) { return site.Domain, nil } } return nil, newError("list not found in ", filename, ": ", list) } type standardLoader struct{} func (d standardLoader) LoadSite(filename, list string) ([]*routercommon.Domain, error) { return loadSite(filename, list) } func (d standardLoader) LoadIP(filename, country string) ([]*routercommon.CIDR, error) { return loadIP(filename, country) } func init() { geodata.RegisterGeoDataLoaderImplementationCreator("standard", func() geodata.LoaderImplementation { return standardLoader{} }) } ================================================ FILE: infra/conf/json/json_test.go ================================================ package json_test import ( "encoding/json" "reflect" "testing" ) func assertResult(t *testing.T, value map[string]interface{}, expected string) { e := make(map[string]interface{}) err := json.Unmarshal([]byte(expected), &e) if err != nil { t.Error(err) } if !reflect.DeepEqual(value, e) { bs, _ := json.Marshal(value) t.Fatalf("expected:\n%s\n\nactual:\n%s", expected, string(bs)) } } ================================================ FILE: infra/conf/json/reader.go ================================================ package json import ( "io" "github.com/v2fly/v2ray-core/v5/common/buf" ) // State is the internal state of parser. type State byte const ( StateContent State = iota StateEscape StateDoubleQuote StateDoubleQuoteEscape StateSingleQuote StateSingleQuoteEscape StateComment StateSlash StateMultilineComment StateMultilineCommentStar ) // Reader is a reader for filtering comments. // It supports Java style single and multi line comment syntax, and Python style single line comment syntax. type Reader struct { io.Reader state State pending []byte br *buf.BufferedReader } // Read implements io.Reader.Read(). func (v *Reader) Read(b []byte) (int, error) { if v.br == nil { v.br = &buf.BufferedReader{Reader: buf.NewReader(v.Reader)} } p := b[:0] for len(p) < len(b) { if len(v.pending) > 0 { max := len(b) - len(p) if max > len(v.pending) { max = len(v.pending) } p = append(p, v.pending[:max]...) v.pending = v.pending[max:] continue } x, err := v.br.ReadByte() if err != nil { if len(p) == 0 { return 0, err } return len(p), nil } switch v.state { case StateContent: switch x { case '"': v.state = StateDoubleQuote p = append(p, x) case '\'': v.state = StateSingleQuote p = append(p, x) case '\\': v.state = StateEscape p = append(p, x) case '#': v.state = StateComment case '/': v.state = StateSlash default: p = append(p, x) } case StateEscape: p = append(p, x) v.state = StateContent case StateDoubleQuote: switch x { case '"': v.state = StateContent p = append(p, x) case '\\': v.state = StateDoubleQuoteEscape p = append(p, x) default: p = append(p, x) } case StateDoubleQuoteEscape: p = append(p, x) v.state = StateDoubleQuote case StateSingleQuote: switch x { case '\'': v.state = StateContent p = append(p, x) case '\\': v.state = StateSingleQuoteEscape p = append(p, x) default: p = append(p, x) } case StateSingleQuoteEscape: p = append(p, x) v.state = StateSingleQuote case StateComment: if x == '\n' { v.state = StateContent p = append(p, x) } case StateSlash: switch x { case '/': v.state = StateComment case '*': v.state = StateMultilineComment default: v.state = StateContent v.pending = append(v.pending, x) p = append(p, '/') } case StateMultilineComment: switch x { case '*': v.state = StateMultilineCommentStar case '\n': p = append(p, x) } case StateMultilineCommentStar: switch x { case '/': v.state = StateContent case '*': // Stay case '\n': p = append(p, x) default: v.state = StateMultilineComment } default: panic("Unknown state.") } } return len(p), nil } ================================================ FILE: infra/conf/json/reader_test.go ================================================ package json_test import ( "bytes" "io" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/infra/conf/json" ) func TestReader(t *testing.T) { data := []struct { input string output string }{ { ` content #comment 1 #comment 2 content 2`, ` content content 2`, }, {`content`, `content`}, {" ", " "}, {`con/*abcd*/tent`, "content"}, {` text // adlkhdf /* //comment adfkj text 2*/`, ` text text 2*`}, {`"//"content`, `"//"content`}, {`abcd'//'abcd`, `abcd'//'abcd`}, {`"\""`, `"\""`}, {`\"/*abcd*/\"`, `\"\"`}, } for _, testCase := range data { reader := &Reader{ Reader: bytes.NewReader([]byte(testCase.input)), } actual := make([]byte, 1024) n, err := reader.Read(actual) common.Must(err) if r := cmp.Diff(string(actual[:n]), testCase.output); r != "" { t.Error(r) } } } func TestReader1(t *testing.T) { type dataStruct struct { input string output string } bufLen := 1 data := []dataStruct{ {"loooooooooooooooooooooooooooooooooooooooog", "loooooooooooooooooooooooooooooooooooooooog"}, {`{"t": "\/testlooooooooooooooooooooooooooooong"}`, `{"t": "\/testlooooooooooooooooooooooooooooong"}`}, {`{"t": "\/test"}`, `{"t": "\/test"}`}, {`"\// fake comment"`, `"\// fake comment"`}, {`"\/\/\/\/\/"`, `"\/\/\/\/\/"`}, {`/test/test`, `/test/test`}, } for _, testCase := range data { reader := &Reader{ Reader: bytes.NewReader([]byte(testCase.input)), } target := make([]byte, 0) buf := make([]byte, bufLen) var n int var err error for n, err = reader.Read(buf); err == nil; n, err = reader.Read(buf) { if n > len(buf) { t.Error("n: ", n) } target = append(target, buf[:n]...) buf = make([]byte, bufLen) } if err != nil && err != io.EOF { t.Error("error: ", err) } if string(target) != testCase.output { t.Error("got ", string(target), " want ", testCase.output) } } } ================================================ FILE: infra/conf/json/toml.go ================================================ package json import ( "bytes" "encoding/json" "io" "github.com/pelletier/go-toml" ) // FromTOML convert toml to json func FromTOML(v []byte) ([]byte, error) { tomlReader := bytes.NewReader(v) jsonStr, err := jsonFromTomlReader(tomlReader) if err != nil { return nil, err } return []byte(jsonStr), nil } func jsonFromTomlReader(r io.Reader) (string, error) { tree, err := toml.LoadReader(r) if err != nil { return "", err } return mapToJSON(tree) } func mapToJSON(tree *toml.Tree) (string, error) { treeMap := tree.ToMap() bytes, err := json.MarshalIndent(treeMap, "", " ") if err != nil { return "", err } return string(bytes), nil } ================================================ FILE: infra/conf/json/toml_test.go ================================================ package json_test import ( "encoding/json" "testing" . "github.com/v2fly/v2ray-core/v5/infra/conf/json" ) func TestTOMLToJSON_V2Style(t *testing.T) { input := ` [log] loglevel = 'debug' [[inbounds]] port = 10800 listen = '127.0.0.1' protocol = 'socks' [inbounds.settings] udp = true [[outbounds]] protocol = 'vmess' [[outbounds.settings.vnext]] port = 443 address = 'example.com' [[outbounds.settings.vnext.users]] id = '98a15fa6-2eb1-edd5-50ea-cfc428aaab78' [outbounds.streamSettings] network = 'tcp' security = 'tls' ` expected := ` { "log": { "loglevel": "debug" }, "inbounds": [{ "port": 10800, "listen": "127.0.0.1", "protocol": "socks", "settings": { "udp": true } }], "outbounds": [{ "protocol": "vmess", "settings": { "vnext": [{ "port": 443, "address": "example.com", "users": [{ "id": "98a15fa6-2eb1-edd5-50ea-cfc428aaab78" }] }] }, "streamSettings": { "network": "tcp", "security": "tls" } }] } ` bs, err := FromTOML([]byte(input)) if err != nil { t.Error(err) } m := make(map[string]interface{}) json.Unmarshal(bs, &m) assertResult(t, m, expected) } func TestTOMLToJSON_ValueTypes(t *testing.T) { input := ` boolean = [ true, false, true, false ] float = [ 3.14, 685_230.15 ] int = [ 123, 685_230 ] string = [ "哈哈", "Hello world", "newline newline2" ] date = [ "2018-02-17" ] datetime = [ "2018-02-17T15:02:31+08:00" ] 1 = 0 true = true str = "hello" [null] nodeName = "node" ` expected := ` { "boolean": [true, false, true, false], "float": [3.14, 685230.15], "int": [123, 685230], "null": { "nodeName": "node" }, "string": ["哈哈", "Hello world", "newline newline2"], "date": ["2018-02-17"], "datetime": ["2018-02-17T15:02:31+08:00"], "1": 0, "true": true, "str": "hello" } ` bs, err := FromTOML([]byte(input)) if err != nil { t.Error(err) } m := make(map[string]interface{}) json.Unmarshal(bs, &m) assertResult(t, m, expected) } ================================================ FILE: infra/conf/json/yaml.go ================================================ package json import ( "encoding/json" "fmt" "gopkg.in/yaml.v3" ) // FromYAML convert yaml to json func FromYAML(v []byte) ([]byte, error) { m1 := make(map[interface{}]interface{}) err := yaml.Unmarshal(v, &m1) if err != nil { return nil, err } m2 := convert(m1) j, err := json.Marshal(m2) if err != nil { return nil, err } return j, nil } func convert(m map[interface{}]interface{}) map[string]interface{} { res := map[string]interface{}{} for k, v := range m { var value interface{} switch v2 := v.(type) { case map[interface{}]interface{}: value = convert(v2) case []interface{}: for i, el := range v2 { if m, ok := el.(map[interface{}]interface{}); ok { v2[i] = convert(m) } } value = v2 default: value = v } key := "null" if k != nil { key = fmt.Sprint(k) } res[key] = value } return res } ================================================ FILE: infra/conf/json/yaml_test.go ================================================ package json_test import ( "encoding/json" "testing" . "github.com/v2fly/v2ray-core/v5/infra/conf/json" ) func TestYMLToJSON_V2Style(t *testing.T) { input := ` log: loglevel: debug inbounds: - port: 10800 listen: 127.0.0.1 protocol: socks settings: udp: true outbounds: - protocol: vmess settings: vnext: - address: example.com port: 443 users: - id: '98a15fa6-2eb1-edd5-50ea-cfc428aaab78' streamSettings: network: tcp security: tls ` expected := ` { "log": { "loglevel": "debug" }, "inbounds": [{ "port": 10800, "listen": "127.0.0.1", "protocol": "socks", "settings": { "udp": true } }], "outbounds": [{ "protocol": "vmess", "settings": { "vnext": [{ "port": 443, "address": "example.com", "users": [{ "id": "98a15fa6-2eb1-edd5-50ea-cfc428aaab78" }] }] }, "streamSettings": { "network": "tcp", "security": "tls" } }] } ` bs, err := FromYAML([]byte(input)) if err != nil { t.Error(err) } m := make(map[string]interface{}) json.Unmarshal(bs, &m) assertResult(t, m, expected) } func TestYMLToJSON_ValueTypes(t *testing.T) { input := ` boolean: - TRUE - FALSE - true - false float: - 3.14 - 6.8523015e+5 int: - 123 - 0b1010_0111_0100_1010_1110 null: nodeName: 'node' parent: ~ # ~ for null string: - 哈哈 - 'Hello world' - newline newline2 # multi-line string date: - 2018-02-17 # yyyy-MM-dd datetime: - 2018-02-17T15:02:31+08:00 # ISO 8601 time mixed: - true - false - 1 - 0 - null - hello # arbitrary keys 1: 0 true: false TRUE: TRUE "str": "hello" ` expected := ` { "boolean": [true, false, true, false], "float": [3.14, 685230.15], "int": [123, 685230], "null": { "nodeName": "node", "parent": null }, "string": ["哈哈", "Hello world", "newline newline2"], "date": ["2018-02-17T00:00:00Z"], "datetime": ["2018-02-17T15:02:31+08:00"], "mixed": [true,false,1,0,null,"hello"], "1": 0, "true": true, "str": "hello" } ` bs, err := FromYAML([]byte(input)) if err != nil { t.Error(err) } m := make(map[string]interface{}) json.Unmarshal(bs, &m) assertResult(t, m, expected) } ================================================ FILE: infra/conf/jsonpb/errors.generated.go ================================================ package jsonpb import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/jsonpb/jsonpb.go ================================================ package jsonpb import ( "bytes" "io" "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/cmdarg" "github.com/v2fly/v2ray-core/v5/common/serial" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func loadJSONPB(data io.Reader) (*core.Config, error) { coreconf := &core.Config{} jsonpbloader := &jsonpb.Unmarshaler{AnyResolver: serial.GetResolver()} err := jsonpbloader.Unmarshal(data, coreconf) if err != nil { return nil, err } return coreconf, nil } func dumpJSONPb(config proto.Message, w io.Writer) error { jsonpbdumper := &jsonpb.Marshaler{AnyResolver: serial.GetResolver()} err := jsonpbdumper.Marshal(w, config) if err != nil { return err } return nil } func DumpJSONPb(config proto.Message, w io.Writer) error { return dumpJSONPb(config, w) } const FormatProtobufJSONPB = "jsonpb" func init() { common.Must(core.RegisterConfigLoader(&core.ConfigFormat{ Name: []string{FormatProtobufJSONPB}, Extension: []string{".pb.json", ".pbjson"}, Loader: func(input interface{}) (*core.Config, error) { switch v := input.(type) { case string: r, err := cmdarg.LoadArg(v) if err != nil { return nil, err } data, err := buf.ReadAllToBytes(r) if err != nil { return nil, err } return loadJSONPB(bytes.NewReader(data)) case []byte: return loadJSONPB(bytes.NewReader(v)) case io.Reader: data, err := buf.ReadAllToBytes(v) if err != nil { return nil, err } return loadJSONPB(bytes.NewReader(data)) default: return nil, newError("unknown type") } }, })) } ================================================ FILE: infra/conf/merge/errors.generated.go ================================================ package merge import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/merge/map.go ================================================ // Copyright 2020 Jebbs. All rights reserved. // Use of this source code is governed by MIT // license that can be found in the LICENSE file. package merge import ( "fmt" "reflect" ) // mergeMaps merges source map into target // it supports only map[string]interface{} type for any children of the map tree func mergeMaps(target map[string]interface{}, source map[string]interface{}) (err error) { for key, value := range source { target[key], err = mergeField(target[key], value) if err != nil { return } } return } func mergeField(target interface{}, source interface{}) (interface{}, error) { if source == nil { return target, nil } if target == nil { return source, nil } if reflect.TypeOf(source) != reflect.TypeOf(target) { return nil, fmt.Errorf("type mismatch, expect %T, incoming %T", target, source) } if slice, ok := source.([]interface{}); ok { tslice, _ := target.([]interface{}) tslice = append(tslice, slice...) return tslice, nil } else if smap, ok := source.(map[string]interface{}); ok { tmap, _ := target.(map[string]interface{}) err := mergeMaps(tmap, smap) return tmap, err } return source, nil } ================================================ FILE: infra/conf/merge/merge.go ================================================ // Copyright 2020 Jebbs. All rights reserved. // Use of this source code is governed by MIT // license that can be found in the LICENSE file. /* Package merge provides the capability to merge multiple JSON files or contents into one output. Merge Rules: - Simple values (string, number, boolean) are overwritten, others are merged - Elements with same "tag" (or "_tag") in an array will be merged - Add "_priority" property to array elements will help sort the */ package merge import ( "bytes" "encoding/json" "github.com/v2fly/v2ray-core/v5/infra/conf/serial" ) // JSONs merges multiple json contents into one json. func JSONs(args [][]byte) ([]byte, error) { m := make(map[string]interface{}) for _, arg := range args { if _, err := ToMap(arg, m); err != nil { return nil, err } } return FromMap(m) } // ToMap merges json content to target map and returns it func ToMap(content []byte, target map[string]interface{}) (map[string]interface{}, error) { if target == nil { target = make(map[string]interface{}) } r := bytes.NewReader(content) n := make(map[string]interface{}) err := serial.DecodeJSON(r, &n) if err != nil { return nil, err } if err = mergeMaps(target, n); err != nil { return nil, err } return target, nil } // FromMap apply merge rules to map and convert it to json func FromMap(target map[string]interface{}) ([]byte, error) { if target == nil { target = make(map[string]interface{}) } err := ApplyRules(target) if err != nil { return nil, err } return json.Marshal(target) } ================================================ FILE: infra/conf/merge/merge_test.go ================================================ // Copyright 2020 Jebbs. All rights reserved. // Use of this source code is governed by MIT // license that can be found in the LICENSE file. package merge_test import ( "bytes" "reflect" "strings" "testing" "github.com/v2fly/v2ray-core/v5/infra/conf/merge" "github.com/v2fly/v2ray-core/v5/infra/conf/serial" ) func TestMergeV2Style(t *testing.T) { json1 := ` { "log": {"access": "some_value", "loglevel": "debug"}, "inbounds": [{"tag": "in-1"}], "outbounds": [{"_priority": 100, "tag": "out-1"}], "routing": {"rules": [ {"_tag":"default_route","inboundTag":["in-1"],"outboundTag":"out-1"} ]} } ` json2 := ` { "log": {"loglevel": "error"}, "inbounds": [{"tag": "in-2"}], "outbounds": [{"_priority": -100, "tag": "out-2"}], "routing": {"rules": [ {"inboundTag":["in-2"],"outboundTag":"out-2"}, {"_tag":"default_route","inboundTag":["in-1.1"],"outboundTag":"out-1.1"} ]} } ` expected := ` { "log": {"access": "some_value", "loglevel": "error"}, "inbounds": [{"tag": "in-1"},{"tag": "in-2"}], "outbounds": [ {"tag": "out-2"}, {"tag": "out-1"} ], "routing": {"rules": [ {"inboundTag":["in-1","in-1.1"],"outboundTag":"out-1.1"}, {"inboundTag":["in-2"],"outboundTag":"out-2"} ]} } ` m, err := merge.JSONs([][]byte{[]byte(json1), []byte(json2)}) if err != nil { t.Error(err) } assertResult(t, m, expected) } func TestMergeTag(t *testing.T) { json1 := ` { "routing": { "rules": [{ "tag":"1", "inboundTag": ["in-1"], "outboundTag": "out-1" }] } } ` json2 := ` { "routing": { "rules": [{ "_tag":"1", "inboundTag": ["in-2"], "outboundTag": "out-2" }] } } ` expected := ` { "routing": { "rules": [{ "tag":"1", "inboundTag": ["in-1", "in-2"], "outboundTag": "out-2" }] } } ` m, err := merge.JSONs([][]byte{[]byte(json1), []byte(json2)}) if err != nil { t.Error(err) } assertResult(t, m, expected) } func TestMergeTagValueTypes(t *testing.T) { json1 := ` { "array_1": [{ "_tag":"1", "array_2": [{ "_tag":"2", "array_3.1": ["string",true,false], "array_3.2": [1,2,3], "number_1": 1, "number_2": 1, "bool_1": true, "bool_2": true }] }] } ` json2 := ` { "array_1": [{ "_tag":"1", "array_2": [{ "_tag":"2", "array_3.1": [0,1,null], "array_3.2": null, "number_1": 0, "number_2": 1, "bool_1": true, "bool_2": false, "null_1": null }] }] } ` expected := ` { "array_1": [{ "array_2": [{ "array_3.1": ["string",true,false,0,1,null], "array_3.2": [1,2,3], "number_1": 0, "number_2": 1, "bool_1": true, "bool_2": false, "null_1": null }] }] } ` m, err := merge.JSONs([][]byte{[]byte(json1), []byte(json2)}) if err != nil { t.Error(err) } assertResult(t, m, expected) } func TestMergeTagDeep(t *testing.T) { json1 := ` { "array_1": [{ "_tag":"1", "array_2": [{ "_tag":"2", "array_3": [true,false,"string"] }] }] } ` json2 := ` { "array_1": [{ "_tag":"1", "array_2": [{ "_tag":"2", "_priority":-100, "array_3": [0,1,null] }] }] } ` expected := ` { "array_1": [{ "array_2": [{ "array_3": [0,1,null,true,false,"string"] }] }] } ` m, err := merge.JSONs([][]byte{[]byte(json1), []byte(json2)}) if err != nil { t.Error(err) } assertResult(t, m, expected) } func TestNoMergeDnsServers(t *testing.T) { json1 := ` { "dns": { "queryStrategy": "UseIPv4", "fallbackStrategy": "disabled-if-any-match", "domainMatcher": "mph", "servers": [ { "address": "aaa.bbb.ccc.ddd", "port": 53, "domains": [ "geosite:cn" ], "tag": "dns-domestic" }, { "address": "114.114.114.114", "port": 53, "domains": [ "geosite:cn" ], "tag": "dns-domestic" }, { "address": "https://1.1.1.1/dns-query", "tag": "dns-international" } ] }, "routing": { "domainStrategy": "IPIfNonMatch", "domainMatcher": "mph", "rules": [ { "type": "field", "inboundTag": "dns-domestic", "outboundTag": "direct" }, { "type": "field", "inboundTag": "dns-international", "outboundTag": "proxy" } ] } } ` expected := ` { "dns": { "queryStrategy": "UseIPv4", "fallbackStrategy": "disabled-if-any-match", "domainMatcher": "mph", "servers": [ { "address": "aaa.bbb.ccc.ddd", "port": 53, "domains": [ "geosite:cn" ], "tag": "dns-domestic" }, { "address": "114.114.114.114", "port": 53, "domains": [ "geosite:cn" ], "tag": "dns-domestic" }, { "address": "https://1.1.1.1/dns-query", "tag": "dns-international" } ] }, "routing": { "domainStrategy": "IPIfNonMatch", "domainMatcher": "mph", "rules": [ { "type": "field", "inboundTag": "dns-domestic", "outboundTag": "direct" }, { "type": "field", "inboundTag": "dns-international", "outboundTag": "proxy" } ] } } ` m, err := merge.JSONs([][]byte{[]byte(json1)}) if err != nil { t.Error(err) } assertResult(t, m, expected) } func assertResult(t *testing.T, value []byte, expected string) { v := make(map[string]interface{}) err := serial.DecodeJSON(bytes.NewReader(value), &v) if err != nil { t.Error(err) } e := make(map[string]interface{}) err = serial.DecodeJSON(strings.NewReader(expected), &e) if err != nil { t.Error(err) } if !reflect.DeepEqual(v, e) { t.Fatalf("expected:\n%s\n\nactual:\n%s", expected, string(value)) } } ================================================ FILE: infra/conf/merge/priority.go ================================================ // Copyright 2020 Jebbs. All rights reserved. // Use of this source code is governed by MIT // license that can be found in the LICENSE file. package merge import "sort" func getPriority(v interface{}) float64 { var m map[string]interface{} var ok bool if m, ok = v.(map[string]interface{}); !ok { return 0 } if i, ok := m[priorityKey]; ok { if p, ok := i.(float64); ok { return p } } return 0 } // sortByPriority sort slice by priority fields of their elements func sortByPriority(slice []interface{}) { sort.Slice( slice, func(i, j int) bool { return getPriority(slice[i]) < getPriority(slice[j]) }, ) } ================================================ FILE: infra/conf/merge/rules.go ================================================ // Copyright 2020 Jebbs. All rights reserved. // Use of this source code is governed by MIT // license that can be found in the LICENSE file. package merge import "fmt" const ( priorityKey string = "_priority" tagKey string = "_tag" ) // ApplyRules applies merge rules according to _tag, _priority fields, and remove them func ApplyRules(m map[string]interface{}) error { err := sortMergeSlices(m, "") if err != nil { return err } removeHelperFields(m) return nil } // sortMergeSlices enumerates all slices in a map, to sort by priority and merge by tag func sortMergeSlices(target map[string]interface{}, path string) error { for key, value := range target { if slice, ok := value.([]interface{}); ok { sortByPriority(slice) s, err := mergeSameTag(slice, fmt.Sprintf("%s.%s", path, key)) if err != nil { return err } target[key] = s for _, item := range s { if m, ok := item.(map[string]interface{}); ok { sortMergeSlices(m, fmt.Sprintf("%s.%s[]", path, key)) } } } else if field, ok := value.(map[string]interface{}); ok { sortMergeSlices(field, fmt.Sprintf("%s.%s", path, key)) } } return nil } func removeHelperFields(target map[string]interface{}) { for key, value := range target { if key == priorityKey || key == tagKey { delete(target, key) } else if slice, ok := value.([]interface{}); ok { for _, e := range slice { if el, ok := e.(map[string]interface{}); ok { removeHelperFields(el) } } } else if field, ok := value.(map[string]interface{}); ok { removeHelperFields(field) } } } ================================================ FILE: infra/conf/merge/tag.go ================================================ // Copyright 2020 Jebbs. All rights reserved. // Use of this source code is governed by MIT // license that can be found in the LICENSE file. package merge import ( "strings" ) func getTag(v map[string]interface{}, tagKeyOnly bool) string { if !tagKeyOnly { if field, ok := v["tag"]; ok { if t, ok := field.(string); ok { return t } } } if field, ok := v[tagKey]; ok { if t, ok := field.(string); ok { return t } } return "" } func mergeSameTag(s []interface{}, path string) ([]interface{}, error) { // from: [a,"",b,"",a,"",b,""] // to: [a,"",b,"",merged,"",merged,""] merged := &struct{}{} tagKeyOnly := false // See https://github.com/v2fly/v2ray-core/issues/2981 if strings.HasPrefix(path, ".dns.servers") { tagKeyOnly = true } for i, item1 := range s { map1, ok := item1.(map[string]interface{}) if !ok { continue } tag1 := getTag(map1, tagKeyOnly) if tag1 == "" { continue } for j := i + 1; j < len(s); j++ { map2, ok := s[j].(map[string]interface{}) if !ok { continue } tag2 := getTag(map2, tagKeyOnly) if tag1 == tag2 { s[j] = merged err := mergeMaps(map1, map2) if err != nil { return nil, err } } } } // remove merged ns := make([]interface{}, 0) for _, item := range s { if item == merged { continue } ns = append(ns, item) } return ns, nil } ================================================ FILE: infra/conf/mergers/errors.generated.go ================================================ package mergers import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/mergers/extensions.go ================================================ package mergers import "strings" // GetExtensions get extensions of given format func GetExtensions(formatName string) ([]string, error) { lowerName := strings.ToLower(formatName) if lowerName == "auto" { return GetAllExtensions(), nil } f, found := mergersByName[lowerName] if !found { return nil, newError(formatName+" not found", formatName).AtWarning() } return f.Extensions, nil } // GetAllExtensions get all extensions supported func GetAllExtensions() []string { extensions := make([]string, 0) for _, f := range mergersByName { extensions = append(extensions, f.Extensions...) } return extensions } ================================================ FILE: infra/conf/mergers/merge.go ================================================ package mergers import ( "io" "path/filepath" "strings" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/cmdarg" ) // MergeAs load input and merge as specified format into m func MergeAs(formatName string, input interface{}, m map[string]interface{}) error { f, found := mergersByName[formatName] if !found { return newError("format merger not found for: ", formatName) } return f.Merge(input, m) } // Merge loads inputs and merges them into m // it detects extension for merger selecting, or try all mergers // if no extension found func Merge(input interface{}, m map[string]interface{}) error { if input == nil { return nil } switch v := input.(type) { case string: err := mergeSingleFile(v, m) if err != nil { return err } case []string: for _, file := range v { err := mergeSingleFile(file, m) if err != nil { return err } } case cmdarg.Arg: for _, file := range v { err := mergeSingleFile(file, m) if err != nil { return err } } case []byte: err := mergeSingleFile(v, m) if err != nil { return err } case io.Reader: // read to []byte incase it tries different mergers bs, err := io.ReadAll(v) if err != nil { return err } err = mergeSingleFile(bs, m) if err != nil { return err } default: return newError("unknown merge input type") } return nil } func mergeSingleFile(input interface{}, m map[string]interface{}) error { if file, ok := input.(string); ok { ext := getExtension(file) if ext != "" { lext := strings.ToLower(ext) f, found := mergersByExt[lext] if !found { return newError("unmergeable format extension: ", ext) } return f.Merge(file, m) } } // no extension, try all mergers for _, f := range mergersByName { if f.Name == core.FormatAuto { continue } err := f.Merge(input, m) if err == nil { return nil } } return newError("tried all mergers but failed for: ", input).AtWarning() } func getExtension(filename string) string { ext := filepath.Ext(filename) return strings.ToLower(ext) } ================================================ FILE: infra/conf/mergers/merger_base.go ================================================ package mergers import ( "fmt" "io" "github.com/v2fly/v2ray-core/v5/common/cmdarg" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/infra/conf/merge" ) type jsonConverter func(v []byte) ([]byte, error) // makeMerger makes a merger who merge the format by converting it to JSON func makeMerger(name string, extensions []string, converter jsonConverter) *Merger { return &Merger{ Name: name, Extensions: extensions, Merge: makeToJSONMergeFunc(converter), } } // makeToJSONMergeFunc makes a merge func who merge the format by converting it to JSON func makeToJSONMergeFunc(converter func(v []byte) ([]byte, error)) MergeFunc { return func(input interface{}, target map[string]interface{}) error { if input == nil { return nil } if target == nil { return errors.New("merge target is nil") } switch v := input.(type) { case string: err := loadFile(v, target, converter) if err != nil { return err } case []string: err := loadFiles(v, target, converter) if err != nil { return err } case cmdarg.Arg: err := loadFiles(v, target, converter) if err != nil { return err } case []byte: err := loadBytes(v, target, converter) if err != nil { return err } case io.Reader: err := loadReader(v, target, converter) if err != nil { return err } default: return newError("unknown merge input type") } return nil } } func loadFiles(files []string, target map[string]interface{}, converter func(v []byte) ([]byte, error)) error { for _, file := range files { err := loadFile(file, target, converter) if err != nil { return err } } return nil } func loadFile(file string, target map[string]interface{}, converter func(v []byte) ([]byte, error)) error { bs, err := cmdarg.LoadArgToBytes(file) if err != nil { return fmt.Errorf("fail to load %s: %s", file, err) } if converter != nil { bs, err = converter(bs) if err != nil { return fmt.Errorf("error convert to json '%s': %s", file, err) } } _, err = merge.ToMap(bs, target) return err } func loadReader(reader io.Reader, target map[string]interface{}, converter func(v []byte) ([]byte, error)) error { bs, err := io.ReadAll(reader) if err != nil { return err } return loadBytes(bs, target, converter) } func loadBytes(bs []byte, target map[string]interface{}, converter func(v []byte) ([]byte, error)) error { var err error if converter != nil { bs, err = converter(bs) if err != nil { return fmt.Errorf("fail to convert to json: %s", err) } } _, err = merge.ToMap(bs, target) return err } ================================================ FILE: infra/conf/mergers/mergers.go ================================================ package mergers //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "strings" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/infra/conf/json" ) func init() { common.Must(RegisterMerger(makeMerger( core.FormatJSON, []string{".json", ".jsonc"}, nil, ))) common.Must(RegisterMerger(makeMerger( core.FormatTOML, []string{".toml"}, json.FromTOML, ))) common.Must(RegisterMerger(makeMerger( core.FormatYAML, []string{".yml", ".yaml"}, json.FromYAML, ))) common.Must(RegisterMerger( &Merger{ Name: core.FormatAuto, Extensions: nil, Merge: Merge, }), ) } // Merger is a configurable format merger for V2Ray config files. type Merger struct { Name string Extensions []string Merge MergeFunc } // MergeFunc is a utility to merge V2Ray config from external source into a map and returns it. type MergeFunc func(input interface{}, m map[string]interface{}) error var ( mergersByName = make(map[string]*Merger) mergersByExt = make(map[string]*Merger) ) // RegisterMerger add a new Merger. func RegisterMerger(format *Merger) error { if _, found := mergersByName[format.Name]; found { return newError(format.Name, " already registered.") } mergersByName[format.Name] = format for _, ext := range format.Extensions { lext := strings.ToLower(ext) if f, found := mergersByExt[lext]; found { return newError(ext, " already registered to ", f.Name) } mergersByExt[lext] = format } return nil } ================================================ FILE: infra/conf/mergers/names.go ================================================ package mergers // GetAllNames get names of all formats func GetAllNames() []string { names := make([]string, 0) for _, f := range mergersByName { names = append(names, f.Name) } return names } ================================================ FILE: infra/conf/rule/errors.generated.go ================================================ package rule import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/rule/rule.go ================================================ package rule import ( "context" "encoding/json" "strconv" "strings" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func parseDomainRule(ctx context.Context, domain string) ([]*routercommon.Domain, error) { cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx) geoLoader := cfgEnv.GetGeoLoader() if strings.HasPrefix(domain, "geosite:") { list := domain[8:] if len(list) == 0 { return nil, newError("empty listname in rule: ", domain) } domains, err := geoLoader.LoadGeoSite(list) if err != nil { return nil, newError("failed to load geosite: ", list).Base(err) } return domains, nil } isExtDatFile := 0 { const prefix = "ext:" if strings.HasPrefix(domain, prefix) { isExtDatFile = len(prefix) } const prefixQualified = "ext-domain:" if strings.HasPrefix(domain, prefixQualified) { isExtDatFile = len(prefixQualified) } } if isExtDatFile != 0 { kv := strings.Split(domain[isExtDatFile:], ":") if len(kv) != 2 { return nil, newError("invalid external resource: ", domain) } filename := kv[0] list := kv[1] domains, err := geoLoader.LoadGeoSiteWithAttr(filename, list) if err != nil { return nil, newError("failed to load external geosite: ", list, " from ", filename).Base(err) } return domains, nil } domainRule := new(routercommon.Domain) switch { case strings.HasPrefix(domain, "regexp:"): regexpVal := domain[7:] if len(regexpVal) == 0 { return nil, newError("empty regexp type of rule: ", domain) } domainRule.Type = routercommon.Domain_Regex domainRule.Value = regexpVal case strings.HasPrefix(domain, "domain:"): domainName := domain[7:] if len(domainName) == 0 { return nil, newError("empty domain type of rule: ", domain) } domainRule.Type = routercommon.Domain_RootDomain domainRule.Value = domainName case strings.HasPrefix(domain, "full:"): fullVal := domain[5:] if len(fullVal) == 0 { return nil, newError("empty full domain type of rule: ", domain) } domainRule.Type = routercommon.Domain_Full domainRule.Value = fullVal case strings.HasPrefix(domain, "keyword:"): keywordVal := domain[8:] if len(keywordVal) == 0 { return nil, newError("empty keyword type of rule: ", domain) } domainRule.Type = routercommon.Domain_Plain domainRule.Value = keywordVal case strings.HasPrefix(domain, "dotless:"): domainRule.Type = routercommon.Domain_Regex switch substr := domain[8:]; { case substr == "": domainRule.Value = "^[^.]*$" case !strings.Contains(substr, "."): domainRule.Value = "^[^.]*" + substr + "[^.]*$" default: return nil, newError("substr in dotless rule should not contain a dot: ", substr) } default: domainRule.Type = routercommon.Domain_Plain domainRule.Value = domain } return []*routercommon.Domain{domainRule}, nil } func toCidrList(ctx context.Context, ips cfgcommon.StringList) ([]*routercommon.GeoIP, error) { cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx) geoLoader := cfgEnv.GetGeoLoader() var geoipList []*routercommon.GeoIP var customCidrs []*routercommon.CIDR for _, ip := range ips { if strings.HasPrefix(ip, "geoip:") { country := ip[6:] isReverseMatch := false if strings.HasPrefix(ip, "geoip:!") { country = ip[7:] isReverseMatch = true } if len(country) == 0 { return nil, newError("empty country name in rule") } geoip, err := geoLoader.LoadGeoIP(country) if err != nil { return nil, newError("failed to load geoip: ", country).Base(err) } geoipList = append(geoipList, &routercommon.GeoIP{ CountryCode: strings.ToUpper(country), Cidr: geoip, InverseMatch: isReverseMatch, }) continue } isExtDatFile := 0 { const prefix = "ext:" if strings.HasPrefix(ip, prefix) { isExtDatFile = len(prefix) } const prefixQualified = "ext-ip:" if strings.HasPrefix(ip, prefixQualified) { isExtDatFile = len(prefixQualified) } } if isExtDatFile != 0 { kv := strings.Split(ip[isExtDatFile:], ":") if len(kv) != 2 { return nil, newError("invalid external resource: ", ip) } filename := kv[0] country := kv[1] if len(filename) == 0 || len(country) == 0 { return nil, newError("empty filename or empty country in rule") } isInverseMatch := false if strings.HasPrefix(country, "!") { country = country[1:] isInverseMatch = true } geoip, err := geoLoader.LoadIP(filename, country) if err != nil { return nil, newError("failed to load geoip: ", country, " from ", filename).Base(err) } geoipList = append(geoipList, &routercommon.GeoIP{ CountryCode: strings.ToUpper(filename + "_" + country), Cidr: geoip, InverseMatch: isInverseMatch, }) continue } ipRule, err := ParseIP(ip) if err != nil { return nil, newError("invalid IP: ", ip).Base(err) } customCidrs = append(customCidrs, ipRule) } if len(customCidrs) > 0 { geoipList = append(geoipList, &routercommon.GeoIP{ Cidr: customCidrs, }) } return geoipList, nil } func parseFieldRule(ctx context.Context, msg json.RawMessage) (*router.RoutingRule, error) { type RawFieldRule struct { RouterRule Domain *cfgcommon.StringList `json:"domain"` Domains *cfgcommon.StringList `json:"domains"` IP *cfgcommon.StringList `json:"ip"` Port *cfgcommon.PortList `json:"port"` Network *cfgcommon.NetworkList `json:"network"` SourceIP *cfgcommon.StringList `json:"source"` SourcePort *cfgcommon.PortList `json:"sourcePort"` User *cfgcommon.StringList `json:"user"` InboundTag *cfgcommon.StringList `json:"inboundTag"` Protocols *cfgcommon.StringList `json:"protocol"` Attributes string `json:"attrs"` } rawFieldRule := new(RawFieldRule) err := json.Unmarshal(msg, rawFieldRule) if err != nil { return nil, err } rule := new(router.RoutingRule) switch { case len(rawFieldRule.OutboundTag) > 0: rule.TargetTag = &router.RoutingRule_Tag{ Tag: rawFieldRule.OutboundTag, } case len(rawFieldRule.BalancerTag) > 0: rule.TargetTag = &router.RoutingRule_BalancingTag{ BalancingTag: rawFieldRule.BalancerTag, } default: return nil, newError("neither outboundTag nor balancerTag is specified in routing rule") } if rawFieldRule.DomainMatcher != "" { rule.DomainMatcher = rawFieldRule.DomainMatcher } if rawFieldRule.Domain != nil { for _, domain := range *rawFieldRule.Domain { rules, err := parseDomainRule(ctx, domain) if err != nil { return nil, newError("failed to parse domain rule: ", domain).Base(err) } rule.Domain = append(rule.Domain, rules...) } } if rawFieldRule.Domains != nil { for _, domain := range *rawFieldRule.Domains { rules, err := parseDomainRule(ctx, domain) if err != nil { return nil, newError("failed to parse domain rule: ", domain).Base(err) } rule.Domain = append(rule.Domain, rules...) } } if rawFieldRule.IP != nil { geoipList, err := toCidrList(ctx, *rawFieldRule.IP) if err != nil { return nil, err } rule.Geoip = geoipList } if rawFieldRule.Port != nil { rule.PortList = rawFieldRule.Port.Build() } if rawFieldRule.Network != nil { rule.Networks = rawFieldRule.Network.Build() } if rawFieldRule.SourceIP != nil { geoipList, err := toCidrList(ctx, *rawFieldRule.SourceIP) if err != nil { return nil, err } rule.SourceGeoip = geoipList } if rawFieldRule.SourcePort != nil { rule.SourcePortList = rawFieldRule.SourcePort.Build() } if rawFieldRule.User != nil { for _, s := range *rawFieldRule.User { rule.UserEmail = append(rule.UserEmail, s) } } if rawFieldRule.InboundTag != nil { for _, s := range *rawFieldRule.InboundTag { rule.InboundTag = append(rule.InboundTag, s) } } if rawFieldRule.Protocols != nil { for _, s := range *rawFieldRule.Protocols { rule.Protocol = append(rule.Protocol, s) } } if len(rawFieldRule.Attributes) > 0 { rule.Attributes = rawFieldRule.Attributes } return rule, nil } func ParseRule(ctx context.Context, msg json.RawMessage) (*router.RoutingRule, error) { rawRule := new(RouterRule) err := json.Unmarshal(msg, rawRule) if err != nil { return nil, newError("invalid router rule").Base(err) } if strings.EqualFold(rawRule.Type, "field") { fieldrule, err := parseFieldRule(ctx, msg) if err != nil { return nil, newError("invalid field rule").Base(err) } return fieldrule, nil } return nil, newError("unknown router rule type: ", rawRule.Type) } func ParseIP(s string) (*routercommon.CIDR, error) { var addr, mask string i := strings.Index(s, "/") if i < 0 { addr = s } else { addr = s[:i] mask = s[i+1:] } ip := net.ParseAddress(addr) switch ip.Family() { case net.AddressFamilyIPv4: bits := uint32(32) if len(mask) > 0 { bits64, err := strconv.ParseUint(mask, 10, 32) if err != nil { return nil, newError("invalid network mask for router: ", mask).Base(err) } bits = uint32(bits64) } if bits > 32 { return nil, newError("invalid network mask for router: ", bits) } return &routercommon.CIDR{ Ip: []byte(ip.IP()), Prefix: bits, }, nil case net.AddressFamilyIPv6: bits := uint32(128) if len(mask) > 0 { bits64, err := strconv.ParseUint(mask, 10, 32) if err != nil { return nil, newError("invalid network mask for router: ", mask).Base(err) } bits = uint32(bits64) } if bits > 128 { return nil, newError("invalid network mask for router: ", bits) } return &routercommon.CIDR{ Ip: []byte(ip.IP()), Prefix: bits, }, nil default: return nil, newError("unsupported address for router: ", s) } } func ParseDomainRule(ctx context.Context, domain string) ([]*routercommon.Domain, error) { return parseDomainRule(ctx, domain) } func ToCidrList(ctx context.Context, ips cfgcommon.StringList) ([]*routercommon.GeoIP, error) { return toCidrList(ctx, ips) } type RouterRule struct { Type string `json:"type"` OutboundTag string `json:"outboundTag"` BalancerTag string `json:"balancerTag"` DomainMatcher string `json:"domainMatcher"` } ================================================ FILE: infra/conf/rule/rule_test.go ================================================ package rule_test import ( "context" "errors" "io/fs" "os" "path/filepath" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/standard" "github.com/v2fly/v2ray-core/v5/infra/conf/rule" ) func init() { const geoipURL = "https://raw.githubusercontent.com/v2fly/geoip/release/geoip.dat" wd, err := os.Getwd() common.Must(err) tempPath := filepath.Join(wd, "..", "..", "..", "testing", "temp") geoipPath := filepath.Join(tempPath, "geoip.dat") os.Setenv("v2ray.location.asset", tempPath) if _, err := os.Stat(geoipPath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geoipBytes, err := common.FetchHTTPContent(geoipURL) common.Must(err) common.Must(filesystem.WriteFile(geoipPath, geoipBytes)) } } func TestToCidrList(t *testing.T) { t.Log(os.Getenv("v2ray.location.asset")) common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoiptestrouter.dat"), platform.GetAssetLocation("geoip.dat"), 0o600)) ips := cfgcommon.StringList([]string{ "geoip:us", "geoip:cn", "geoip:!cn", "ext:geoiptestrouter.dat:!cn", "ext:geoiptestrouter.dat:ca", "ext-ip:geoiptestrouter.dat:!cn", "ext-ip:geoiptestrouter.dat:!ca", }) cfgctx := cfgcommon.NewConfigureLoadingContext(context.Background()) if loader, err := geodata.GetGeoDataLoader("standard"); err == nil { cfgcommon.SetGeoDataLoader(cfgctx, loader) } else { t.Fatal(err) } _, err := rule.ToCidrList(cfgctx, ips) if err != nil { t.Fatalf("Failed to parse geoip list, got %s", err) } } ================================================ FILE: infra/conf/serial/errors.generated.go ================================================ package serial import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/serial/loader.go ================================================ package serial import ( "bytes" "encoding/json" "io" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/errors" json_reader "github.com/v2fly/v2ray-core/v5/infra/conf/json" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" ) type offset struct { line int char int } func findOffset(b []byte, o int) *offset { if o >= len(b) || o < 0 { return nil } line := 1 char := 0 for i, x := range b { if i == o { break } if x == '\n' { line++ char = 0 } else { char++ } } return &offset{line: line, char: char} } // DecodeJSONConfig reads from reader and decode the config into *conf.Config // syntax error could be detected. func DecodeJSONConfig(reader io.Reader) (*v4.Config, error) { jsonConfig := &v4.Config{} err := DecodeJSON(reader, jsonConfig) if err != nil { return nil, err } return jsonConfig, nil } // DecodeJSON reads from reader and decode into target // syntax error could be detected. func DecodeJSON(reader io.Reader, target interface{}) error { jsonContent := bytes.NewBuffer(make([]byte, 0, 10240)) jsonReader := io.TeeReader(&json_reader.Reader{ Reader: reader, }, jsonContent) decoder := json.NewDecoder(jsonReader) if err := decoder.Decode(target); err != nil { var pos *offset cause := errors.Cause(err) switch tErr := cause.(type) { case *json.SyntaxError: pos = findOffset(jsonContent.Bytes(), int(tErr.Offset)) case *json.UnmarshalTypeError: pos = findOffset(jsonContent.Bytes(), int(tErr.Offset)) } if pos != nil { return newError("failed to read config file at line ", pos.line, " char ", pos.char).Base(err) } return newError("failed to read config file").Base(err) } return nil } func LoadJSONConfig(reader io.Reader) (*core.Config, error) { jsonConfig, err := DecodeJSONConfig(reader) if err != nil { return nil, err } pbConfig, err := jsonConfig.Build() if err != nil { return nil, newError("failed to parse json config").Base(err) } return pbConfig, nil } ================================================ FILE: infra/conf/serial/loader_test.go ================================================ package serial_test import ( "bytes" "strings" "testing" "github.com/v2fly/v2ray-core/v5/infra/conf/serial" ) func TestLoaderError(t *testing.T) { testCases := []struct { Input string Output string }{ { Input: `{ "log": { // abcd 0, "loglevel": "info" } }`, Output: "line 4 char 6", }, { Input: `{ "log": { // abcd "loglevel": "info", } }`, Output: "line 5 char 5", }, { Input: `{ "port": 1, "inbounds": [{ "protocol": "test" }] }`, Output: "parse json config", }, { Input: `{ "inbounds": [{ "port": 1, "listen": 0, "protocol": "test" }] }`, Output: "line 1 char 1", }, } for _, testCase := range testCases { reader := bytes.NewReader([]byte(testCase.Input)) _, err := serial.LoadJSONConfig(reader) errString := err.Error() if !strings.Contains(errString, testCase.Output) { t.Error("unexpected output from json: ", testCase.Input, ". expected ", testCase.Output, ", but actually ", errString) } } } ================================================ FILE: infra/conf/serial/serial.go ================================================ package serial //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: infra/conf/synthetic/dns/dns.go ================================================ package dns //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "encoding/json" "sort" "strings" "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" rule2 "github.com/v2fly/v2ray-core/v5/infra/conf/rule" ) type NameServerConfig struct { Address *cfgcommon.Address ClientIP *cfgcommon.Address Port uint16 Tag string QueryStrategy string CacheStrategy string FallbackStrategy string SkipFallback bool Domains []string ExpectIPs cfgcommon.StringList FakeDNS FakeDNSConfigExtend cfgctx context.Context } func (c *NameServerConfig) UnmarshalJSON(data []byte) error { var address cfgcommon.Address if err := json.Unmarshal(data, &address); err == nil { c.Address = &address return nil } var advanced struct { Address *cfgcommon.Address `json:"address"` ClientIP *cfgcommon.Address `json:"clientIp"` Port uint16 `json:"port"` Tag string `json:"tag"` QueryStrategy string `json:"queryStrategy"` CacheStrategy string `json:"cacheStrategy"` FallbackStrategy string `json:"fallbackStrategy"` SkipFallback bool `json:"skipFallback"` Domains []string `json:"domains"` ExpectIPs cfgcommon.StringList `json:"expectIps"` FakeDNS FakeDNSConfigExtend `json:"fakedns"` } if err := json.Unmarshal(data, &advanced); err == nil { c.Address = advanced.Address c.ClientIP = advanced.ClientIP c.Port = advanced.Port c.Tag = advanced.Tag c.QueryStrategy = advanced.QueryStrategy c.CacheStrategy = advanced.CacheStrategy c.FallbackStrategy = advanced.FallbackStrategy c.SkipFallback = advanced.SkipFallback c.Domains = advanced.Domains c.ExpectIPs = advanced.ExpectIPs c.FakeDNS = advanced.FakeDNS return nil } return newError("failed to parse name server: ", string(data)) } func toDomainMatchingType(t routercommon.Domain_Type) dns.DomainMatchingType { switch t { case routercommon.Domain_RootDomain: return dns.DomainMatchingType_Subdomain case routercommon.Domain_Full: return dns.DomainMatchingType_Full case routercommon.Domain_Plain: return dns.DomainMatchingType_Keyword case routercommon.Domain_Regex: return dns.DomainMatchingType_Regex default: panic("unknown domain type") } } func (c *NameServerConfig) BuildV5(ctx context.Context) (*dns.NameServer, error) { c.cfgctx = ctx return c.Build() } func (c *NameServerConfig) Build() (*dns.NameServer, error) { cfgctx := c.cfgctx if c.Address == nil { return nil, newError("NameServer address is not specified.") } var domains []*dns.NameServer_PriorityDomain var originalRules []*dns.NameServer_OriginalRule for _, rule := range c.Domains { parsedDomain, err := rule2.ParseDomainRule(cfgctx, rule) if err != nil { return nil, newError("invalid domain rule: ", rule).Base(err) } for _, pd := range parsedDomain { domains = append(domains, &dns.NameServer_PriorityDomain{ Type: toDomainMatchingType(pd.Type), Domain: pd.Value, }) } originalRules = append(originalRules, &dns.NameServer_OriginalRule{ Rule: rule, Size: uint32(len(parsedDomain)), }) } geoipList, err := rule2.ToCidrList(cfgctx, c.ExpectIPs) if err != nil { return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err) } var fakeDNS *fakedns.FakeDnsPoolMulti if c.FakeDNS.FakeDNSConfig != nil { fake, err := c.FakeDNS.FakeDNSConfig.Build() if err != nil { return nil, newError("failed to build fakedns").Base(err) } fakeDNS = fake } var myClientIP []byte if c.ClientIP != nil { if !c.ClientIP.Family().IsIP() { return nil, newError("not an IP address:", c.ClientIP.String()) } myClientIP = []byte(c.ClientIP.IP()) } queryStrategy := new(dns.QueryStrategy) switch strings.ToLower(c.QueryStrategy) { case "useip", "use_ip", "use-ip": *queryStrategy = dns.QueryStrategy_USE_IP case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": *queryStrategy = dns.QueryStrategy_USE_IP4 case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": *queryStrategy = dns.QueryStrategy_USE_IP6 default: queryStrategy = nil } cacheStrategy := new(dns.CacheStrategy) switch strings.ToLower(c.CacheStrategy) { case "enabled": *cacheStrategy = dns.CacheStrategy_CacheEnabled case "disabled": *cacheStrategy = dns.CacheStrategy_CacheDisabled default: cacheStrategy = nil } fallbackStrategy := new(dns.FallbackStrategy) switch strings.ToLower(c.FallbackStrategy) { case "enabled": *fallbackStrategy = dns.FallbackStrategy_Enabled case "disabled": *fallbackStrategy = dns.FallbackStrategy_Disabled case "disabledifanymatch", "disabled_if_any_match", "disabled-if-any-match": *fallbackStrategy = dns.FallbackStrategy_DisabledIfAnyMatch default: fallbackStrategy = nil } return &dns.NameServer{ Address: &net.Endpoint{ Network: net.Network_UDP, Address: c.Address.Build(), Port: uint32(c.Port), }, ClientIp: myClientIP, Tag: c.Tag, SkipFallback: c.SkipFallback, QueryStrategy: queryStrategy, CacheStrategy: cacheStrategy, FallbackStrategy: fallbackStrategy, PrioritizedDomain: domains, Geoip: geoipList, OriginalRules: originalRules, FakeDns: fakeDNS, }, nil } var typeMap = map[routercommon.Domain_Type]dns.DomainMatchingType{ routercommon.Domain_Full: dns.DomainMatchingType_Full, routercommon.Domain_RootDomain: dns.DomainMatchingType_Subdomain, routercommon.Domain_Plain: dns.DomainMatchingType_Keyword, routercommon.Domain_Regex: dns.DomainMatchingType_Regex, } // DNSConfig is a JSON serializable object for dns.Config. type DNSConfig struct { // nolint: revive Servers []*NameServerConfig `json:"servers"` Hosts map[string]*HostAddress `json:"hosts"` FakeDNS *FakeDNSConfig `json:"fakedns"` DomainMatcher string `json:"domainMatcher"` ClientIP *cfgcommon.Address `json:"clientIp"` Tag string `json:"tag"` QueryStrategy string `json:"queryStrategy"` CacheStrategy string `json:"cacheStrategy"` FallbackStrategy string `json:"fallbackStrategy"` DisableCache bool `json:"disableCache"` DisableFallback bool `json:"disableFallback"` DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"` cfgctx context.Context } type HostAddress struct { addr *cfgcommon.Address addrs []*cfgcommon.Address } // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (h *HostAddress) UnmarshalJSON(data []byte) error { addr := new(cfgcommon.Address) var addrs []*cfgcommon.Address switch { case json.Unmarshal(data, &addr) == nil: h.addr = addr case json.Unmarshal(data, &addrs) == nil: h.addrs = addrs default: return newError("invalid address") } return nil } func getHostMapping(ha *HostAddress) *dns.HostMapping { if ha.addr != nil { if ha.addr.Family().IsDomain() { return &dns.HostMapping{ ProxiedDomain: ha.addr.Domain(), } } return &dns.HostMapping{ Ip: [][]byte{ha.addr.IP()}, } } ips := make([][]byte, 0, len(ha.addrs)) for _, addr := range ha.addrs { if addr.Family().IsDomain() { return &dns.HostMapping{ ProxiedDomain: addr.Domain(), } } ips = append(ips, []byte(addr.IP())) } return &dns.HostMapping{ Ip: ips, } } func (c *DNSConfig) BuildV5(ctx context.Context) (*dns.Config, error) { c.cfgctx = ctx return c.Build() } // Build implements Buildable func (c *DNSConfig) Build() (*dns.Config, error) { if c.cfgctx == nil { c.cfgctx = cfgcommon.NewConfigureLoadingContext(context.Background()) geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string { return "standard" }) if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil { cfgcommon.SetGeoDataLoader(c.cfgctx, loader) } else { return nil, newError("unable to create geo data loader ").Base(err) } } cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(c.cfgctx) geoLoader := cfgEnv.GetGeoLoader() config := &dns.Config{ Tag: c.Tag, DisableCache: c.DisableCache, DisableFallback: c.DisableFallback, DisableFallbackIfMatch: c.DisableFallbackIfMatch, DomainMatcher: c.DomainMatcher, } if c.ClientIP != nil { if !c.ClientIP.Family().IsIP() { return nil, newError("not an IP address:", c.ClientIP.String()) } config.ClientIp = []byte(c.ClientIP.IP()) } config.QueryStrategy = dns.QueryStrategy_USE_IP switch strings.ToLower(c.QueryStrategy) { case "useip", "use_ip", "use-ip": config.QueryStrategy = dns.QueryStrategy_USE_IP case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": config.QueryStrategy = dns.QueryStrategy_USE_IP4 case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": config.QueryStrategy = dns.QueryStrategy_USE_IP6 } config.CacheStrategy = dns.CacheStrategy_CacheEnabled switch strings.ToLower(c.CacheStrategy) { case "enabled": config.CacheStrategy = dns.CacheStrategy_CacheEnabled case "disabled": config.CacheStrategy = dns.CacheStrategy_CacheDisabled } config.FallbackStrategy = dns.FallbackStrategy_Enabled switch strings.ToLower(c.FallbackStrategy) { case "enabled": config.FallbackStrategy = dns.FallbackStrategy_Enabled case "disabled": config.FallbackStrategy = dns.FallbackStrategy_Disabled case "disabledifanymatch", "disabled_if_any_match", "disabled-if-any-match": config.FallbackStrategy = dns.FallbackStrategy_DisabledIfAnyMatch } for _, server := range c.Servers { server.cfgctx = c.cfgctx ns, err := server.Build() if err != nil { return nil, newError("failed to build nameserver").Base(err) } config.NameServer = append(config.NameServer, ns) } if c.Hosts != nil { mappings := make([]*dns.HostMapping, 0, 20) domains := make([]string, 0, len(c.Hosts)) for domain := range c.Hosts { domains = append(domains, domain) } sort.Strings(domains) for _, domain := range domains { switch { case strings.HasPrefix(domain, "domain:"): domainName := domain[7:] if len(domainName) == 0 { return nil, newError("empty domain type of rule: ", domain) } mapping := getHostMapping(c.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Subdomain mapping.Domain = domainName mappings = append(mappings, mapping) case strings.HasPrefix(domain, "geosite:"): listName := domain[8:] if len(listName) == 0 { return nil, newError("empty geosite rule: ", domain) } geositeList, err := geoLoader.LoadGeoSite(listName) if err != nil { return nil, newError("failed to load geosite: ", listName).Base(err) } for _, d := range geositeList { mapping := getHostMapping(c.Hosts[domain]) mapping.Type = typeMap[d.Type] mapping.Domain = d.Value mappings = append(mappings, mapping) } case strings.HasPrefix(domain, "regexp:"): regexpVal := domain[7:] if len(regexpVal) == 0 { return nil, newError("empty regexp type of rule: ", domain) } mapping := getHostMapping(c.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Regex mapping.Domain = regexpVal mappings = append(mappings, mapping) case strings.HasPrefix(domain, "keyword:"): keywordVal := domain[8:] if len(keywordVal) == 0 { return nil, newError("empty keyword type of rule: ", domain) } mapping := getHostMapping(c.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Keyword mapping.Domain = keywordVal mappings = append(mappings, mapping) case strings.HasPrefix(domain, "full:"): fullVal := domain[5:] if len(fullVal) == 0 { return nil, newError("empty full domain type of rule: ", domain) } mapping := getHostMapping(c.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Full mapping.Domain = fullVal mappings = append(mappings, mapping) case strings.HasPrefix(domain, "dotless:"): mapping := getHostMapping(c.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Regex switch substr := domain[8:]; { case substr == "": mapping.Domain = "^[^.]*$" case !strings.Contains(substr, "."): mapping.Domain = "^[^.]*" + substr + "[^.]*$" default: return nil, newError("substr in dotless rule should not contain a dot: ", substr) } mappings = append(mappings, mapping) case strings.HasPrefix(domain, "ext:"): kv := strings.Split(domain[4:], ":") if len(kv) != 2 { return nil, newError("invalid external resource: ", domain) } filename := kv[0] list := kv[1] geositeList, err := geoLoader.LoadGeoSiteWithAttr(filename, list) if err != nil { return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err) } for _, d := range geositeList { mapping := getHostMapping(c.Hosts[domain]) mapping.Type = typeMap[d.Type] mapping.Domain = d.Value mappings = append(mappings, mapping) } default: mapping := getHostMapping(c.Hosts[domain]) mapping.Type = dns.DomainMatchingType_Full mapping.Domain = domain mappings = append(mappings, mapping) } } config.StaticHosts = append(config.StaticHosts, mappings...) } if c.FakeDNS != nil { fakeDNS, err := c.FakeDNS.Build() if err != nil { return nil, newError("failed to build fakedns").Base(err) } config.FakeDns = fakeDNS } return config, nil } ================================================ FILE: infra/conf/synthetic/dns/dns_test.go ================================================ package dns_test import ( "encoding/json" "errors" "io/fs" "os" "path/filepath" "testing" "google.golang.org/protobuf/runtime/protoiface" "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/platform/filesystem" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/standard" dns2 "github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/dns" ) func init() { const ( geoipURL = "https://raw.githubusercontent.com/v2fly/geoip/release/geoip.dat" geositeURL = "https://raw.githubusercontent.com/v2fly/domain-list-community/release/dlc.dat" ) wd, err := os.Getwd() common.Must(err) tempPath := filepath.Join(wd, "..", "..", "..", "..", "testing", "temp") geoipPath := filepath.Join(tempPath, "geoip.dat") geositePath := filepath.Join(tempPath, "geosite.dat") os.Setenv("v2ray.location.asset", tempPath) if _, err := os.Stat(geoipPath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geoipBytes, err := common.FetchHTTPContent(geoipURL) common.Must(err) common.Must(filesystem.WriteFile(geoipPath, geoipBytes)) } if _, err := os.Stat(geositePath); err != nil && errors.Is(err, fs.ErrNotExist) { common.Must(os.MkdirAll(tempPath, 0o755)) geositeBytes, err := common.FetchHTTPContent(geositeURL) common.Must(err) common.Must(filesystem.WriteFile(geositePath, geositeBytes)) } } func TestDNSConfigParsing(t *testing.T) { parserCreator := func() func(string) (protoiface.MessageV1, error) { return func(s string) (protoiface.MessageV1, error) { config := new(dns2.DNSConfig) if err := json.Unmarshal([]byte(s), config); err != nil { return nil, err } return config.Build() } } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "servers": [{ "address": "8.8.8.8", "clientIp": "10.0.0.1", "port": 5353, "skipFallback": true, "domains": ["domain:v2fly.org"] }], "hosts": { "v2fly.org": "127.0.0.1", "www.v2fly.org": ["1.2.3.4", "5.6.7.8"], "domain:example.com": "google.com", "geosite:test": ["127.0.0.1", "127.0.0.2"], "keyword:google": ["8.8.8.8", "8.8.4.4"], "regexp:.*\\.com": "8.8.4.4" }, "clientIp": "10.0.0.1", "queryStrategy": "UseIPv4", "disableCache": true, "disableFallback": true }`, Parser: parserCreator(), Output: &dns.Config{ NameServer: []*dns.NameServer{ { Address: &net.Endpoint{ Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{8, 8, 8, 8}, }, }, Network: net.Network_UDP, Port: 5353, }, ClientIp: []byte{10, 0, 0, 1}, SkipFallback: true, PrioritizedDomain: []*dns.NameServer_PriorityDomain{ { Type: dns.DomainMatchingType_Subdomain, Domain: "v2fly.org", }, }, OriginalRules: []*dns.NameServer_OriginalRule{ { Rule: "domain:v2fly.org", Size: 1, }, }, }, }, StaticHosts: []*dns.HostMapping{ { Type: dns.DomainMatchingType_Subdomain, Domain: "example.com", ProxiedDomain: "google.com", }, { Type: dns.DomainMatchingType_Full, Domain: "test.example.com", Ip: [][]byte{{127, 0, 0, 1}, {127, 0, 0, 2}}, }, { Type: dns.DomainMatchingType_Keyword, Domain: "google", Ip: [][]byte{{8, 8, 8, 8}, {8, 8, 4, 4}}, }, { Type: dns.DomainMatchingType_Regex, Domain: ".*\\.com", Ip: [][]byte{{8, 8, 4, 4}}, }, { Type: dns.DomainMatchingType_Full, Domain: "v2fly.org", Ip: [][]byte{{127, 0, 0, 1}}, }, { Type: dns.DomainMatchingType_Full, Domain: "www.v2fly.org", Ip: [][]byte{{1, 2, 3, 4}, {5, 6, 7, 8}}, }, }, ClientIp: []byte{10, 0, 0, 1}, QueryStrategy: dns.QueryStrategy_USE_IP4, DisableCache: true, DisableFallback: true, }, }, { Input: `{ "servers": [{ "address": "fakedns", "tag": "fake", "queryStrategy": "UseIPv6", "fallbackStrategy": "disabledIfAnyMatch", "fakedns": true }, { "address": "8.8.8.8", "port": 5353, "tag": "local", "clientIp": "10.0.0.1", "queryStrategy": "UseIP", "cacheStrategy": "enabled", "fallbackStrategy": "disabled", "domains": ["domain:v2fly.org"], "fakedns": ["198.19.0.0/16", "fc01::/18"] }], "hosts": { "v2fly.org": "127.0.0.1", "www.v2fly.org": ["1.2.3.4", "5.6.7.8"], "domain:example.com": "google.com", "geosite:test": ["127.0.0.1", "127.0.0.2"], "keyword:google": ["8.8.8.8", "8.8.4.4"], "regexp:.*\\.com": "8.8.4.4" }, "fakedns": [ { "ipPool": "198.18.0.0/16", "poolSize": 32768 }, { "ipPool": "fc00::/18", "poolSize": 32768 } ], "tag": "global", "clientIp": "10.0.0.1", "queryStrategy": "UseIPv4", "cacheStrategy": "disabled", "fallbackStrategy": "enabled" }`, Parser: parserCreator(), Output: &dns.Config{ NameServer: []*dns.NameServer{ { Address: &net.Endpoint{ Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Domain{ Domain: "fakedns", }, }, Network: net.Network_UDP, }, Tag: "fake", QueryStrategy: dns.QueryStrategy_USE_IP6.Enum(), FallbackStrategy: dns.FallbackStrategy_DisabledIfAnyMatch.Enum(), FakeDns: &fakedns.FakeDnsPoolMulti{ Pools: []*fakedns.FakeDnsPool{}, }, }, { Address: &net.Endpoint{ Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{8, 8, 8, 8}, }, }, Network: net.Network_UDP, Port: 5353, }, Tag: "local", ClientIp: []byte{10, 0, 0, 1}, QueryStrategy: dns.QueryStrategy_USE_IP.Enum(), CacheStrategy: dns.CacheStrategy_CacheEnabled.Enum(), FallbackStrategy: dns.FallbackStrategy_Disabled.Enum(), PrioritizedDomain: []*dns.NameServer_PriorityDomain{ { Type: dns.DomainMatchingType_Subdomain, Domain: "v2fly.org", }, }, OriginalRules: []*dns.NameServer_OriginalRule{ { Rule: "domain:v2fly.org", Size: 1, }, }, FakeDns: &fakedns.FakeDnsPoolMulti{ Pools: []*fakedns.FakeDnsPool{ {IpPool: "198.19.0.0/16", LruSize: 65535}, {IpPool: "fc01::/18", LruSize: 65535}, }, }, }, }, StaticHosts: []*dns.HostMapping{ { Type: dns.DomainMatchingType_Subdomain, Domain: "example.com", ProxiedDomain: "google.com", }, { Type: dns.DomainMatchingType_Full, Domain: "test.example.com", Ip: [][]byte{{127, 0, 0, 1}, {127, 0, 0, 2}}, }, { Type: dns.DomainMatchingType_Keyword, Domain: "google", Ip: [][]byte{{8, 8, 8, 8}, {8, 8, 4, 4}}, }, { Type: dns.DomainMatchingType_Regex, Domain: ".*\\.com", Ip: [][]byte{{8, 8, 4, 4}}, }, { Type: dns.DomainMatchingType_Full, Domain: "v2fly.org", Ip: [][]byte{{127, 0, 0, 1}}, }, { Type: dns.DomainMatchingType_Full, Domain: "www.v2fly.org", Ip: [][]byte{{1, 2, 3, 4}, {5, 6, 7, 8}}, }, }, FakeDns: &fakedns.FakeDnsPoolMulti{ Pools: []*fakedns.FakeDnsPool{ {IpPool: "198.18.0.0/16", LruSize: 32768}, {IpPool: "fc00::/18", LruSize: 32768}, }, }, Tag: "global", ClientIp: []byte{10, 0, 0, 1}, QueryStrategy: dns.QueryStrategy_USE_IP4, CacheStrategy: dns.CacheStrategy_CacheDisabled, FallbackStrategy: dns.FallbackStrategy_Enabled, }, }, }) } ================================================ FILE: infra/conf/synthetic/dns/errors.generated.go ================================================ package dns import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/synthetic/dns/fakedns.go ================================================ package dns import ( "encoding/json" "net" "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" ) type FakeDNSPoolElementConfig struct { IPPool string `json:"ipPool"` LRUSize int64 `json:"poolSize"` } type FakeDNSConfig struct { pool *FakeDNSPoolElementConfig pools []*FakeDNSPoolElementConfig } // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON func (f *FakeDNSConfig) UnmarshalJSON(data []byte) error { var pool FakeDNSPoolElementConfig var pools []*FakeDNSPoolElementConfig var ipPools []string switch { case json.Unmarshal(data, &pool) == nil: f.pool = &pool case json.Unmarshal(data, &pools) == nil: f.pools = pools case json.Unmarshal(data, &ipPools) == nil: f.pools = make([]*FakeDNSPoolElementConfig, 0, len(ipPools)) for _, ipPool := range ipPools { _, ipNet, err := net.ParseCIDR(ipPool) if err != nil { return err } ones, bits := ipNet.Mask.Size() sizeInBits := bits - ones if sizeInBits > 16 { // At most 65536 ips for a IP pool sizeInBits = 16 } f.pools = append(f.pools, &FakeDNSPoolElementConfig{ IPPool: ipPool, LRUSize: (1 << sizeInBits) - 1, }) } default: return newError("invalid fakedns config") } return nil } func (f *FakeDNSConfig) Build() (*fakedns.FakeDnsPoolMulti, error) { fakeDNSPool := fakedns.FakeDnsPoolMulti{} if f.pool != nil { fakeDNSPool.Pools = append(fakeDNSPool.Pools, &fakedns.FakeDnsPool{ IpPool: f.pool.IPPool, LruSize: f.pool.LRUSize, }) return &fakeDNSPool, nil } if f.pools != nil { for _, v := range f.pools { fakeDNSPool.Pools = append(fakeDNSPool.Pools, &fakedns.FakeDnsPool{IpPool: v.IPPool, LruSize: v.LRUSize}) } return &fakeDNSPool, nil } return nil, newError("no valid FakeDNS config") } type FakeDNSConfigExtend struct { // Adds boolean value parsing for "fakedns" config *FakeDNSConfig } func (f *FakeDNSConfigExtend) UnmarshalJSON(data []byte) error { var enabled bool if json.Unmarshal(data, &enabled) == nil { if enabled { f.FakeDNSConfig = &FakeDNSConfig{pools: []*FakeDNSPoolElementConfig{}} } return nil } return json.Unmarshal(data, &f.FakeDNSConfig) } ================================================ FILE: infra/conf/synthetic/log/log.go ================================================ package log import ( "strings" "github.com/v2fly/v2ray-core/v5/app/log" clog "github.com/v2fly/v2ray-core/v5/common/log" ) func DefaultLogConfig() *log.Config { return &log.Config{ Access: &log.LogSpecification{Type: log.LogType_None}, Error: &log.LogSpecification{Type: log.LogType_Console, Level: clog.Severity_Warning}, } } type LogConfig struct { // nolint: revive AccessLog string `json:"access"` ErrorLog string `json:"error"` LogLevel string `json:"loglevel"` } func (v *LogConfig) Build() *log.Config { if v == nil { return nil } config := &log.Config{ Access: &log.LogSpecification{Type: log.LogType_Console}, Error: &log.LogSpecification{Type: log.LogType_Console}, } if v.AccessLog == "none" { config.Access.Type = log.LogType_None } else if len(v.AccessLog) > 0 { config.Access.Path = v.AccessLog config.Access.Type = log.LogType_File } if v.ErrorLog == "none" { config.Error.Type = log.LogType_None } else if len(v.ErrorLog) > 0 { config.Error.Path = v.ErrorLog config.Error.Type = log.LogType_File } level := strings.ToLower(v.LogLevel) switch level { case "debug": config.Error.Level = clog.Severity_Debug case "info": config.Error.Level = clog.Severity_Info case "error": config.Error.Level = clog.Severity_Error case "none": config.Error.Type = log.LogType_None config.Error.Type = log.LogType_None default: config.Error.Level = clog.Severity_Warning } return config } ================================================ FILE: infra/conf/synthetic/router/errors.generated.go ================================================ package router import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/synthetic/router/router.go ================================================ package router //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "encoding/json" "strings" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" rule2 "github.com/v2fly/v2ray-core/v5/infra/conf/rule" ) type RouterRulesConfig struct { // nolint: revive RuleList []json.RawMessage `json:"rules"` DomainStrategy string `json:"domainStrategy"` } // StrategyConfig represents a strategy config type StrategyConfig struct { Type string `json:"type"` Settings *json.RawMessage `json:"settings"` } type BalancingRule struct { Tag string `json:"tag"` Selectors cfgcommon.StringList `json:"selector"` Strategy StrategyConfig `json:"strategy"` FallbackTag string `json:"fallbackTag"` } // Build builds the balancing rule func (r *BalancingRule) Build() (*router.BalancingRule, error) { if r.Tag == "" { return nil, newError("empty balancer tag") } if len(r.Selectors) == 0 { return nil, newError("empty selector list") } var strategy string switch strings.ToLower(r.Strategy.Type) { case strategyRandom, "": r.Strategy.Type = strategyRandom strategy = strategyRandom case strategyLeastLoad: strategy = strategyLeastLoad case strategyLeastPing: strategy = "leastping" default: return nil, newError("unknown balancing strategy: " + r.Strategy.Type) } settings := []byte("{}") if r.Strategy.Settings != nil { settings = ([]byte)(*r.Strategy.Settings) } rawConfig, err := strategyConfigLoader.LoadWithID(settings, r.Strategy.Type) if err != nil { return nil, newError("failed to parse to strategy config.").Base(err) } var ts proto.Message if builder, ok := rawConfig.(cfgcommon.Buildable); ok { ts, err = builder.Build() if err != nil { return nil, err } } return &router.BalancingRule{ Strategy: strategy, StrategySettings: serial.ToTypedMessage(ts), FallbackTag: r.FallbackTag, OutboundSelector: r.Selectors, Tag: r.Tag, }, nil } type RouterConfig struct { // nolint: revive Settings *RouterRulesConfig `json:"settings"` // Deprecated RuleList []json.RawMessage `json:"rules"` DomainStrategy *string `json:"domainStrategy"` Balancers []*BalancingRule `json:"balancers"` DomainMatcher string `json:"domainMatcher"` cfgctx context.Context } func (c *RouterConfig) getDomainStrategy() router.DomainStrategy { ds := "" if c.DomainStrategy != nil { ds = *c.DomainStrategy } else if c.Settings != nil { ds = c.Settings.DomainStrategy } switch strings.ToLower(ds) { case "alwaysip", "always_ip", "always-ip": return router.DomainStrategy_UseIp case "ipifnonmatch", "ip_if_non_match", "ip-if-non-match": return router.DomainStrategy_IpIfNonMatch case "ipondemand", "ip_on_demand", "ip-on-demand": return router.DomainStrategy_IpOnDemand default: return router.DomainStrategy_AsIs } } func (c *RouterConfig) BuildV5(ctx context.Context) (*router.Config, error) { c.cfgctx = ctx return c.Build() } func (c *RouterConfig) Build() (*router.Config, error) { config := new(router.Config) config.DomainStrategy = c.getDomainStrategy() if c.cfgctx == nil { c.cfgctx = cfgcommon.NewConfigureLoadingContext(context.Background()) geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string { return "standard" }) if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil { cfgcommon.SetGeoDataLoader(c.cfgctx, loader) } else { return nil, newError("unable to create geo data loader ").Base(err) } } var rawRuleList []json.RawMessage if c != nil { rawRuleList = c.RuleList if c.Settings != nil { c.RuleList = append(c.RuleList, c.Settings.RuleList...) rawRuleList = c.RuleList } } for _, rawRule := range rawRuleList { rule, err := rule2.ParseRule(c.cfgctx, rawRule) if err != nil { return nil, err } if rule.DomainMatcher == "" { rule.DomainMatcher = c.DomainMatcher } config.Rule = append(config.Rule, rule) } for _, rawBalancer := range c.Balancers { balancer, err := rawBalancer.Build() if err != nil { return nil, err } config.BalancingRule = append(config.BalancingRule, balancer) } return config, nil } ================================================ FILE: infra/conf/synthetic/router/router_strategy.go ================================================ package router import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/observatory/burst" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/duration" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/loader" ) const ( strategyRandom string = "random" strategyLeastLoad string = "leastload" strategyLeastPing string = "leastping" ) var strategyConfigLoader = loader.NewJSONConfigLoader(loader.ConfigCreatorCache{ strategyRandom: func() interface{} { return new(strategyRandomConfig) }, strategyLeastLoad: func() interface{} { return new(strategyLeastLoadConfig) }, strategyLeastPing: func() interface{} { return new(strategyLeastPingConfig) }, }, "type", "settings") type strategyEmptyConfig struct{} func (v *strategyEmptyConfig) Build() (proto.Message, error) { return nil, nil } type strategyLeastLoadConfig struct { // weight settings Costs []*router.StrategyWeight `json:"costs,omitempty"` // ping rtt baselines Baselines []duration.Duration `json:"baselines,omitempty"` // expected nodes count to select Expected int32 `json:"expected,omitempty"` // max acceptable rtt, filter away high delay nodes. default 0 MaxRTT duration.Duration `json:"maxRTT,omitempty"` // acceptable failure rate Tolerance float64 `json:"tolerance,omitempty"` ObserverTag string `json:"observerTag,omitempty"` } // HealthCheckSettings holds settings for health Checker type HealthCheckSettings struct { Destination string `json:"destination"` Connectivity string `json:"connectivity"` Interval duration.Duration `json:"interval"` SamplingCount int `json:"sampling"` Timeout duration.Duration `json:"timeout"` } func (h HealthCheckSettings) Build() (proto.Message, error) { return &burst.HealthPingConfig{ Destination: h.Destination, Connectivity: h.Connectivity, Interval: int64(h.Interval), Timeout: int64(h.Timeout), SamplingCount: int32(h.SamplingCount), }, nil } // Build implements Buildable. func (v *strategyLeastLoadConfig) Build() (proto.Message, error) { config := &router.StrategyLeastLoadConfig{} config.Costs = v.Costs config.Tolerance = float32(v.Tolerance) config.ObserverTag = v.ObserverTag if config.Tolerance < 0 { config.Tolerance = 0 } if config.Tolerance > 1 { config.Tolerance = 1 } config.Expected = v.Expected if config.Expected < 0 { config.Expected = 0 } config.MaxRTT = int64(v.MaxRTT) if config.MaxRTT < 0 { config.MaxRTT = 0 } config.Baselines = make([]int64, 0) for _, b := range v.Baselines { if b <= 0 { continue } config.Baselines = append(config.Baselines, int64(b)) } return config, nil } type strategyLeastPingConfig struct { ObserverTag string `json:"observerTag,omitempty"` } func (s strategyLeastPingConfig) Build() (proto.Message, error) { return &router.StrategyLeastPingConfig{ObserverTag: s.ObserverTag}, nil } type strategyRandomConfig struct { AliveOnly bool `json:"aliveOnly,omitempty"` ObserverTag string `json:"observerTag,omitempty"` } func (s strategyRandomConfig) Build() (proto.Message, error) { return &router.StrategyRandomConfig{ObserverTag: s.ObserverTag, AliveOnly: s.AliveOnly}, nil } ================================================ FILE: infra/conf/synthetic/router/router_test.go ================================================ package router_test import ( "encoding/json" "testing" "time" _ "unsafe" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/memconservative" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/standard" router2 "github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/router" ) func TestRouterConfig(t *testing.T) { createParser := func() func(string) (proto.Message, error) { return func(s string) (proto.Message, error) { config := new(router2.RouterConfig) if err := json.Unmarshal([]byte(s), config); err != nil { return nil, err } return config.Build() } } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "strategy": "rules", "settings": { "domainStrategy": "AsIs", "rules": [ { "type": "field", "domain": [ "baidu.com", "qq.com" ], "outboundTag": "direct" }, { "type": "field", "domains": [ "v2fly.org", "github.com" ], "outboundTag": "direct" }, { "type": "field", "ip": [ "10.0.0.0/8", "::1/128" ], "outboundTag": "test" },{ "type": "field", "port": "53, 443, 1000-2000", "outboundTag": "test" },{ "type": "field", "port": 123, "outboundTag": "test" } ] }, "balancers": [ { "tag": "b1", "selector": ["test"] }, { "tag": "b2", "selector": ["test"], "strategy": { "type": "leastload", "settings": { "healthCheck": { "interval": "5m0s", "sampling": 2, "timeout": "5s", "destination": "dest", "connectivity": "conn" }, "costs": [ { "regexp": true, "match": "\\d+(\\.\\d+)", "value": 5 } ], "baselines": ["400ms", "600ms"], "expected": 6, "maxRTT": "1000ms", "tolerance": 0.5 } }, "fallbackTag": "fall" } ] }`, Parser: createParser(), Output: &router.Config{ DomainStrategy: router.DomainStrategy_AsIs, BalancingRule: []*router.BalancingRule{ { Tag: "b1", OutboundSelector: []string{"test"}, Strategy: "random", StrategySettings: serial.ToTypedMessage(&router.StrategyRandomConfig{}), }, { Tag: "b2", OutboundSelector: []string{"test"}, Strategy: "leastload", StrategySettings: serial.ToTypedMessage(&router.StrategyLeastLoadConfig{ Costs: []*router.StrategyWeight{ { Regexp: true, Match: "\\d+(\\.\\d+)", Value: 5, }, }, Baselines: []int64{ int64(time.Duration(400) * time.Millisecond), int64(time.Duration(600) * time.Millisecond), }, Expected: 6, MaxRTT: int64(time.Duration(1000) * time.Millisecond), Tolerance: 0.5, }), FallbackTag: "fall", }, }, Rule: []*router.RoutingRule{ { Domain: []*routercommon.Domain{ { Type: routercommon.Domain_Plain, Value: "baidu.com", }, { Type: routercommon.Domain_Plain, Value: "qq.com", }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "direct", }, }, { Domain: []*routercommon.Domain{ { Type: routercommon.Domain_Plain, Value: "v2fly.org", }, { Type: routercommon.Domain_Plain, Value: "github.com", }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "direct", }, }, { Geoip: []*routercommon.GeoIP{ { Cidr: []*routercommon.CIDR{ { Ip: []byte{10, 0, 0, 0}, Prefix: 8, }, { Ip: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Prefix: 128, }, }, }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "test", }, }, { PortList: &net.PortList{ Range: []*net.PortRange{ {From: 53, To: 53}, {From: 443, To: 443}, {From: 1000, To: 2000}, }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "test", }, }, { PortList: &net.PortList{ Range: []*net.PortRange{ {From: 123, To: 123}, }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "test", }, }, }, }, }, { Input: `{ "strategy": "rules", "settings": { "domainStrategy": "IPIfNonMatch", "rules": [ { "type": "field", "domain": [ "baidu.com", "qq.com" ], "outboundTag": "direct" }, { "type": "field", "domains": [ "v2fly.org", "github.com" ], "outboundTag": "direct" }, { "type": "field", "ip": [ "10.0.0.0/8", "::1/128" ], "outboundTag": "test" } ] } }`, Parser: createParser(), Output: &router.Config{ DomainStrategy: router.DomainStrategy_IpIfNonMatch, Rule: []*router.RoutingRule{ { Domain: []*routercommon.Domain{ { Type: routercommon.Domain_Plain, Value: "baidu.com", }, { Type: routercommon.Domain_Plain, Value: "qq.com", }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "direct", }, }, { Domain: []*routercommon.Domain{ { Type: routercommon.Domain_Plain, Value: "v2fly.org", }, { Type: routercommon.Domain_Plain, Value: "github.com", }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "direct", }, }, { Geoip: []*routercommon.GeoIP{ { Cidr: []*routercommon.CIDR{ { Ip: []byte{10, 0, 0, 0}, Prefix: 8, }, { Ip: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Prefix: 128, }, }, }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "test", }, }, }, }, }, { Input: `{ "domainStrategy": "AsIs", "rules": [ { "type": "field", "domain": [ "baidu.com", "qq.com" ], "outboundTag": "direct" }, { "type": "field", "domains": [ "v2fly.org", "github.com" ], "outboundTag": "direct" }, { "type": "field", "ip": [ "10.0.0.0/8", "::1/128" ], "outboundTag": "test" } ] }`, Parser: createParser(), Output: &router.Config{ DomainStrategy: router.DomainStrategy_AsIs, Rule: []*router.RoutingRule{ { Domain: []*routercommon.Domain{ { Type: routercommon.Domain_Plain, Value: "baidu.com", }, { Type: routercommon.Domain_Plain, Value: "qq.com", }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "direct", }, }, { Domain: []*routercommon.Domain{ { Type: routercommon.Domain_Plain, Value: "v2fly.org", }, { Type: routercommon.Domain_Plain, Value: "github.com", }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "direct", }, }, { Geoip: []*routercommon.GeoIP{ { Cidr: []*routercommon.CIDR{ { Ip: []byte{10, 0, 0, 0}, Prefix: 8, }, { Ip: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Prefix: 128, }, }, }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "test", }, }, }, }, }, }) } ================================================ FILE: infra/conf/v2jsonpb/any2.go ================================================ package v2jsonpb import ( "github.com/golang/protobuf/jsonpb" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" ) type anyresolverv2 struct { backgroundResolver jsonpb.AnyResolver } func (r anyresolverv2) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { panic("implement me") } func (r anyresolverv2) FindMessageByURL(url string) (protoreflect.MessageType, error) { msg, err := r.backgroundResolver.Resolve(url) if err != nil { return nil, err } return msg.(proto.Message).ProtoReflect().Type(), nil } func (r anyresolverv2) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { panic("implement me") } func (r anyresolverv2) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { panic("implement me") } ================================================ FILE: infra/conf/v2jsonpb/errors.generated.go ================================================ package v2jsonpb import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/v2jsonpb/follower.go ================================================ package v2jsonpb import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/anypb" "github.com/v2fly/v2ray-core/v5/common/serial" ) type V2JsonProtobufFollowerFieldDescriptor struct { protoreflect.FieldDescriptor } type V2JsonProtobufFollower struct { protoreflect.Message } func (v *V2JsonProtobufFollower) Type() protoreflect.MessageType { panic("implement me") } func (v *V2JsonProtobufFollower) New() protoreflect.Message { panic("implement me") } func (v *V2JsonProtobufFollower) Interface() protoreflect.ProtoMessage { return v.Message.Interface() } func (v *V2JsonProtobufFollower) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { v.Message.Range(func(descriptor protoreflect.FieldDescriptor, value protoreflect.Value) bool { name := descriptor.FullName() fullname := v.Message.Descriptor().FullName() if fullname == "google.protobuf.Any" { switch name { case "google.protobuf.Any.type_url": fd := V2JsonProtobufAnyTypeFieldDescriptor{descriptor} return f(fd, value) case "google.protobuf.Any.value": url := v.Message.Get(v.Message.Descriptor().Fields().ByName("type_url")).String() fd := &V2JsonProtobufAnyValueField{descriptor, url} bytesout := v.Message.Get(v.Message.Descriptor().Fields().Get(1)).Bytes() v2type := serial.V2TypeFromURL(url) instance, err := serial.GetInstance(v2type) if err != nil { panic(err) } unmarshaler := proto.UnmarshalOptions{AllowPartial: true, Resolver: anyresolverv2{backgroundResolver: serial.GetResolver()}} err = unmarshaler.Unmarshal(bytesout, instance.(proto.Message)) if err != nil { panic(err) } return f(fd, protoreflect.ValueOfMessage(&V2JsonProtobufFollower{instance.(proto.Message).ProtoReflect()})) default: panic("unexpected any value") } } return followValue(descriptor, value, f) }) } func followValue(descriptor protoreflect.FieldDescriptor, value protoreflect.Value, f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) bool { fd := V2JsonProtobufFollowerFieldDescriptor{descriptor} if descriptor.Kind() == protoreflect.MessageKind { if descriptor.IsList() { value2 := protoreflect.ValueOfList(V2JsonProtobufListFollower{value.List()}) return f(fd, value2) } if descriptor.IsMap() { value2 := protoreflect.ValueOfMap(V2JsonProtobufMapFollower{value.Map(), descriptor.MapValue()}) return f(fd, value2) } value2 := protoreflect.ValueOfMessage(&V2JsonProtobufFollower{value.Message()}) return f(fd, value2) } return f(fd, value) } func (v *V2JsonProtobufFollower) Has(descriptor protoreflect.FieldDescriptor) bool { panic("implement me") } func (v *V2JsonProtobufFollower) Clear(descriptor protoreflect.FieldDescriptor) { v.Message.Clear(descriptor) } func (v *V2JsonProtobufFollower) Set(descriptor protoreflect.FieldDescriptor, value protoreflect.Value) { switch descriptor := descriptor.(type) { case V2JsonProtobufFollowerFieldDescriptor: v.Message.Set(descriptor.FieldDescriptor, value) case *V2JsonProtobufFollowerFieldDescriptor: v.Message.Set(descriptor.FieldDescriptor, value) case *V2JsonProtobufAnyValueField: protodata := value.Message() bytesw, err := proto.MarshalOptions{AllowPartial: true}.Marshal(&V2JsonProtobufAnyValueFieldReturn{protodata}) if err != nil { panic(err) } v.Message.Set(descriptor.FieldDescriptor, protoreflect.ValueOfBytes(bytesw)) default: v.Message.Set(descriptor, value) } } func (v *V2JsonProtobufFollower) Mutable(descriptor protoreflect.FieldDescriptor) protoreflect.Value { value := v.Message.Mutable(descriptor.(V2JsonProtobufFollowerFieldDescriptor).FieldDescriptor) if descriptor.IsList() { return protoreflect.ValueOfList(&V2JsonProtobufListFollower{value.List()}) } if descriptor.IsMap() { return protoreflect.ValueOfMap(&V2JsonProtobufMapFollower{value.Map(), descriptor}) } if descriptor.Kind() == protoreflect.MessageKind { return protoreflect.ValueOfMessage(&V2JsonProtobufFollower{value.Message()}) } return value } func (v *V2JsonProtobufFollower) NewField(descriptor protoreflect.FieldDescriptor) protoreflect.Value { if _, ok := descriptor.(*V2JsonProtobufAnyValueField); ok { url := v.Message.Get(v.Message.Descriptor().Fields().ByName("type_url")).String() v2type := serial.V2TypeFromURL(url) instance, err := serial.GetInstance(v2type) if err != nil { panic(err) } newvalue := protoreflect.ValueOfMessage(&V2JsonProtobufFollower{instance.(proto.Message).ProtoReflect()}) return newvalue } value := v.Message.NewField(descriptor.(V2JsonProtobufFollowerFieldDescriptor).FieldDescriptor) newvalue := protoreflect.ValueOfMessage(&V2JsonProtobufFollower{value.Message()}) return newvalue } func (v *V2JsonProtobufFollower) WhichOneof(descriptor protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { panic("implement me") } func (v *V2JsonProtobufFollower) GetUnknown() protoreflect.RawFields { panic("implement me") } func (v *V2JsonProtobufFollower) SetUnknown(fields protoreflect.RawFields) { v.Message.SetUnknown(fields) } func (v *V2JsonProtobufFollower) IsValid() bool { return v.Message.IsValid() } func (v *V2JsonProtobufFollower) ProtoReflect() protoreflect.Message { return v } func (v *V2JsonProtobufFollower) Descriptor() protoreflect.MessageDescriptor { fullname := v.Message.Descriptor().FullName() if fullname == "google.protobuf.Any" { desc := &V2JsonProtobufAnyTypeDescriptor{(&anypb.Any{}).ProtoReflect().Descriptor()} return desc } return &V2JsonProtobufFollowerDescriptor{v.Message.Descriptor()} } func (v *V2JsonProtobufFollower) Get(fd protoreflect.FieldDescriptor) protoreflect.Value { panic("implement me") } type V2JsonProtobufFollowerDescriptor struct { protoreflect.MessageDescriptor } func (v *V2JsonProtobufFollowerDescriptor) Fields() protoreflect.FieldDescriptors { return &V2JsonProtobufFollowerFields{v.MessageDescriptor.Fields()} } type V2JsonProtobufFollowerFields struct { protoreflect.FieldDescriptors } func (v *V2JsonProtobufFollowerFields) ByJSONName(s string) protoreflect.FieldDescriptor { return V2JsonProtobufFollowerFieldDescriptor{v.FieldDescriptors.ByJSONName(s)} } func (v *V2JsonProtobufFollowerFields) ByTextName(s string) protoreflect.FieldDescriptor { return V2JsonProtobufFollowerFieldDescriptor{v.FieldDescriptors.ByTextName(s)} } ================================================ FILE: infra/conf/v2jsonpb/followerany.go ================================================ package v2jsonpb import "google.golang.org/protobuf/reflect/protoreflect" type V2JsonProtobufAnyTypeDescriptor struct { protoreflect.MessageDescriptor } func (v V2JsonProtobufAnyTypeDescriptor) FullName() protoreflect.FullName { return "org.v2fly.SynAny" } func (v V2JsonProtobufAnyTypeDescriptor) Fields() protoreflect.FieldDescriptors { return V2JsonProtobufAnyTypeFields{v.MessageDescriptor.Fields()} } type V2JsonProtobufAnyTypeFields struct { protoreflect.FieldDescriptors } func (v V2JsonProtobufAnyTypeFields) Len() int { panic("implement me") } func (v V2JsonProtobufAnyTypeFields) Get(i int) protoreflect.FieldDescriptor { panic("implement me") } func (v V2JsonProtobufAnyTypeFields) ByName(s protoreflect.Name) protoreflect.FieldDescriptor { panic("implement me") } func (v V2JsonProtobufAnyTypeFields) ByJSONName(s string) protoreflect.FieldDescriptor { switch s { case "type": return &V2JsonProtobufFollowerFieldDescriptor{v.FieldDescriptors.ByName("type_url")} default: return &V2JsonProtobufAnyValueField{v.FieldDescriptors.ByName("value"), "value"} } } func (v V2JsonProtobufAnyTypeFields) ByTextName(s string) protoreflect.FieldDescriptor { panic("implement me") } func (v V2JsonProtobufAnyTypeFields) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor { panic("implement me") } type V2JsonProtobufAnyTypeFieldDescriptor struct { protoreflect.FieldDescriptor } func (v V2JsonProtobufAnyTypeFieldDescriptor) JSONName() string { return "type" } func (v V2JsonProtobufAnyTypeFieldDescriptor) TextName() string { return "type" } type V2JsonProtobufAnyValueField struct { protoreflect.FieldDescriptor name string } func (v *V2JsonProtobufAnyValueField) Kind() protoreflect.Kind { return protoreflect.MessageKind } func (v *V2JsonProtobufAnyValueField) JSONName() string { return v.name } func (v *V2JsonProtobufAnyValueField) TextName() string { return v.name } type V2JsonProtobufAnyValueFieldReturn struct { protoreflect.Message } func (v *V2JsonProtobufAnyValueFieldReturn) ProtoReflect() protoreflect.Message { if bufFollow, ok := v.Message.(*V2JsonProtobufFollower); ok { return bufFollow.Message } return v.Message } ================================================ FILE: infra/conf/v2jsonpb/followerlist.go ================================================ package v2jsonpb import "google.golang.org/protobuf/reflect/protoreflect" type V2JsonProtobufListFollower struct { protoreflect.List } func (v V2JsonProtobufListFollower) Len() int { return v.List.Len() } func (v V2JsonProtobufListFollower) Get(i int) protoreflect.Value { return protoreflect.ValueOfMessage(&V2JsonProtobufFollower{v.List.Get(i).Message()}) } func (v V2JsonProtobufListFollower) Set(i int, value protoreflect.Value) { panic("implement me") } func (v V2JsonProtobufListFollower) Append(value protoreflect.Value) { v.List.Append(value) } func (v V2JsonProtobufListFollower) AppendMutable() protoreflect.Value { panic("implement me") } func (v V2JsonProtobufListFollower) Truncate(i int) { panic("implement me") } func (v V2JsonProtobufListFollower) NewElement() protoreflect.Value { newelement := v.List.NewElement() return protoreflect.ValueOfMessage(&V2JsonProtobufFollower{newelement.Message()}) } func (v V2JsonProtobufListFollower) IsValid() bool { panic("implement me") } ================================================ FILE: infra/conf/v2jsonpb/followermap.go ================================================ package v2jsonpb import "google.golang.org/protobuf/reflect/protoreflect" type V2JsonProtobufMapFollower struct { protoreflect.Map ValueKind protoreflect.FieldDescriptor } func (v V2JsonProtobufMapFollower) Len() int { panic("implement me") } func (v V2JsonProtobufMapFollower) Range(f func(protoreflect.MapKey, protoreflect.Value) bool) { v.Map.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool { return followMapValue(v.ValueKind, value, key, f) }) } func (v V2JsonProtobufMapFollower) Has(key protoreflect.MapKey) bool { return v.Map.Has(key) } func (v V2JsonProtobufMapFollower) Clear(key protoreflect.MapKey) { panic("implement me") } func (v V2JsonProtobufMapFollower) Get(key protoreflect.MapKey) protoreflect.Value { panic("implement me") } func (v V2JsonProtobufMapFollower) Set(key protoreflect.MapKey, value protoreflect.Value) { v.Map.Set(key, value) } func (v V2JsonProtobufMapFollower) Mutable(key protoreflect.MapKey) protoreflect.Value { panic("implement me") } func (v V2JsonProtobufMapFollower) NewValue() protoreflect.Value { newelement := v.Map.NewValue() return protoreflect.ValueOfMessage(&V2JsonProtobufFollower{newelement.Message()}) } func (v V2JsonProtobufMapFollower) IsValid() bool { panic("implement me") } func followMapValue(descriptor protoreflect.FieldDescriptor, value protoreflect.Value, mapkey protoreflect.MapKey, f func(protoreflect.MapKey, protoreflect.Value) bool) bool { if descriptor.Kind() == protoreflect.MessageKind { if descriptor.IsList() { value2 := protoreflect.ValueOfList(V2JsonProtobufListFollower{value.List()}) return f(mapkey, value2) } if descriptor.IsMap() { value2 := protoreflect.ValueOfMap(V2JsonProtobufMapFollower{value.Map(), descriptor.MapValue()}) return f(mapkey, value2) } value2 := protoreflect.ValueOfMessage(&V2JsonProtobufFollower{value.Message()}) return f(mapkey, value2) } return f(mapkey, value) } ================================================ FILE: infra/conf/v2jsonpb/v2jsonpb.go ================================================ package v2jsonpb import ( "io" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/cmdarg" "github.com/v2fly/v2ray-core/v5/common/serial" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func loadV2JsonPb(data []byte) (*core.Config, error) { coreconf := &core.Config{} jsonpbloader := &protojson.UnmarshalOptions{Resolver: anyresolverv2{serial.GetResolver()}, AllowPartial: true} err := jsonpbloader.Unmarshal(data, &V2JsonProtobufFollower{coreconf.ProtoReflect()}) if err != nil { return nil, err } return coreconf, nil } func dumpV2JsonPb(config proto.Message) ([]byte, error) { jsonpbdumper := &protojson.MarshalOptions{Resolver: anyresolverv2{serial.GetResolver()}, AllowPartial: true} bytew, err := jsonpbdumper.Marshal(&V2JsonProtobufFollower{config.ProtoReflect()}) if err != nil { return nil, err } return bytew, nil } func DumpV2JsonPb(config proto.Message) ([]byte, error) { return dumpV2JsonPb(config) } const FormatProtobufV2JSONPB = "v2jsonpb" func init() { common.Must(core.RegisterConfigLoader(&core.ConfigFormat{ Name: []string{FormatProtobufV2JSONPB}, Extension: []string{".v2pb.json", ".v2pbjson"}, Loader: func(input interface{}) (*core.Config, error) { switch v := input.(type) { case string: r, err := cmdarg.LoadArg(v) if err != nil { return nil, err } data, err := buf.ReadAllToBytes(r) if err != nil { return nil, err } return loadV2JsonPb(data) case []byte: return loadV2JsonPb(v) case io.Reader: data, err := buf.ReadAllToBytes(v) if err != nil { return nil, err } return loadV2JsonPb(data) default: return nil, newError("unknown type") } }, })) } ================================================ FILE: infra/conf/v4/api.go ================================================ package v4 import ( "strings" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/dynamic" "google.golang.org/protobuf/types/known/anypb" "github.com/v2fly/v2ray-core/v5/app/commander" loggerservice "github.com/v2fly/v2ray-core/v5/app/log/command" observatoryservice "github.com/v2fly/v2ray-core/v5/app/observatory/command" handlerservice "github.com/v2fly/v2ray-core/v5/app/proxyman/command" routerservice "github.com/v2fly/v2ray-core/v5/app/router/command" statsservice "github.com/v2fly/v2ray-core/v5/app/stats/command" "github.com/v2fly/v2ray-core/v5/common/serial" ) type APIConfig struct { Tag string `json:"tag"` Services []string `json:"services"` } func (c *APIConfig) Build() (*commander.Config, error) { if c.Tag == "" { return nil, newError("API tag can't be empty.") } services := make([]*anypb.Any, 0, 16) for _, s := range c.Services { switch strings.ToLower(s) { case "reflectionservice": services = append(services, serial.ToTypedMessage(&commander.ReflectionConfig{})) case "handlerservice": services = append(services, serial.ToTypedMessage(&handlerservice.Config{})) case "loggerservice": services = append(services, serial.ToTypedMessage(&loggerservice.Config{})) case "statsservice": services = append(services, serial.ToTypedMessage(&statsservice.Config{})) case "observatoryservice": services = append(services, serial.ToTypedMessage(&observatoryservice.Config{})) case "routingservice": services = append(services, serial.ToTypedMessage(&routerservice.Config{})) default: if !strings.HasPrefix(s, "#") { continue } message, err := desc.LoadMessageDescriptor(s[1:]) if err != nil || message == nil { return nil, newError("Cannot find API", s, "").Base(err) } serviceConfig := dynamic.NewMessage(message) services = append(services, serial.ToTypedMessage(serviceConfig)) } } return &commander.Config{ Tag: c.Tag, Service: services, }, nil } ================================================ FILE: infra/conf/v4/blackhole.go ================================================ package v4 import ( "encoding/json" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/loader" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" ) type NoneResponse struct{} func (*NoneResponse) Build() (proto.Message, error) { return new(blackhole.NoneResponse), nil } type HTTPResponse struct{} func (*HTTPResponse) Build() (proto.Message, error) { return new(blackhole.HTTPResponse), nil } type BlackholeConfig struct { Response json.RawMessage `json:"response"` } func (v *BlackholeConfig) Build() (proto.Message, error) { config := new(blackhole.Config) if v.Response != nil { response, _, err := configLoader.Load(v.Response) if err != nil { return nil, newError("Config: Failed to parse Blackhole response config.").Base(err) } responseSettings, err := response.(cfgcommon.Buildable).Build() if err != nil { return nil, err } config.Response = serial.ToTypedMessage(responseSettings) } return config, nil } var configLoader = loader.NewJSONConfigLoader( loader.ConfigCreatorCache{ "none": func() interface{} { return new(NoneResponse) }, "http": func() interface{} { return new(HTTPResponse) }, }, "type", "") ================================================ FILE: infra/conf/v4/blackhole_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" ) func TestHTTPResponseJSON(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.BlackholeConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "response": { "type": "http" } }`, Parser: testassist.LoadJSON(creator), Output: &blackhole.Config{ Response: serial.ToTypedMessage(&blackhole.HTTPResponse{}), }, }, { Input: `{}`, Parser: testassist.LoadJSON(creator), Output: &blackhole.Config{}, }, }) } ================================================ FILE: infra/conf/v4/browser_forwarder.go ================================================ package v4 import ( "strings" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/browserforwarder" ) type BrowserForwarderConfig struct { ListenAddr string `json:"listenAddr"` ListenPort int32 `json:"listenPort"` } func (b *BrowserForwarderConfig) Build() (proto.Message, error) { b.ListenAddr = strings.TrimSpace(b.ListenAddr) if b.ListenAddr != "" && b.ListenPort == 0 { b.ListenPort = 54321 } return &browserforwarder.Config{ ListenAddr: b.ListenAddr, ListenPort: b.ListenPort, }, nil } ================================================ FILE: infra/conf/v4/conf.go ================================================ package v4 //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: infra/conf/v4/dns_proxy.go ================================================ package v4 import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/dns" ) type DNSOutboundConfig struct { Network cfgcommon.Network `json:"network"` Address *cfgcommon.Address `json:"address"` Port uint16 `json:"port"` UserLevel uint32 `json:"userLevel"` } func (c *DNSOutboundConfig) Build() (proto.Message, error) { config := &dns.Config{ Server: &net.Endpoint{ Network: c.Network.Build(), Port: uint32(c.Port), }, UserLevel: c.UserLevel, } if c.Address != nil { config.Server.Address = c.Address.Build() } return config, nil } ================================================ FILE: infra/conf/v4/dns_proxy_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/dns" ) func TestDnsProxyConfig(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.DNSOutboundConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "address": "8.8.8.8", "port": 53, "network": "tcp" }`, Parser: testassist.LoadJSON(creator), Output: &dns.Config{ Server: &net.Endpoint{ Network: net.Network_TCP, Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})), Port: 53, }, }, }, }) } ================================================ FILE: infra/conf/v4/dokodemo.go ================================================ package v4 import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" ) type DokodemoConfig struct { Host *cfgcommon.Address `json:"address"` PortValue uint16 `json:"port"` NetworkList *cfgcommon.NetworkList `json:"network"` TimeoutValue uint32 `json:"timeout"` Redirect bool `json:"followRedirect"` UserLevel uint32 `json:"userLevel"` } func (v *DokodemoConfig) Build() (proto.Message, error) { config := new(dokodemo.Config) if v.Host != nil { config.Address = v.Host.Build() } config.Port = uint32(v.PortValue) config.Networks = v.NetworkList.Build() config.Timeout = v.TimeoutValue config.FollowRedirect = v.Redirect config.UserLevel = v.UserLevel return config, nil } ================================================ FILE: infra/conf/v4/dokodemo_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" ) func TestDokodemoConfig(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.DokodemoConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "address": "8.8.8.8", "port": 53, "network": "tcp", "timeout": 10, "followRedirect": true, "userLevel": 1 }`, Parser: testassist.LoadJSON(creator), Output: &dokodemo.Config{ Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{8, 8, 8, 8}, }, }, Port: 53, Networks: []net.Network{net.Network_TCP}, Timeout: 10, FollowRedirect: true, UserLevel: 1, }, }, }) } ================================================ FILE: infra/conf/v4/errors.generated.go ================================================ package v4 import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/v4/freedom.go ================================================ package v4 import ( "net" "strings" "github.com/golang/protobuf/proto" v2net "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/proxy/freedom" ) type FreedomConfig struct { DomainStrategy string `json:"domainStrategy"` Timeout *uint32 `json:"timeout"` Redirect string `json:"redirect"` UserLevel uint32 `json:"userLevel"` } // Build implements Buildable func (c *FreedomConfig) Build() (proto.Message, error) { config := new(freedom.Config) config.DomainStrategy = freedom.Config_AS_IS switch strings.ToLower(c.DomainStrategy) { case "useip", "use_ip", "use-ip": config.DomainStrategy = freedom.Config_USE_IP case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": config.DomainStrategy = freedom.Config_USE_IP4 case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": config.DomainStrategy = freedom.Config_USE_IP6 } if c.Timeout != nil { config.Timeout = *c.Timeout } config.UserLevel = c.UserLevel if len(c.Redirect) > 0 { host, portStr, err := net.SplitHostPort(c.Redirect) if err != nil { return nil, newError("invalid redirect address: ", c.Redirect, ": ", err).Base(err) } port, err := v2net.PortFromString(portStr) if err != nil { return nil, newError("invalid redirect port: ", c.Redirect, ": ", err).Base(err) } config.DestinationOverride = &freedom.DestinationOverride{ Server: &protocol.ServerEndpoint{ Port: uint32(port), }, } if len(host) > 0 { config.DestinationOverride.Server.Address = v2net.NewIPOrDomain(v2net.ParseAddress(host)) } } return config, nil } ================================================ FILE: infra/conf/v4/freedom_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/freedom" ) func TestFreedomConfig(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.FreedomConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "domainStrategy": "AsIs", "timeout": 10, "redirect": "127.0.0.1:3366", "userLevel": 1 }`, Parser: testassist.LoadJSON(creator), Output: &freedom.Config{ DomainStrategy: freedom.Config_AS_IS, Timeout: 10, DestinationOverride: &freedom.DestinationOverride{ Server: &protocol.ServerEndpoint{ Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: 3366, }, }, UserLevel: 1, }, }, }) } ================================================ FILE: infra/conf/v4/gun.go ================================================ package v4 import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/transport/internet/grpc" ) type GunConfig struct { ServiceName string `json:"serviceName"` IdleTimeout int32 `json:"idle_timeout"` HealthCheckTimeout int32 `json:"health_check_timeout"` PermitWithoutStream bool `json:"permit_without_stream"` InitialWindowsSize int32 `json:"initial_windows_size"` } func (g GunConfig) Build() (proto.Message, error) { if g.IdleTimeout <= 0 { g.IdleTimeout = 0 } if g.HealthCheckTimeout <= 0 { g.HealthCheckTimeout = 0 } if g.InitialWindowsSize < 0 { g.InitialWindowsSize = 0 } return &grpc.Config{ ServiceName: g.ServiceName, IdleTimeout: g.IdleTimeout, HealthCheckTimeout: g.HealthCheckTimeout, PermitWithoutStream: g.PermitWithoutStream, InitialWindowsSize: g.InitialWindowsSize, }, nil } ================================================ FILE: infra/conf/v4/http.go ================================================ package v4 import ( "encoding/json" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/http" ) type HTTPAccount struct { Username string `json:"user"` Password string `json:"pass"` } func (v *HTTPAccount) Build() *http.Account { return &http.Account{ Username: v.Username, Password: v.Password, } } type HTTPServerConfig struct { Timeout uint32 `json:"timeout"` Accounts []*HTTPAccount `json:"accounts"` Transparent bool `json:"allowTransparent"` UserLevel uint32 `json:"userLevel"` } func (c *HTTPServerConfig) Build() (proto.Message, error) { config := &http.ServerConfig{ Timeout: c.Timeout, AllowTransparent: c.Transparent, UserLevel: c.UserLevel, } if len(c.Accounts) > 0 { config.Accounts = make(map[string]string) for _, account := range c.Accounts { config.Accounts[account.Username] = account.Password } } return config, nil } type HTTPRemoteConfig struct { Address *cfgcommon.Address `json:"address"` Port uint16 `json:"port"` Users []json.RawMessage `json:"users"` } type HTTPClientConfig struct { Servers []*HTTPRemoteConfig `json:"servers"` } func (v *HTTPClientConfig) Build() (proto.Message, error) { config := new(http.ClientConfig) config.Server = make([]*protocol.ServerEndpoint, len(v.Servers)) for idx, serverConfig := range v.Servers { server := &protocol.ServerEndpoint{ Address: serverConfig.Address.Build(), Port: uint32(serverConfig.Port), } for _, rawUser := range serverConfig.Users { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { return nil, newError("failed to parse HTTP user").Base(err).AtError() } account := new(HTTPAccount) if err := json.Unmarshal(rawUser, account); err != nil { return nil, newError("failed to parse HTTP account").Base(err).AtError() } user.Account = serial.ToTypedMessage(account.Build()) server.User = append(server.User, user) } config.Server[idx] = server } return config, nil } ================================================ FILE: infra/conf/v4/http_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/http" ) func TestHTTPServerConfig(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.HTTPServerConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "timeout": 10, "accounts": [ { "user": "my-username", "pass": "my-password" } ], "allowTransparent": true, "userLevel": 1 }`, Parser: testassist.LoadJSON(creator), Output: &http.ServerConfig{ Accounts: map[string]string{ "my-username": "my-password", }, AllowTransparent: true, UserLevel: 1, Timeout: 10, }, }, }) } ================================================ FILE: infra/conf/v4/hysteria2.go ================================================ package v4 import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/hysteria2" ) // Hysteria2ServerTarget is configuration of a single hysteria2 server type Hysteria2ServerTarget struct { Address *cfgcommon.Address `json:"address"` Port uint16 `json:"port"` Email string `json:"email"` Level byte `json:"level"` } // Hysteria2ClientConfig is configuration of hysteria2 servers type Hysteria2ClientConfig struct { Servers []*Hysteria2ServerTarget `json:"servers"` } // Build implements Buildable func (c *Hysteria2ClientConfig) Build() (proto.Message, error) { config := new(hysteria2.ClientConfig) if len(c.Servers) == 0 { return nil, newError("0 Hysteria2 server configured.") } serverSpecs := make([]*protocol.ServerEndpoint, len(c.Servers)) for idx, rec := range c.Servers { if rec.Address == nil { return nil, newError("Hysteria2 server address is not set.") } if rec.Port == 0 { return nil, newError("Invalid Hysteria2 port.") } account := &hysteria2.Account{} hysteria2 := &protocol.ServerEndpoint{ Address: rec.Address.Build(), Port: uint32(rec.Port), User: []*protocol.User{ { Level: uint32(rec.Level), Email: rec.Email, Account: serial.ToTypedMessage(account), }, }, } serverSpecs[idx] = hysteria2 } config.Server = serverSpecs return config, nil } // Hysteria2ServerConfig is Inbound configuration type Hysteria2ServerConfig struct { PacketEncoding string `json:"packetEncoding"` } // Build implements Buildable func (c *Hysteria2ServerConfig) Build() (proto.Message, error) { config := new(hysteria2.ServerConfig) switch c.PacketEncoding { case "Packet": config.PacketEncoding = packetaddr.PacketAddrType_Packet case "", "None": config.PacketEncoding = packetaddr.PacketAddrType_None } return config, nil } ================================================ FILE: infra/conf/v4/lint.go ================================================ package v4 type ConfigureFilePostProcessingStage interface { Process(conf *Config) error } var configureFilePostProcessingStages map[string]ConfigureFilePostProcessingStage func RegisterConfigureFilePostProcessingStage(name string, stage ConfigureFilePostProcessingStage) { if configureFilePostProcessingStages == nil { configureFilePostProcessingStages = make(map[string]ConfigureFilePostProcessingStage) } configureFilePostProcessingStages[name] = stage } func PostProcessConfigureFile(conf *Config) error { for k, v := range configureFilePostProcessingStages { if err := v.Process(conf); err != nil { return newError("Rejected by Postprocessing Stage ", k).AtError().Base(err) } } return nil } ================================================ FILE: infra/conf/v4/loopback.go ================================================ package v4 import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/proxy/loopback" ) type LoopbackConfig struct { InboundTag string `json:"inboundTag"` } func (l LoopbackConfig) Build() (proto.Message, error) { return &loopback.Config{InboundTag: l.InboundTag}, nil } ================================================ FILE: infra/conf/v4/observatory.go ================================================ package v4 import ( "encoding/json" "github.com/golang/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" "github.com/v2fly/v2ray-core/v5/app/observatory" "github.com/v2fly/v2ray-core/v5/app/observatory/burst" "github.com/v2fly/v2ray-core/v5/app/observatory/multiobservatory" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/taggedfeatures" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/duration" "github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/router" ) type ObservatoryConfig struct { SubjectSelector []string `json:"subjectSelector"` ProbeURL string `json:"probeURL"` ProbeInterval duration.Duration `json:"probeInterval"` } func (o *ObservatoryConfig) Build() (proto.Message, error) { return &observatory.Config{SubjectSelector: o.SubjectSelector, ProbeUrl: o.ProbeURL, ProbeInterval: int64(o.ProbeInterval)}, nil } type BurstObservatoryConfig struct { SubjectSelector []string `json:"subjectSelector"` // health check settings HealthCheck *router.HealthCheckSettings `json:"pingConfig,omitempty"` } func (b BurstObservatoryConfig) Build() (proto.Message, error) { result, err := b.HealthCheck.Build() if err == nil { return &burst.Config{SubjectSelector: b.SubjectSelector, PingConfig: result.(*burst.HealthPingConfig)}, nil } return nil, err } type MultiObservatoryItem struct { MemberType string `json:"type"` Tag string `json:"tag"` Value json.RawMessage `json:"settings"` } type MultiObservatoryConfig struct { Observers []MultiObservatoryItem `json:"observers"` } func (o *MultiObservatoryConfig) Build() (proto.Message, error) { ret := &multiobservatory.Config{Holders: &taggedfeatures.Config{Features: make(map[string]*anypb.Any)}} for _, v := range o.Observers { switch v.MemberType { case "burst": var burstObservatoryConfig BurstObservatoryConfig err := json.Unmarshal(v.Value, &burstObservatoryConfig) if err != nil { return nil, err } burstObservatoryConfigPb, err := burstObservatoryConfig.Build() if err != nil { return nil, err } ret.Holders.Features[v.Tag] = serial.ToTypedMessage(burstObservatoryConfigPb) case "default": fallthrough default: var observatoryConfig ObservatoryConfig err := json.Unmarshal(v.Value, &observatoryConfig) if err != nil { return nil, err } observatoryConfigPb, err := observatoryConfig.Build() if err != nil { return nil, err } ret.Holders.Features[v.Tag] = serial.ToTypedMessage(observatoryConfigPb) } } return ret, nil } ================================================ FILE: infra/conf/v4/policy.go ================================================ package v4 import ( "github.com/v2fly/v2ray-core/v5/app/policy" ) type Policy struct { Handshake *uint32 `json:"handshake"` ConnectionIdle *uint32 `json:"connIdle"` UplinkOnly *uint32 `json:"uplinkOnly"` DownlinkOnly *uint32 `json:"downlinkOnly"` StatsUserUplink bool `json:"statsUserUplink"` StatsUserDownlink bool `json:"statsUserDownlink"` BufferSize *int32 `json:"bufferSize"` } func (t *Policy) Build() (*policy.Policy, error) { config := new(policy.Policy_Timeout) if t.Handshake != nil { config.Handshake = &policy.Second{Value: *t.Handshake} } if t.ConnectionIdle != nil { config.ConnectionIdle = &policy.Second{Value: *t.ConnectionIdle} } if t.UplinkOnly != nil { config.UplinkOnly = &policy.Second{Value: *t.UplinkOnly} } if t.DownlinkOnly != nil { config.DownlinkOnly = &policy.Second{Value: *t.DownlinkOnly} } p := &policy.Policy{ Timeout: config, Stats: &policy.Policy_Stats{ UserUplink: t.StatsUserUplink, UserDownlink: t.StatsUserDownlink, }, } if t.BufferSize != nil { bs := int32(-1) if *t.BufferSize >= 0 { bs = (*t.BufferSize) * 1024 } p.Buffer = &policy.Policy_Buffer{ Connection: bs, } } return p, nil } type SystemPolicy struct { StatsInboundUplink bool `json:"statsInboundUplink"` StatsInboundDownlink bool `json:"statsInboundDownlink"` StatsOutboundUplink bool `json:"statsOutboundUplink"` StatsOutboundDownlink bool `json:"statsOutboundDownlink"` OverrideAccessLogDest bool `json:"overrideAccessLogDest"` } func (p *SystemPolicy) Build() (*policy.SystemPolicy, error) { return &policy.SystemPolicy{ Stats: &policy.SystemPolicy_Stats{ InboundUplink: p.StatsInboundUplink, InboundDownlink: p.StatsInboundDownlink, OutboundUplink: p.StatsOutboundUplink, OutboundDownlink: p.StatsOutboundDownlink, }, OverrideAccessLogDest: p.OverrideAccessLogDest, }, nil } type PolicyConfig struct { Levels map[uint32]*Policy `json:"levels"` System *SystemPolicy `json:"system"` } func (c *PolicyConfig) Build() (*policy.Config, error) { levels := make(map[uint32]*policy.Policy) for l, p := range c.Levels { if p != nil { pp, err := p.Build() if err != nil { return nil, err } levels[l] = pp } } config := &policy.Config{ Level: levels, } if c.System != nil { sc, err := c.System.Build() if err != nil { return nil, err } config.System = sc } return config, nil } ================================================ FILE: infra/conf/v4/policy_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" ) func TestBufferSize(t *testing.T) { cases := []struct { Input int32 Output int32 }{ { Input: 0, Output: 0, }, { Input: -1, Output: -1, }, { Input: 1, Output: 1024, }, } for _, c := range cases { bs := c.Input pConf := v4.Policy{ BufferSize: &bs, } p, err := pConf.Build() common.Must(err) if p.Buffer.Connection != c.Output { t.Error("expected buffer size ", c.Output, " but got ", p.Buffer.Connection) } } } ================================================ FILE: infra/conf/v4/reverse.go ================================================ package v4 import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/app/reverse" ) type BridgeConfig struct { Tag string `json:"tag"` Domain string `json:"domain"` } func (c *BridgeConfig) Build() (*reverse.BridgeConfig, error) { return &reverse.BridgeConfig{ Tag: c.Tag, Domain: c.Domain, }, nil } type PortalConfig struct { Tag string `json:"tag"` Domain string `json:"domain"` } func (c *PortalConfig) Build() (*reverse.PortalConfig, error) { return &reverse.PortalConfig{ Tag: c.Tag, Domain: c.Domain, }, nil } type ReverseConfig struct { Bridges []BridgeConfig `json:"bridges"` Portals []PortalConfig `json:"portals"` } func (c *ReverseConfig) Build() (proto.Message, error) { config := &reverse.Config{} for _, bconfig := range c.Bridges { b, err := bconfig.Build() if err != nil { return nil, err } config.BridgeConfig = append(config.BridgeConfig, b) } for _, pconfig := range c.Portals { p, err := pconfig.Build() if err != nil { return nil, err } config.PortalConfig = append(config.PortalConfig, p) } return config, nil } ================================================ FILE: infra/conf/v4/reverse_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/app/reverse" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" ) func TestReverseConfig(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.ReverseConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "bridges": [{ "tag": "test", "domain": "test.v2fly.org" }] }`, Parser: testassist.LoadJSON(creator), Output: &reverse.Config{ BridgeConfig: []*reverse.BridgeConfig{ {Tag: "test", Domain: "test.v2fly.org"}, }, }, }, { Input: `{ "portals": [{ "tag": "test", "domain": "test.v2fly.org" }] }`, Parser: testassist.LoadJSON(creator), Output: &reverse.Config{ PortalConfig: []*reverse.PortalConfig{ {Tag: "test", Domain: "test.v2fly.org"}, }, }, }, }) } ================================================ FILE: infra/conf/v4/services.go ================================================ package v4 import ( "encoding/json" "github.com/golang/protobuf/jsonpb" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/dynamic" "google.golang.org/protobuf/types/known/anypb" "github.com/v2fly/v2ray-core/v5/common/serial" ) func (c *Config) BuildServices(service map[string]*json.RawMessage) ([]*anypb.Any, error) { var ret []*anypb.Any for k, v := range service { message, err := desc.LoadMessageDescriptor(k) if err != nil || message == nil { return nil, newError("Cannot find service", k, "").Base(err) } serviceConfig := dynamic.NewMessage(message) if err := serviceConfig.UnmarshalJSONPB(&jsonpb.Unmarshaler{AllowUnknownFields: false}, *v); err != nil { return nil, newError("Cannot interpret service configure file", k, "").Base(err) } ret = append(ret, serial.ToTypedMessage(serviceConfig)) } return ret, nil } ================================================ FILE: infra/conf/v4/shadowsocks.go ================================================ package v4 import ( "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" ) type ShadowsocksServerConfig struct { Cipher string `json:"method"` Password string `json:"password"` UDP bool `json:"udp"` Level byte `json:"level"` Email string `json:"email"` NetworkList *cfgcommon.NetworkList `json:"network"` IVCheck bool `json:"ivCheck"` PacketEncoding string `json:"packetEncoding"` } func (v *ShadowsocksServerConfig) Build() (proto.Message, error) { config := new(shadowsocks.ServerConfig) config.UdpEnabled = v.UDP config.Network = v.NetworkList.Build() if v.Password == "" { return nil, newError("Shadowsocks password is not specified.") } account := &shadowsocks.Account{ Password: v.Password, IvCheck: v.IVCheck, } account.CipherType = shadowsocks.CipherFromString(v.Cipher) if account.CipherType == shadowsocks.CipherType_UNKNOWN { return nil, newError("unknown cipher method: ", v.Cipher) } config.User = &protocol.User{ Email: v.Email, Level: uint32(v.Level), Account: serial.ToTypedMessage(account), } switch v.PacketEncoding { case "Packet": config.PacketEncoding = packetaddr.PacketAddrType_Packet case "", "None": config.PacketEncoding = packetaddr.PacketAddrType_None } return config, nil } type ShadowsocksServerTarget struct { Address *cfgcommon.Address `json:"address"` Port uint16 `json:"port"` Cipher string `json:"method"` Password string `json:"password"` Email string `json:"email"` Ota bool `json:"ota"` Level byte `json:"level"` IVCheck bool `json:"ivCheck"` } type ShadowsocksClientConfig struct { Servers []*ShadowsocksServerTarget `json:"servers"` } func (v *ShadowsocksClientConfig) Build() (proto.Message, error) { config := new(shadowsocks.ClientConfig) if len(v.Servers) == 0 { return nil, newError("0 Shadowsocks server configured.") } serverSpecs := make([]*protocol.ServerEndpoint, len(v.Servers)) for idx, server := range v.Servers { if server.Address == nil { return nil, newError("Shadowsocks server address is not set.") } if server.Port == 0 { return nil, newError("Invalid Shadowsocks port.") } if server.Password == "" { return nil, newError("Shadowsocks password is not specified.") } account := &shadowsocks.Account{ Password: server.Password, } account.CipherType = shadowsocks.CipherFromString(server.Cipher) if account.CipherType == shadowsocks.CipherType_UNKNOWN { return nil, newError("unknown cipher method: ", server.Cipher) } account.IvCheck = server.IVCheck ss := &protocol.ServerEndpoint{ Address: server.Address.Build(), Port: uint32(server.Port), User: []*protocol.User{ { Level: uint32(server.Level), Email: server.Email, Account: serial.ToTypedMessage(account), }, }, } serverSpecs[idx] = ss } config.Server = serverSpecs return config, nil } ================================================ FILE: infra/conf/v4/shadowsocks_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" ) func TestShadowsocksServerConfigParsing(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.ShadowsocksServerConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "method": "aes-256-GCM", "password": "v2ray-password" }`, Parser: testassist.LoadJSON(creator), Output: &shadowsocks.ServerConfig{ User: &protocol.User{ Account: serial.ToTypedMessage(&shadowsocks.Account{ CipherType: shadowsocks.CipherType_AES_256_GCM, Password: "v2ray-password", }), }, Network: []net.Network{net.Network_TCP}, }, }, }) } ================================================ FILE: infra/conf/v4/socks.go ================================================ package v4 import ( "encoding/json" "strings" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/socks" ) type SocksAccount struct { Username string `json:"user"` Password string `json:"pass"` } func (v *SocksAccount) Build() *socks.Account { return &socks.Account{ Username: v.Username, Password: v.Password, } } const ( AuthMethodNoAuth = "noauth" AuthMethodUserPass = "password" ) type SocksServerConfig struct { AuthMethod string `json:"auth"` Accounts []*SocksAccount `json:"accounts"` UDP bool `json:"udp"` Host *cfgcommon.Address `json:"ip"` Timeout uint32 `json:"timeout"` UserLevel uint32 `json:"userLevel"` PacketEncoding string `json:"packetEncoding"` } func (v *SocksServerConfig) Build() (proto.Message, error) { config := new(socks.ServerConfig) switch v.AuthMethod { case AuthMethodNoAuth: config.AuthType = socks.AuthType_NO_AUTH case AuthMethodUserPass: config.AuthType = socks.AuthType_PASSWORD default: // newError("unknown socks auth method: ", v.AuthMethod, ". Default to noauth.").AtWarning().WriteToLog() config.AuthType = socks.AuthType_NO_AUTH } if len(v.Accounts) > 0 { config.Accounts = make(map[string]string, len(v.Accounts)) for _, account := range v.Accounts { config.Accounts[account.Username] = account.Password } } config.UdpEnabled = v.UDP if v.Host != nil { config.Address = v.Host.Build() } config.Timeout = v.Timeout config.UserLevel = v.UserLevel switch v.PacketEncoding { case "Packet": config.PacketEncoding = packetaddr.PacketAddrType_Packet case "", "None": config.PacketEncoding = packetaddr.PacketAddrType_None } return config, nil } type SocksRemoteConfig struct { Address *cfgcommon.Address `json:"address"` Port uint16 `json:"port"` Users []json.RawMessage `json:"users"` } type SocksClientConfig struct { Servers []*SocksRemoteConfig `json:"servers"` Version string `json:"version"` } func (v *SocksClientConfig) Build() (proto.Message, error) { config := new(socks.ClientConfig) config.Server = make([]*protocol.ServerEndpoint, len(v.Servers)) switch strings.ToLower(v.Version) { case "4": config.Version = socks.Version_SOCKS4 case "4a": config.Version = socks.Version_SOCKS4A case "", "5": config.Version = socks.Version_SOCKS5 default: return nil, newError("failed to parse socks server version: ", v.Version).AtError() } for idx, serverConfig := range v.Servers { server := &protocol.ServerEndpoint{ Address: serverConfig.Address.Build(), Port: uint32(serverConfig.Port), } for _, rawUser := range serverConfig.Users { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { return nil, newError("failed to parse Socks user").Base(err).AtError() } account := new(SocksAccount) if err := json.Unmarshal(rawUser, account); err != nil { return nil, newError("failed to parse socks account").Base(err).AtError() } if config.Version != socks.Version_SOCKS5 && account.Password != "" { return nil, newError("password is only supported in socks5").AtError() } user.Account = serial.ToTypedMessage(account.Build()) server.User = append(server.User, user) } config.Server[idx] = server } return config, nil } ================================================ FILE: infra/conf/v4/socks_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/socks" ) func TestSocksInboundConfig(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.SocksServerConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "auth": "password", "accounts": [ { "user": "my-username", "pass": "my-password" } ], "udp": false, "ip": "127.0.0.1", "timeout": 5, "userLevel": 1, "packetEncoding": "Packet" }`, Parser: testassist.LoadJSON(creator), Output: &socks.ServerConfig{ AuthType: socks.AuthType_PASSWORD, Accounts: map[string]string{ "my-username": "my-password", }, UdpEnabled: false, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Timeout: 5, UserLevel: 1, PacketEncoding: packetaddr.PacketAddrType_Packet, }, }, }) } func TestSocksOutboundConfig(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.SocksClientConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "servers": [{ "address": "127.0.0.1", "port": 1234, "users": [ {"user": "test user", "pass": "test pass", "email": "test@email.com"} ] }] }`, Parser: testassist.LoadJSON(creator), Output: &socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: 1234, User: []*protocol.User{ { Email: "test@email.com", Account: serial.ToTypedMessage(&socks.Account{ Username: "test user", Password: "test pass", }), }, }, }, }, }, }, }) } ================================================ FILE: infra/conf/v4/transport.go ================================================ package v4 import ( "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type TransportConfig struct { TCPConfig *TCPConfig `json:"tcpSettings"` KCPConfig *KCPConfig `json:"kcpSettings"` WSConfig *WebSocketConfig `json:"wsSettings"` HTTPConfig *HTTPConfig `json:"httpSettings"` DSConfig *DomainSocketConfig `json:"dsSettings"` QUICConfig *QUICConfig `json:"quicSettings"` GunConfig *GunConfig `json:"gunSettings"` GRPCConfig *GunConfig `json:"grpcSettings"` } // Build implements Buildable. func (c *TransportConfig) Build() (*transport.Config, error) { config := new(transport.Config) if c.TCPConfig != nil { ts, err := c.TCPConfig.Build() if err != nil { return nil, newError("failed to build TCP config").Base(err).AtError() } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "tcp", Settings: serial.ToTypedMessage(ts), }) } if c.KCPConfig != nil { ts, err := c.KCPConfig.Build() if err != nil { return nil, newError("failed to build mKCP config").Base(err).AtError() } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "mkcp", Settings: serial.ToTypedMessage(ts), }) } if c.WSConfig != nil { ts, err := c.WSConfig.Build() if err != nil { return nil, newError("failed to build WebSocket config").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "websocket", Settings: serial.ToTypedMessage(ts), }) } if c.HTTPConfig != nil { ts, err := c.HTTPConfig.Build() if err != nil { return nil, newError("Failed to build HTTP config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "http", Settings: serial.ToTypedMessage(ts), }) } if c.DSConfig != nil { ds, err := c.DSConfig.Build() if err != nil { return nil, newError("Failed to build DomainSocket config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "domainsocket", Settings: serial.ToTypedMessage(ds), }) } if c.QUICConfig != nil { qs, err := c.QUICConfig.Build() if err != nil { return nil, newError("Failed to build QUIC config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "quic", Settings: serial.ToTypedMessage(qs), }) } if c.GunConfig == nil { c.GunConfig = c.GRPCConfig } if c.GunConfig != nil { gs, err := c.GunConfig.Build() if err != nil { return nil, newError("Failed to build Gun config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "gun", Settings: serial.ToTypedMessage(gs), }) } return config, nil } ================================================ FILE: infra/conf/v4/transport_authenticators.go ================================================ package v4 import ( "sort" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/noop" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/srtp" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/tls" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/utp" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wechat" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wireguard" ) type NoOpAuthenticator struct{} func (NoOpAuthenticator) Build() (proto.Message, error) { return new(noop.Config), nil } type NoOpConnectionAuthenticator struct{} func (NoOpConnectionAuthenticator) Build() (proto.Message, error) { return new(noop.ConnectionConfig), nil } type SRTPAuthenticator struct{} func (SRTPAuthenticator) Build() (proto.Message, error) { return new(srtp.Config), nil } type UTPAuthenticator struct{} func (UTPAuthenticator) Build() (proto.Message, error) { return new(utp.Config), nil } type WechatVideoAuthenticator struct{} func (WechatVideoAuthenticator) Build() (proto.Message, error) { return new(wechat.VideoConfig), nil } type WireguardAuthenticator struct{} func (WireguardAuthenticator) Build() (proto.Message, error) { return new(wireguard.WireguardConfig), nil } type DTLSAuthenticator struct{} func (DTLSAuthenticator) Build() (proto.Message, error) { return new(tls.PacketConfig), nil } type AuthenticatorRequest struct { Version string `json:"version"` Method string `json:"method"` Path cfgcommon.StringList `json:"path"` Headers map[string]*cfgcommon.StringList `json:"headers"` } func sortMapKeys(m map[string]*cfgcommon.StringList) []string { var keys []string for key := range m { keys = append(keys, key) } sort.Strings(keys) return keys } func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) { config := &http.RequestConfig{ Uri: []string{"/"}, Header: []*http.Header{ { Name: "Host", Value: []string{"www.baidu.com", "www.bing.com"}, }, { Name: "User-Agent", Value: []string{ "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46", }, }, { Name: "Accept-Encoding", Value: []string{"gzip, deflate"}, }, { Name: "Connection", Value: []string{"keep-alive"}, }, { Name: "Pragma", Value: []string{"no-cache"}, }, }, } if len(v.Version) > 0 { config.Version = &http.Version{Value: v.Version} } if len(v.Method) > 0 { config.Method = &http.Method{Value: v.Method} } if len(v.Path) > 0 { config.Uri = append([]string(nil), (v.Path)...) } if len(v.Headers) > 0 { config.Header = make([]*http.Header, 0, len(v.Headers)) headerNames := sortMapKeys(v.Headers) for _, key := range headerNames { value := v.Headers[key] if value == nil { return nil, newError("empty HTTP header value: " + key).AtError() } config.Header = append(config.Header, &http.Header{ Name: key, Value: append([]string(nil), (*value)...), }) } } return config, nil } type AuthenticatorResponse struct { Version string `json:"version"` Status string `json:"status"` Reason string `json:"reason"` Headers map[string]*cfgcommon.StringList `json:"headers"` } func (v *AuthenticatorResponse) Build() (*http.ResponseConfig, error) { config := &http.ResponseConfig{ Header: []*http.Header{ { Name: "Content-Type", Value: []string{"application/octet-stream", "video/mpeg"}, }, { Name: "Transfer-Encoding", Value: []string{"chunked"}, }, { Name: "Connection", Value: []string{"keep-alive"}, }, { Name: "Pragma", Value: []string{"no-cache"}, }, { Name: "Cache-Control", Value: []string{"private", "no-cache"}, }, }, } if len(v.Version) > 0 { config.Version = &http.Version{Value: v.Version} } if len(v.Status) > 0 || len(v.Reason) > 0 { config.Status = &http.Status{ Code: "200", Reason: "OK", } if len(v.Status) > 0 { config.Status.Code = v.Status } if len(v.Reason) > 0 { config.Status.Reason = v.Reason } } if len(v.Headers) > 0 { config.Header = make([]*http.Header, 0, len(v.Headers)) headerNames := sortMapKeys(v.Headers) for _, key := range headerNames { value := v.Headers[key] if value == nil { return nil, newError("empty HTTP header value: " + key).AtError() } config.Header = append(config.Header, &http.Header{ Name: key, Value: append([]string(nil), (*value)...), }) } } return config, nil } type Authenticator struct { Request AuthenticatorRequest `json:"request"` Response AuthenticatorResponse `json:"response"` } func (v *Authenticator) Build() (proto.Message, error) { config := new(http.Config) requestConfig, err := v.Request.Build() if err != nil { return nil, err } config.Request = requestConfig responseConfig, err := v.Response.Build() if err != nil { return nil, err } config.Response = responseConfig return config, nil } ================================================ FILE: infra/conf/v4/transport_internet.go ================================================ package v4 import ( "encoding/json" "strings" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/loader" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/socketcfg" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/tlscfg" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket" httpheader "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http" "github.com/v2fly/v2ray-core/v5/transport/internet/http" "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2" "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" "github.com/v2fly/v2ray-core/v5/transport/internet/quic" "github.com/v2fly/v2ray-core/v5/transport/internet/tcp" "github.com/v2fly/v2ray-core/v5/transport/internet/websocket" ) var ( kcpHeaderLoader = loader.NewJSONConfigLoader(loader.ConfigCreatorCache{ "none": func() interface{} { return new(NoOpAuthenticator) }, "srtp": func() interface{} { return new(SRTPAuthenticator) }, "utp": func() interface{} { return new(UTPAuthenticator) }, "wechat-video": func() interface{} { return new(WechatVideoAuthenticator) }, "dtls": func() interface{} { return new(DTLSAuthenticator) }, "wireguard": func() interface{} { return new(WireguardAuthenticator) }, }, "type", "") tcpHeaderLoader = loader.NewJSONConfigLoader(loader.ConfigCreatorCache{ "none": func() interface{} { return new(NoOpConnectionAuthenticator) }, "http": func() interface{} { return new(Authenticator) }, }, "type", "") ) type KCPConfig struct { Mtu *uint32 `json:"mtu"` Tti *uint32 `json:"tti"` UpCap *uint32 `json:"uplinkCapacity"` DownCap *uint32 `json:"downlinkCapacity"` Congestion *bool `json:"congestion"` ReadBufferSize *uint32 `json:"readBufferSize"` WriteBufferSize *uint32 `json:"writeBufferSize"` HeaderConfig json.RawMessage `json:"header"` Seed *string `json:"seed"` } // Build implements Buildable. func (c *KCPConfig) Build() (proto.Message, error) { config := new(kcp.Config) if c.Mtu != nil { mtu := *c.Mtu if mtu < 576 || mtu > 1460 { return nil, newError("invalid mKCP MTU size: ", mtu).AtError() } config.Mtu = &kcp.MTU{Value: mtu} } if c.Tti != nil { tti := *c.Tti if tti < 10 || tti > 100 { return nil, newError("invalid mKCP TTI: ", tti).AtError() } config.Tti = &kcp.TTI{Value: tti} } if c.UpCap != nil { config.UplinkCapacity = &kcp.UplinkCapacity{Value: *c.UpCap} } if c.DownCap != nil { config.DownlinkCapacity = &kcp.DownlinkCapacity{Value: *c.DownCap} } if c.Congestion != nil { config.Congestion = *c.Congestion } if c.ReadBufferSize != nil { size := *c.ReadBufferSize if size > 0 { config.ReadBuffer = &kcp.ReadBuffer{Size: size * 1024 * 1024} } else { config.ReadBuffer = &kcp.ReadBuffer{Size: 512 * 1024} } } if c.WriteBufferSize != nil { size := *c.WriteBufferSize if size > 0 { config.WriteBuffer = &kcp.WriteBuffer{Size: size * 1024 * 1024} } else { config.WriteBuffer = &kcp.WriteBuffer{Size: 512 * 1024} } } if len(c.HeaderConfig) > 0 { headerConfig, _, err := kcpHeaderLoader.Load(c.HeaderConfig) if err != nil { return nil, newError("invalid mKCP header config.").Base(err).AtError() } ts, err := headerConfig.(cfgcommon.Buildable).Build() if err != nil { return nil, newError("invalid mKCP header config").Base(err).AtError() } config.HeaderConfig = serial.ToTypedMessage(ts) } if c.Seed != nil { config.Seed = &kcp.EncryptionSeed{Seed: *c.Seed} } return config, nil } type TCPConfig struct { HeaderConfig json.RawMessage `json:"header"` AcceptProxyProtocol bool `json:"acceptProxyProtocol"` } // Build implements Buildable. func (c *TCPConfig) Build() (proto.Message, error) { config := new(tcp.Config) if len(c.HeaderConfig) > 0 { headerConfig, _, err := tcpHeaderLoader.Load(c.HeaderConfig) if err != nil { return nil, newError("invalid TCP header config").Base(err).AtError() } ts, err := headerConfig.(cfgcommon.Buildable).Build() if err != nil { return nil, newError("invalid TCP header config").Base(err).AtError() } config.HeaderSettings = serial.ToTypedMessage(ts) } if c.AcceptProxyProtocol { config.AcceptProxyProtocol = c.AcceptProxyProtocol } return config, nil } type Hy2ConfigCongestion struct { Type string `json:"type"` UpMbps uint64 `json:"up_mbps"` DownMbps uint64 `json:"down_mbps"` } type Hy2Config struct { Password string `json:"password"` Congestion Hy2ConfigCongestion `json:"congestion"` UseUDPExtension bool `json:"use_udp_extension"` IgnoreClientBandwidth bool `json:"ignore_client_bandwidth"` } // Build implements Buildable. func (c *Hy2Config) Build() (proto.Message, error) { return &hysteria2.Config{ Password: c.Password, Congestion: &hysteria2.Congestion{ Type: c.Congestion.Type, DownMbps: c.Congestion.DownMbps, UpMbps: c.Congestion.UpMbps, }, UseUdpExtension: c.UseUDPExtension, IgnoreClientBandwidth: c.IgnoreClientBandwidth, }, nil } type WebSocketConfig struct { Path string `json:"path"` Headers map[string]string `json:"headers"` AcceptProxyProtocol bool `json:"acceptProxyProtocol"` MaxEarlyData int32 `json:"maxEarlyData"` UseBrowserForwarding bool `json:"useBrowserForwarding"` EarlyDataHeaderName string `json:"earlyDataHeaderName"` } // Build implements Buildable. func (c *WebSocketConfig) Build() (proto.Message, error) { path := c.Path header := make([]*websocket.Header, 0, 32) for key, value := range c.Headers { header = append(header, &websocket.Header{ Key: key, Value: value, }) } config := &websocket.Config{ Path: path, Header: header, MaxEarlyData: c.MaxEarlyData, UseBrowserForwarding: c.UseBrowserForwarding, EarlyDataHeaderName: c.EarlyDataHeaderName, } if c.AcceptProxyProtocol { config.AcceptProxyProtocol = c.AcceptProxyProtocol } return config, nil } type HTTPConfig struct { Host *cfgcommon.StringList `json:"host"` Path string `json:"path"` Method string `json:"method"` Headers map[string]*cfgcommon.StringList `json:"headers"` } // Build implements Buildable. func (c *HTTPConfig) Build() (proto.Message, error) { config := &http.Config{ Path: c.Path, } if c.Host != nil { config.Host = []string(*c.Host) } if c.Method != "" { config.Method = c.Method } if len(c.Headers) > 0 { config.Header = make([]*httpheader.Header, 0, len(c.Headers)) headerNames := sortMapKeys(c.Headers) for _, key := range headerNames { value := c.Headers[key] if value == nil { return nil, newError("empty HTTP header value: " + key).AtError() } config.Header = append(config.Header, &httpheader.Header{ Name: key, Value: append([]string(nil), (*value)...), }) } } return config, nil } type QUICConfig struct { Header json.RawMessage `json:"header"` Security string `json:"security"` Key string `json:"key"` } // Build implements Buildable. func (c *QUICConfig) Build() (proto.Message, error) { config := &quic.Config{ Key: c.Key, } if len(c.Header) > 0 { headerConfig, _, err := kcpHeaderLoader.Load(c.Header) if err != nil { return nil, newError("invalid QUIC header config.").Base(err).AtError() } ts, err := headerConfig.(cfgcommon.Buildable).Build() if err != nil { return nil, newError("invalid QUIC header config").Base(err).AtError() } config.Header = serial.ToTypedMessage(ts) } var st protocol.SecurityType switch strings.ToLower(c.Security) { case "aes-128-gcm": st = protocol.SecurityType_AES128_GCM case "chacha20-poly1305": st = protocol.SecurityType_CHACHA20_POLY1305 default: st = protocol.SecurityType_NONE } config.Security = &protocol.SecurityConfig{ Type: st, } return config, nil } type DomainSocketConfig struct { Path string `json:"path"` Abstract bool `json:"abstract"` Padding bool `json:"padding"` } // Build implements Buildable. func (c *DomainSocketConfig) Build() (proto.Message, error) { return &domainsocket.Config{ Path: c.Path, Abstract: c.Abstract, Padding: c.Padding, }, nil } type TransportProtocol string // Build implements Buildable. func (p TransportProtocol) Build() (string, error) { switch strings.ToLower(string(p)) { case "tcp": return "tcp", nil case "kcp", "mkcp": return "mkcp", nil case "ws", "websocket": return "websocket", nil case "h2", "http": return "http", nil case "ds", "domainsocket": return "domainsocket", nil case "quic": return "quic", nil case "gun", "grpc": return "gun", nil case "hy2", "hysteria2": return "hysteria2", nil default: return "", newError("Config: unknown transport protocol: ", p) } } type StreamConfig struct { Network *TransportProtocol `json:"network"` Security string `json:"security"` TLSSettings *tlscfg.TLSConfig `json:"tlsSettings"` TCPSettings *TCPConfig `json:"tcpSettings"` KCPSettings *KCPConfig `json:"kcpSettings"` WSSettings *WebSocketConfig `json:"wsSettings"` HTTPSettings *HTTPConfig `json:"httpSettings"` DSSettings *DomainSocketConfig `json:"dsSettings"` QUICSettings *QUICConfig `json:"quicSettings"` GunSettings *GunConfig `json:"gunSettings"` GRPCSettings *GunConfig `json:"grpcSettings"` Hy2Settings *Hy2Config `json:"hy2Settings"` SocketSettings *socketcfg.SocketConfig `json:"sockopt"` } // Build implements Buildable. func (c *StreamConfig) Build() (*internet.StreamConfig, error) { config := &internet.StreamConfig{ ProtocolName: "tcp", } if c.Network != nil { protocol, err := c.Network.Build() if err != nil { return nil, err } config.ProtocolName = protocol } if strings.EqualFold(c.Security, "tls") { tlsSettings := c.TLSSettings if tlsSettings == nil { tlsSettings = &tlscfg.TLSConfig{} } ts, err := tlsSettings.Build() if err != nil { return nil, newError("Failed to build TLS config.").Base(err) } tm := serial.ToTypedMessage(ts) config.SecuritySettings = append(config.SecuritySettings, tm) config.SecurityType = serial.V2Type(tm) } if c.TCPSettings != nil { ts, err := c.TCPSettings.Build() if err != nil { return nil, newError("Failed to build TCP config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "tcp", Settings: serial.ToTypedMessage(ts), }) } if c.KCPSettings != nil { ts, err := c.KCPSettings.Build() if err != nil { return nil, newError("Failed to build mKCP config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "mkcp", Settings: serial.ToTypedMessage(ts), }) } if c.WSSettings != nil { ts, err := c.WSSettings.Build() if err != nil { return nil, newError("Failed to build WebSocket config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "websocket", Settings: serial.ToTypedMessage(ts), }) } if c.HTTPSettings != nil { ts, err := c.HTTPSettings.Build() if err != nil { return nil, newError("Failed to build HTTP config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "http", Settings: serial.ToTypedMessage(ts), }) } if c.DSSettings != nil { ds, err := c.DSSettings.Build() if err != nil { return nil, newError("Failed to build DomainSocket config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "domainsocket", Settings: serial.ToTypedMessage(ds), }) } if c.QUICSettings != nil { qs, err := c.QUICSettings.Build() if err != nil { return nil, newError("Failed to build QUIC config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "quic", Settings: serial.ToTypedMessage(qs), }) } if c.GunSettings == nil { c.GunSettings = c.GRPCSettings } if c.GunSettings != nil { gs, err := c.GunSettings.Build() if err != nil { return nil, newError("Failed to build Gun config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "gun", Settings: serial.ToTypedMessage(gs), }) } if c.Hy2Settings != nil { hy2, err := c.Hy2Settings.Build() if err != nil { return nil, newError("Failed to build hy2 config.").Base(err) } config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: "hysteria2", Settings: serial.ToTypedMessage(hy2), }) } if c.SocketSettings != nil { ss, err := c.SocketSettings.Build() if err != nil { return nil, newError("Failed to build sockopt.").Base(err) } config.SocketSettings = ss } return config, nil } ================================================ FILE: infra/conf/v4/transport_test.go ================================================ package v4_test import ( "encoding/json" "testing" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/socketcfg" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/noop" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/tls" "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" "github.com/v2fly/v2ray-core/v5/transport/internet/quic" "github.com/v2fly/v2ray-core/v5/transport/internet/tcp" "github.com/v2fly/v2ray-core/v5/transport/internet/websocket" ) func TestSocketConfig(t *testing.T) { createParser := func() func(string) (proto.Message, error) { return func(s string) (proto.Message, error) { config := new(socketcfg.SocketConfig) if err := json.Unmarshal([]byte(s), config); err != nil { return nil, err } return config.Build() } } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "mark": 1, "tcpFastOpen": true, "tcpFastOpenQueueLength": 1024, "mptcp": true }`, Parser: createParser(), Output: &internet.SocketConfig{ Mark: 1, Tfo: internet.SocketConfig_Enable, TfoQueueLength: 1024, Mptcp: internet.MPTCPState_Enable, }, }, }) } func TestTransportConfig(t *testing.T) { createParser := func() func(string) (proto.Message, error) { return func(s string) (proto.Message, error) { config := new(v4.TransportConfig) if err := json.Unmarshal([]byte(s), config); err != nil { return nil, err } return config.Build() } } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "tcpSettings": { "header": { "type": "http", "request": { "version": "1.1", "method": "GET", "path": "/b", "headers": { "a": "b", "c": "d" } }, "response": { "version": "1.0", "status": "404", "reason": "Not Found" } } }, "kcpSettings": { "mtu": 1200, "header": { "type": "none" } }, "wsSettings": { "path": "/t" }, "quicSettings": { "key": "abcd", "header": { "type": "dtls" } } }`, Parser: createParser(), Output: &transport.Config{ TransportSettings: []*internet.TransportConfig{ { ProtocolName: "tcp", Settings: serial.ToTypedMessage(&tcp.Config{ HeaderSettings: serial.ToTypedMessage(&http.Config{ Request: &http.RequestConfig{ Version: &http.Version{Value: "1.1"}, Method: &http.Method{Value: "GET"}, Uri: []string{"/b"}, Header: []*http.Header{ {Name: "a", Value: []string{"b"}}, {Name: "c", Value: []string{"d"}}, }, }, Response: &http.ResponseConfig{ Version: &http.Version{Value: "1.0"}, Status: &http.Status{Code: "404", Reason: "Not Found"}, Header: []*http.Header{ { Name: "Content-Type", Value: []string{"application/octet-stream", "video/mpeg"}, }, { Name: "Transfer-Encoding", Value: []string{"chunked"}, }, { Name: "Connection", Value: []string{"keep-alive"}, }, { Name: "Pragma", Value: []string{"no-cache"}, }, { Name: "Cache-Control", Value: []string{"private", "no-cache"}, }, }, }, }), }), }, { ProtocolName: "mkcp", Settings: serial.ToTypedMessage(&kcp.Config{ Mtu: &kcp.MTU{Value: 1200}, HeaderConfig: serial.ToTypedMessage(&noop.Config{}), }), }, { ProtocolName: "websocket", Settings: serial.ToTypedMessage(&websocket.Config{ Path: "/t", }), }, { ProtocolName: "quic", Settings: serial.ToTypedMessage(&quic.Config{ Key: "abcd", Security: &protocol.SecurityConfig{ Type: protocol.SecurityType_NONE, }, Header: serial.ToTypedMessage(&tls.PacketConfig{}), }), }, }, }, }, }) } ================================================ FILE: infra/conf/v4/trojan.go ================================================ package v4 import ( "encoding/json" "path/filepath" "runtime" "strconv" "strings" "syscall" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/trojan" ) // TrojanServerTarget is configuration of a single trojan server type TrojanServerTarget struct { Address *cfgcommon.Address `json:"address"` Port uint16 `json:"port"` Password string `json:"password"` Email string `json:"email"` Level byte `json:"level"` } // TrojanClientConfig is configuration of trojan servers type TrojanClientConfig struct { Servers []*TrojanServerTarget `json:"servers"` } // Build implements Buildable func (c *TrojanClientConfig) Build() (proto.Message, error) { config := new(trojan.ClientConfig) if len(c.Servers) == 0 { return nil, newError("0 Trojan server configured.") } serverSpecs := make([]*protocol.ServerEndpoint, len(c.Servers)) for idx, rec := range c.Servers { if rec.Address == nil { return nil, newError("Trojan server address is not set.") } if rec.Port == 0 { return nil, newError("Invalid Trojan port.") } if rec.Password == "" { return nil, newError("Trojan password is not specified.") } account := &trojan.Account{ Password: rec.Password, } trojan := &protocol.ServerEndpoint{ Address: rec.Address.Build(), Port: uint32(rec.Port), User: []*protocol.User{ { Level: uint32(rec.Level), Email: rec.Email, Account: serial.ToTypedMessage(account), }, }, } serverSpecs[idx] = trojan } config.Server = serverSpecs return config, nil } // TrojanInboundFallback is fallback configuration type TrojanInboundFallback struct { Alpn string `json:"alpn"` Path string `json:"path"` Type string `json:"type"` Dest json.RawMessage `json:"dest"` Xver uint64 `json:"xver"` } // TrojanUserConfig is user configuration type TrojanUserConfig struct { Password string `json:"password"` Level byte `json:"level"` Email string `json:"email"` } // TrojanServerConfig is Inbound configuration type TrojanServerConfig struct { Clients []*TrojanUserConfig `json:"clients"` Fallback json.RawMessage `json:"fallback"` Fallbacks []*TrojanInboundFallback `json:"fallbacks"` PacketEncoding string `json:"packetEncoding"` } // Build implements Buildable func (c *TrojanServerConfig) Build() (proto.Message, error) { config := new(trojan.ServerConfig) config.Users = make([]*protocol.User, len(c.Clients)) for idx, rawUser := range c.Clients { user := new(protocol.User) account := &trojan.Account{ Password: rawUser.Password, } user.Email = rawUser.Email user.Level = uint32(rawUser.Level) user.Account = serial.ToTypedMessage(account) config.Users[idx] = user } if c.Fallback != nil { return nil, newError(`Trojan settings: please use "fallbacks":[{}] instead of "fallback":{}`) } for _, fb := range c.Fallbacks { var i uint16 var s string if err := json.Unmarshal(fb.Dest, &i); err == nil { s = strconv.Itoa(int(i)) } else { _ = json.Unmarshal(fb.Dest, &s) } config.Fallbacks = append(config.Fallbacks, &trojan.Fallback{ Alpn: fb.Alpn, Path: fb.Path, Type: fb.Type, Dest: s, Xver: fb.Xver, }) } for _, fb := range config.Fallbacks { /* if fb.Alpn == "h2" && fb.Path != "" { return nil, newError(`Trojan fallbacks: "alpn":"h2" doesn't support "path"`) } */ if fb.Path != "" && fb.Path[0] != '/' { return nil, newError(`Trojan fallbacks: "path" must be empty or start with "/"`) } if fb.Type == "" && fb.Dest != "" { if fb.Dest == "serve-ws-none" { // nolint:gocritic fb.Type = "serve" } else if filepath.IsAbs(fb.Dest) || fb.Dest[0] == '@' { fb.Type = "unix" if strings.HasPrefix(fb.Dest, "@@") && (runtime.GOOS == "linux" || runtime.GOOS == "android") { fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy copy(fullAddr, fb.Dest[1:]) fb.Dest = string(fullAddr) } } else { if _, err := strconv.Atoi(fb.Dest); err == nil { fb.Dest = "127.0.0.1:" + fb.Dest } if _, _, err := net.SplitHostPort(fb.Dest); err == nil { fb.Type = "tcp" } } } if fb.Type == "" { return nil, newError(`Trojan fallbacks: please fill in a valid value for every "dest"`) } if fb.Xver > 2 { return nil, newError(`Trojan fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) } } switch c.PacketEncoding { case "Packet": config.PacketEncoding = packetaddr.PacketAddrType_Packet case "", "None": config.PacketEncoding = packetaddr.PacketAddrType_None } return config, nil } ================================================ FILE: infra/conf/v4/v2ray.go ================================================ package v4 import ( "context" "encoding/json" "fmt" "path/filepath" "strings" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/stats" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/loader" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/muxcfg" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/proxycfg" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/sniffer" "github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/dns" "github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/log" "github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/router" "github.com/v2fly/v2ray-core/v5/infra/conf/v5cfg" ) var ( inboundConfigLoader = loader.NewJSONConfigLoader(loader.ConfigCreatorCache{ "dokodemo-door": func() interface{} { return new(DokodemoConfig) }, "http": func() interface{} { return new(HTTPServerConfig) }, "shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) }, "socks": func() interface{} { return new(SocksServerConfig) }, "vless": func() interface{} { return new(VLessInboundConfig) }, "vmess": func() interface{} { return new(VMessInboundConfig) }, "trojan": func() interface{} { return new(TrojanServerConfig) }, "hysteria2": func() interface{} { return new(Hysteria2ServerConfig) }, }, "protocol", "settings") outboundConfigLoader = loader.NewJSONConfigLoader(loader.ConfigCreatorCache{ "blackhole": func() interface{} { return new(BlackholeConfig) }, "freedom": func() interface{} { return new(FreedomConfig) }, "http": func() interface{} { return new(HTTPClientConfig) }, "shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) }, "socks": func() interface{} { return new(SocksClientConfig) }, "vless": func() interface{} { return new(VLessOutboundConfig) }, "vmess": func() interface{} { return new(VMessOutboundConfig) }, "trojan": func() interface{} { return new(TrojanClientConfig) }, "hysteria2": func() interface{} { return new(Hysteria2ClientConfig) }, "dns": func() interface{} { return new(DNSOutboundConfig) }, "loopback": func() interface{} { return new(LoopbackConfig) }, }, "protocol", "settings") ) func toProtocolList(s []string) ([]proxyman.KnownProtocols, error) { kp := make([]proxyman.KnownProtocols, 0, 8) for _, p := range s { switch strings.ToLower(p) { case "http": kp = append(kp, proxyman.KnownProtocols_HTTP) case "https", "tls", "ssl": kp = append(kp, proxyman.KnownProtocols_TLS) default: return nil, newError("Unknown protocol: ", p) } } return kp, nil } type InboundDetourAllocationConfig struct { Strategy string `json:"strategy"` Concurrency *uint32 `json:"concurrency"` RefreshMin *uint32 `json:"refresh"` } // Build implements Buildable. func (c *InboundDetourAllocationConfig) Build() (*proxyman.AllocationStrategy, error) { config := new(proxyman.AllocationStrategy) switch strings.ToLower(c.Strategy) { case "always": config.Type = proxyman.AllocationStrategy_Always case "random": config.Type = proxyman.AllocationStrategy_Random case "external": config.Type = proxyman.AllocationStrategy_External default: return nil, newError("unknown allocation strategy: ", c.Strategy) } if c.Concurrency != nil { config.Concurrency = &proxyman.AllocationStrategy_AllocationStrategyConcurrency{ Value: *c.Concurrency, } } if c.RefreshMin != nil { config.Refresh = &proxyman.AllocationStrategy_AllocationStrategyRefresh{ Value: *c.RefreshMin, } } return config, nil } type InboundDetourConfig struct { Protocol string `json:"protocol"` PortRange *cfgcommon.PortRange `json:"port"` ListenOn *cfgcommon.Address `json:"listen"` Settings *json.RawMessage `json:"settings"` Tag string `json:"tag"` Allocation *InboundDetourAllocationConfig `json:"allocate"` StreamSetting *StreamConfig `json:"streamSettings"` DomainOverride *cfgcommon.StringList `json:"domainOverride"` SniffingConfig *sniffer.SniffingConfig `json:"sniffing"` } // Build implements Buildable. func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { receiverSettings := &proxyman.ReceiverConfig{} if c.ListenOn == nil { // Listen on anyip, must set PortRange if c.PortRange == nil { return nil, newError("Listen on AnyIP but no Port(s) set in InboundDetour.") } receiverSettings.PortRange = c.PortRange.Build() } else { // Listen on specific IP or Unix Domain Socket receiverSettings.Listen = c.ListenOn.Build() listenDS := c.ListenOn.Family().IsDomain() && (filepath.IsAbs(c.ListenOn.Domain()) || c.ListenOn.Domain()[0] == '@') listenIP := c.ListenOn.Family().IsIP() || (c.ListenOn.Family().IsDomain() && c.ListenOn.Domain() == "localhost") switch { case listenIP: // Listen on specific IP, must set PortRange if c.PortRange == nil { return nil, newError("Listen on specific ip without port in InboundDetour.") } // Listen on IP:Port receiverSettings.PortRange = c.PortRange.Build() case listenDS: if c.PortRange != nil { // Listen on Unix Domain Socket, PortRange should be nil receiverSettings.PortRange = nil } default: return nil, newError("unable to listen on domain address: ", c.ListenOn.Domain()) } } if c.Allocation != nil { concurrency := -1 if c.Allocation.Concurrency != nil && c.Allocation.Strategy == "random" { concurrency = int(*c.Allocation.Concurrency) } portRange := int(c.PortRange.To - c.PortRange.From + 1) if concurrency >= 0 && concurrency >= portRange { return nil, newError("not enough ports. concurrency = ", concurrency, " ports: ", c.PortRange.From, " - ", c.PortRange.To) } as, err := c.Allocation.Build() if err != nil { return nil, err } receiverSettings.AllocationStrategy = as } if c.StreamSetting != nil { ss, err := c.StreamSetting.Build() if err != nil { return nil, err } receiverSettings.StreamSettings = ss } if c.SniffingConfig != nil { s, err := c.SniffingConfig.Build() if err != nil { return nil, newError("failed to build sniffing config").Base(err) } receiverSettings.SniffingSettings = s } if c.DomainOverride != nil { kp, err := toProtocolList(*c.DomainOverride) if err != nil { return nil, newError("failed to parse inbound detour config").Base(err) } receiverSettings.DomainOverride = kp } settings := []byte("{}") if c.Settings != nil { settings = ([]byte)(*c.Settings) } rawConfig, err := inboundConfigLoader.LoadWithID(settings, c.Protocol) if err != nil { return nil, newError("failed to load inbound detour config.").Base(err) } if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok { receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect } ts, err := rawConfig.(cfgcommon.Buildable).Build() if err != nil { return nil, err } return &core.InboundHandlerConfig{ Tag: c.Tag, ReceiverSettings: serial.ToTypedMessage(receiverSettings), ProxySettings: serial.ToTypedMessage(ts), }, nil } type OutboundDetourConfig struct { Protocol string `json:"protocol"` SendThrough *cfgcommon.Address `json:"sendThrough"` Tag string `json:"tag"` Settings *json.RawMessage `json:"settings"` StreamSetting *StreamConfig `json:"streamSettings"` ProxySettings *proxycfg.ProxyConfig `json:"proxySettings"` MuxSettings *muxcfg.MuxConfig `json:"mux"` DomainStrategy string `json:"domainStrategy"` } // Build implements Buildable. func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { senderSettings := &proxyman.SenderConfig{} if c.SendThrough != nil { address := c.SendThrough if address.Family().IsDomain() { return nil, newError("unable to send through: " + address.String()) } senderSettings.Via = address.Build() } if c.StreamSetting != nil { ss, err := c.StreamSetting.Build() if err != nil { return nil, err } senderSettings.StreamSettings = ss } if c.ProxySettings != nil { ps, err := c.ProxySettings.Build() if err != nil { return nil, newError("invalid outbound detour proxy settings.").Base(err) } senderSettings.ProxySettings = ps } if c.MuxSettings != nil { senderSettings.MultiplexSettings = c.MuxSettings.Build() } senderSettings.DomainStrategy = proxyman.SenderConfig_AS_IS switch strings.ToLower(c.DomainStrategy) { case "useip", "use_ip", "use-ip": senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP4 case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP6 } settings := []byte("{}") if c.Settings != nil { settings = ([]byte)(*c.Settings) } rawConfig, err := outboundConfigLoader.LoadWithID(settings, c.Protocol) if err != nil { return nil, newError("failed to parse to outbound detour config.").Base(err) } ts, err := rawConfig.(cfgcommon.Buildable).Build() if err != nil { return nil, err } return &core.OutboundHandlerConfig{ SenderSettings: serial.ToTypedMessage(senderSettings), Tag: c.Tag, ProxySettings: serial.ToTypedMessage(ts), }, nil } type StatsConfig struct{} // Build implements Buildable. func (c *StatsConfig) Build() (*stats.Config, error) { return &stats.Config{}, nil } type Config struct { // Port of this Point server. // Deprecated: Port exists for historical compatibility // and should not be used. Port uint16 `json:"port"` // Deprecated: InboundConfig exists for historical compatibility // and should not be used. InboundConfig *InboundDetourConfig `json:"inbound"` // Deprecated: OutboundConfig exists for historical compatibility // and should not be used. OutboundConfig *OutboundDetourConfig `json:"outbound"` // Deprecated: InboundDetours exists for historical compatibility // and should not be used. InboundDetours []InboundDetourConfig `json:"inboundDetour"` // Deprecated: OutboundDetours exists for historical compatibility // and should not be used. OutboundDetours []OutboundDetourConfig `json:"outboundDetour"` LogConfig *log.LogConfig `json:"log"` RouterConfig *router.RouterConfig `json:"routing"` DNSConfig *dns.DNSConfig `json:"dns"` InboundConfigs []InboundDetourConfig `json:"inbounds"` OutboundConfigs []OutboundDetourConfig `json:"outbounds"` Transport *TransportConfig `json:"transport"` Policy *PolicyConfig `json:"policy"` API *APIConfig `json:"api"` Stats *StatsConfig `json:"stats"` Reverse *ReverseConfig `json:"reverse"` FakeDNS *dns.FakeDNSConfig `json:"fakeDns"` BrowserForwarder *BrowserForwarderConfig `json:"browserForwarder"` Observatory *ObservatoryConfig `json:"observatory"` BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"` MultiObservatory *MultiObservatoryConfig `json:"multiObservatory"` Services map[string]*json.RawMessage `json:"services"` } func (c *Config) findInboundTag(tag string) int { found := -1 for idx, ib := range c.InboundConfigs { if ib.Tag == tag { found = idx break } } return found } func (c *Config) findOutboundTag(tag string) int { found := -1 for idx, ob := range c.OutboundConfigs { if ob.Tag == tag { found = idx break } } return found } func applyTransportConfig(s *StreamConfig, t *TransportConfig) { if s.TCPSettings == nil { s.TCPSettings = t.TCPConfig } if s.KCPSettings == nil { s.KCPSettings = t.KCPConfig } if s.WSSettings == nil { s.WSSettings = t.WSConfig } if s.HTTPSettings == nil { s.HTTPSettings = t.HTTPConfig } if s.DSSettings == nil { s.DSSettings = t.DSConfig } } // Build implements Buildable. func (c *Config) Build() (*core.Config, error) { if err := PostProcessConfigureFile(c); err != nil { return nil, err } config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, } if c.API != nil { apiConf, err := c.API.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(apiConf)) } if c.Stats != nil { statsConf, err := c.Stats.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(statsConf)) } var logConfMsg *anypb.Any if c.LogConfig != nil { logConfMsg = serial.ToTypedMessage(c.LogConfig.Build()) } else { logConfMsg = serial.ToTypedMessage(log.DefaultLogConfig()) } // let logger module be the first App to start, // so that other modules could print log during initiating config.App = append([]*anypb.Any{logConfMsg}, config.App...) if c.RouterConfig != nil { routerConfig, err := c.RouterConfig.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(routerConfig)) } if c.FakeDNS != nil { features.PrintDeprecatedFeatureWarning("root fakedns settings") if c.DNSConfig != nil { c.DNSConfig.FakeDNS = c.FakeDNS } else { c.DNSConfig = &dns.DNSConfig{ FakeDNS: c.FakeDNS, } } } if c.DNSConfig != nil { dnsApp, err := c.DNSConfig.Build() if err != nil { return nil, newError("failed to parse DNS config").Base(err) } config.App = append(config.App, serial.ToTypedMessage(dnsApp)) } if c.Policy != nil { pc, err := c.Policy.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(pc)) } if c.Reverse != nil { r, err := c.Reverse.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(r)) } if c.BrowserForwarder != nil { r, err := c.BrowserForwarder.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(r)) } if c.Observatory != nil { r, err := c.Observatory.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(r)) } if c.BurstObservatory != nil { r, err := c.BurstObservatory.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(r)) } if c.MultiObservatory != nil { r, err := c.MultiObservatory.Build() if err != nil { return nil, err } config.App = append(config.App, serial.ToTypedMessage(r)) } // Load Additional Services that do not have a json translator for serviceName, service := range c.Services { servicePackedConfig, err := v5cfg.LoadHeterogeneousConfigFromRawJSON(context.Background(), "service", serviceName, *service) if err != nil { return nil, newError(fmt.Sprintf("failed to parse %v config in Services", serviceName)).Base(err) } config.App = append(config.App, serial.ToTypedMessage(servicePackedConfig)) } var inbounds []InboundDetourConfig if c.InboundConfig != nil { inbounds = append(inbounds, *c.InboundConfig) } if len(c.InboundDetours) > 0 { inbounds = append(inbounds, c.InboundDetours...) } if len(c.InboundConfigs) > 0 { inbounds = append(inbounds, c.InboundConfigs...) } // Backward compatibility. if len(inbounds) > 0 && inbounds[0].PortRange == nil && c.Port > 0 { inbounds[0].PortRange = &cfgcommon.PortRange{ From: uint32(c.Port), To: uint32(c.Port), } } for _, rawInboundConfig := range inbounds { if c.Transport != nil { if rawInboundConfig.StreamSetting == nil { rawInboundConfig.StreamSetting = &StreamConfig{} } applyTransportConfig(rawInboundConfig.StreamSetting, c.Transport) } ic, err := rawInboundConfig.Build() if err != nil { return nil, err } config.Inbound = append(config.Inbound, ic) } var outbounds []OutboundDetourConfig if c.OutboundConfig != nil { outbounds = append(outbounds, *c.OutboundConfig) } if len(c.OutboundDetours) > 0 { outbounds = append(outbounds, c.OutboundDetours...) } if len(c.OutboundConfigs) > 0 { outbounds = append(outbounds, c.OutboundConfigs...) } for _, rawOutboundConfig := range outbounds { if c.Transport != nil { if rawOutboundConfig.StreamSetting == nil { rawOutboundConfig.StreamSetting = &StreamConfig{} } applyTransportConfig(rawOutboundConfig.StreamSetting, c.Transport) } oc, err := rawOutboundConfig.Build() if err != nil { return nil, err } config.Outbound = append(config.Outbound, oc) } return config, nil } ================================================ FILE: infra/conf/v4/v2ray_test.go ================================================ package v4_test import ( "encoding/json" "reflect" "testing" "github.com/golang/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/muxcfg" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/memconservative" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/standard" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" dns_proxy "github.com/v2fly/v2ray-core/v5/proxy/dns" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/http" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" "github.com/v2fly/v2ray-core/v5/transport/internet/websocket" ) func TestV2RayConfig(t *testing.T) { createParser := func() func(string) (proto.Message, error) { return func(s string) (proto.Message, error) { config := new(v4.Config) if err := json.Unmarshal([]byte(s), config); err != nil { return nil, err } return config.Build() } } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "outbound": { "protocol": "freedom", "settings": {} }, "log": { "access": "/var/log/v2ray/access.log", "loglevel": "error", "error": "/var/log/v2ray/error.log" }, "inbound": { "streamSettings": { "network": "ws", "wsSettings": { "headers": { "host": "example.domain" }, "path": "" }, "tlsSettings": { "alpn": "h2" }, "security": "tls" }, "protocol": "vmess", "port": 443, "settings": { "clients": [ { "alterId": 100, "security": "aes-128-gcm", "id": "0cdf8a45-303d-4fed-9780-29aa7f54175e" } ] } }, "inbounds": [{ "streamSettings": { "network": "ws", "wsSettings": { "headers": { "host": "example.domain" }, "path": "" }, "tlsSettings": { "alpn": "h2" }, "security": "tls" }, "protocol": "vmess", "port": "443-500", "allocate": { "strategy": "random", "concurrency": 3 }, "settings": { "clients": [ { "alterId": 100, "security": "aes-128-gcm", "id": "0cdf8a45-303d-4fed-9780-29aa7f54175e" } ] } }], "outboundDetour": [ { "tag": "blocked", "protocol": "blackhole" }, { "protocol": "dns" } ], "routing": { "strategy": "rules", "settings": { "rules": [ { "ip": [ "10.0.0.0/8" ], "type": "field", "outboundTag": "blocked" } ] } }, "transport": { "httpSettings": { "path": "/test" } } }`, Parser: createParser(), Output: &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{ Type: log.LogType_File, Level: clog.Severity_Error, Path: "/var/log/v2ray/error.log", }, Access: &log.LogSpecification{ Type: log.LogType_File, Path: "/var/log/v2ray/access.log", }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&router.Config{ DomainStrategy: router.DomainStrategy_AsIs, Rule: []*router.RoutingRule{ { Geoip: []*routercommon.GeoIP{ { Cidr: []*routercommon.CIDR{ { Ip: []byte{10, 0, 0, 0}, Prefix: 8, }, }, }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "blocked", }, }, }, }), }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "tcp", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&freedom.Config{ DomainStrategy: freedom.Config_AS_IS, UserLevel: 0, }), }, { Tag: "blocked", SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "tcp", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "tcp", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{ Server: &net.Endpoint{}, }), }, }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{ From: 443, To: 443, }, StreamSettings: &internet.StreamConfig{ ProtocolName: "websocket", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "websocket", Settings: serial.ToTypedMessage(&websocket.Config{ Header: []*websocket.Header{ { Key: "host", Value: "example.domain", }, }, }), }, { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, SecurityType: "v2ray.core.transport.internet.tls.Config", SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ NextProtocol: []string{"h2"}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Level: 0, Account: serial.ToTypedMessage(&vmess.Account{ Id: "0cdf8a45-303d-4fed-9780-29aa7f54175e", AlterId: 100, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{ From: 443, To: 500, }, AllocationStrategy: &proxyman.AllocationStrategy{ Type: proxyman.AllocationStrategy_Random, Concurrency: &proxyman.AllocationStrategy_AllocationStrategyConcurrency{ Value: 3, }, }, StreamSettings: &internet.StreamConfig{ ProtocolName: "websocket", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "websocket", Settings: serial.ToTypedMessage(&websocket.Config{ Header: []*websocket.Header{ { Key: "host", Value: "example.domain", }, }, }), }, { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, SecurityType: "v2ray.core.transport.internet.tls.Config", SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ NextProtocol: []string{"h2"}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Level: 0, Account: serial.ToTypedMessage(&vmess.Account{ Id: "0cdf8a45-303d-4fed-9780-29aa7f54175e", AlterId: 100, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }), }, }, }, }, }) } func TestMuxConfig_Build(t *testing.T) { tests := []struct { name string fields string want *proxyman.MultiplexingConfig }{ {"default", `{"enabled": true, "concurrency": 16}`, &proxyman.MultiplexingConfig{ Enabled: true, Concurrency: 16, }}, {"empty def", `{}`, &proxyman.MultiplexingConfig{ Enabled: false, Concurrency: 8, }}, {"not enable", `{"enabled": false, "concurrency": 4}`, &proxyman.MultiplexingConfig{ Enabled: false, Concurrency: 4, }}, {"forbidden", `{"enabled": false, "concurrency": -1}`, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &muxcfg.MuxConfig{} common.Must(json.Unmarshal([]byte(tt.fields), m)) if got := m.Build(); !reflect.DeepEqual(got, tt.want) { t.Errorf("MuxConfig.Build() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: infra/conf/v4/vless.go ================================================ package v4 import ( "encoding/json" "path/filepath" "runtime" "strconv" "strings" "syscall" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/vless" "github.com/v2fly/v2ray-core/v5/proxy/vless/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vless/outbound" ) type VLessInboundFallback struct { Alpn string `json:"alpn"` Path string `json:"path"` Type string `json:"type"` Dest json.RawMessage `json:"dest"` Xver uint64 `json:"xver"` } type VLessInboundConfig struct { Clients []json.RawMessage `json:"clients"` Decryption string `json:"decryption"` Fallback json.RawMessage `json:"fallback"` Fallbacks []*VLessInboundFallback `json:"fallbacks"` } // Build implements Buildable func (c *VLessInboundConfig) Build() (proto.Message, error) { config := new(inbound.Config) config.Clients = make([]*protocol.User, len(c.Clients)) for idx, rawUser := range c.Clients { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { return nil, newError(`VLESS clients: invalid user`).Base(err) } account := new(vless.Account) if err := json.Unmarshal(rawUser, account); err != nil { return nil, newError(`VLESS clients: invalid user`).Base(err) } if account.Encryption != "" { return nil, newError(`VLESS clients: "encryption" should not in inbound settings`) } user.Account = serial.ToTypedMessage(account) config.Clients[idx] = user } if c.Decryption != "none" { return nil, newError(`VLESS settings: please add/set "decryption":"none" to every settings`) } config.Decryption = c.Decryption if c.Fallback != nil { return nil, newError(`VLESS settings: please use "fallbacks":[{}] instead of "fallback":{}`) } for _, fb := range c.Fallbacks { var i uint16 var s string if err := json.Unmarshal(fb.Dest, &i); err == nil { s = strconv.Itoa(int(i)) } else { _ = json.Unmarshal(fb.Dest, &s) } config.Fallbacks = append(config.Fallbacks, &inbound.Fallback{ Alpn: fb.Alpn, Path: fb.Path, Type: fb.Type, Dest: s, Xver: fb.Xver, }) } for _, fb := range config.Fallbacks { /* if fb.Alpn == "h2" && fb.Path != "" { return nil, newError(`VLESS fallbacks: "alpn":"h2" doesn't support "path"`) } */ if fb.Path != "" && fb.Path[0] != '/' { return nil, newError(`VLESS fallbacks: "path" must be empty or start with "/"`) } if fb.Type == "" && fb.Dest != "" { if fb.Dest == "serve-ws-none" { // nolint:gocritic fb.Type = "serve" } else if filepath.IsAbs(fb.Dest) || fb.Dest[0] == '@' { fb.Type = "unix" if strings.HasPrefix(fb.Dest, "@@") && (runtime.GOOS == "linux" || runtime.GOOS == "android") { fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy copy(fullAddr, fb.Dest[1:]) fb.Dest = string(fullAddr) } } else { if _, err := strconv.Atoi(fb.Dest); err == nil { fb.Dest = "127.0.0.1:" + fb.Dest } if _, _, err := net.SplitHostPort(fb.Dest); err == nil { fb.Type = "tcp" } } } if fb.Type == "" { return nil, newError(`VLESS fallbacks: please fill in a valid value for every "dest"`) } if fb.Xver > 2 { return nil, newError(`VLESS fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) } } return config, nil } type VLessOutboundVnext struct { Address *cfgcommon.Address `json:"address"` Port uint16 `json:"port"` Users []json.RawMessage `json:"users"` } type VLessOutboundConfig struct { Vnext []*VLessOutboundVnext `json:"vnext"` } // Build implements Buildable func (c *VLessOutboundConfig) Build() (proto.Message, error) { config := new(outbound.Config) if len(c.Vnext) == 0 { return nil, newError(`VLESS settings: "vnext" is empty`) } config.Vnext = make([]*protocol.ServerEndpoint, len(c.Vnext)) for idx, rec := range c.Vnext { if rec.Address == nil { return nil, newError(`VLESS vnext: "address" is not set`) } if len(rec.Users) == 0 { return nil, newError(`VLESS vnext: "users" is empty`) } spec := &protocol.ServerEndpoint{ Address: rec.Address.Build(), Port: uint32(rec.Port), User: make([]*protocol.User, len(rec.Users)), } for idx, rawUser := range rec.Users { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { return nil, newError(`VLESS users: invalid user`).Base(err) } account := new(vless.Account) if err := json.Unmarshal(rawUser, account); err != nil { return nil, newError(`VLESS users: invalid user`).Base(err) } if account.Encryption != "none" { return nil, newError(`VLESS users: please add/set "encryption":"none" for every user`) } user.Account = serial.ToTypedMessage(account) spec.User[idx] = user } config.Vnext[idx] = spec } return config, nil } ================================================ FILE: infra/conf/v4/vless_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/vless" "github.com/v2fly/v2ray-core/v5/proxy/vless/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vless/outbound" ) func TestVLessOutbound(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.VLessOutboundConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "vnext": [{ "address": "example.com", "port": 443, "users": [ { "id": "27848739-7e62-4138-9fd3-098a63964b6b", "encryption": "none", "level": 0 } ] }] }`, Parser: testassist.LoadJSON(creator), Output: &outbound.Config{ Vnext: []*protocol.ServerEndpoint{ { Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Domain{ Domain: "example.com", }, }, Port: 443, User: []*protocol.User{ { Account: serial.ToTypedMessage(&vless.Account{ Id: "27848739-7e62-4138-9fd3-098a63964b6b", Encryption: "none", }), Level: 0, }, }, }, }, }, }, }) } func TestVLessInbound(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.VLessInboundConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "clients": [ { "id": "27848739-7e62-4138-9fd3-098a63964b6b", "level": 0, "email": "love@v2fly.org" } ], "decryption": "none", "fallbacks": [ { "dest": 80 }, { "alpn": "h2", "dest": "@/dev/shm/domain.socket", "xver": 2 }, { "path": "/innerws", "dest": "serve-ws-none" } ] }`, Parser: testassist.LoadJSON(creator), Output: &inbound.Config{ Clients: []*protocol.User{ { Account: serial.ToTypedMessage(&vless.Account{ Id: "27848739-7e62-4138-9fd3-098a63964b6b", }), Level: 0, Email: "love@v2fly.org", }, }, Decryption: "none", Fallbacks: []*inbound.Fallback{ { Alpn: "", Path: "", Type: "tcp", Dest: "127.0.0.1:80", Xver: 0, }, { Alpn: "h2", Path: "", Type: "unix", Dest: "@/dev/shm/domain.socket", Xver: 2, }, { Alpn: "", Path: "/innerws", Type: "serve", Dest: "serve-ws-none", Xver: 0, }, }, }, }, }) } ================================================ FILE: infra/conf/v4/vmess.go ================================================ package v4 import ( "encoding/json" "strings" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" ) type VMessAccount struct { ID string `json:"id"` AlterIds uint16 `json:"alterId"` Security string `json:"security"` Experiments string `json:"experiments"` } // Build implements Buildable func (a *VMessAccount) Build() *vmess.Account { var st protocol.SecurityType switch strings.ToLower(a.Security) { case "aes-128-gcm": st = protocol.SecurityType_AES128_GCM case "chacha20-poly1305": st = protocol.SecurityType_CHACHA20_POLY1305 case "auto": st = protocol.SecurityType_AUTO case "none": st = protocol.SecurityType_NONE case "zero": st = protocol.SecurityType_ZERO default: st = protocol.SecurityType_AUTO } return &vmess.Account{ Id: a.ID, AlterId: uint32(a.AlterIds), SecuritySettings: &protocol.SecurityConfig{ Type: st, }, TestsEnabled: a.Experiments, } } type VMessDetourConfig struct { ToTag string `json:"to"` } // Build implements Buildable func (c *VMessDetourConfig) Build() *inbound.DetourConfig { return &inbound.DetourConfig{ To: c.ToTag, } } type FeaturesConfig struct { Detour *VMessDetourConfig `json:"detour"` } type VMessDefaultConfig struct { AlterIDs uint16 `json:"alterId"` Level byte `json:"level"` } // Build implements Buildable func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig { config := new(inbound.DefaultConfig) config.AlterId = uint32(c.AlterIDs) config.Level = uint32(c.Level) return config } type VMessInboundConfig struct { Users []json.RawMessage `json:"clients"` Features *FeaturesConfig `json:"features"` Defaults *VMessDefaultConfig `json:"default"` DetourConfig *VMessDetourConfig `json:"detour"` SecureOnly bool `json:"disableInsecureEncryption"` } // Build implements Buildable func (c *VMessInboundConfig) Build() (proto.Message, error) { config := &inbound.Config{ SecureEncryptionOnly: c.SecureOnly, } if c.Defaults != nil { config.Default = c.Defaults.Build() } if c.DetourConfig != nil { config.Detour = c.DetourConfig.Build() } else if c.Features != nil && c.Features.Detour != nil { config.Detour = c.Features.Detour.Build() } config.User = make([]*protocol.User, len(c.Users)) for idx, rawData := range c.Users { user := new(protocol.User) if err := json.Unmarshal(rawData, user); err != nil { return nil, newError("invalid VMess user").Base(err) } account := new(VMessAccount) if err := json.Unmarshal(rawData, account); err != nil { return nil, newError("invalid VMess user").Base(err) } user.Account = serial.ToTypedMessage(account.Build()) config.User[idx] = user } return config, nil } type VMessOutboundTarget struct { Address *cfgcommon.Address `json:"address"` Port uint16 `json:"port"` Users []json.RawMessage `json:"users"` } type VMessOutboundConfig struct { Receivers []*VMessOutboundTarget `json:"vnext"` } // Build implements Buildable func (c *VMessOutboundConfig) Build() (proto.Message, error) { config := new(outbound.Config) if len(c.Receivers) == 0 { return nil, newError("0 VMess receiver configured") } serverSpecs := make([]*protocol.ServerEndpoint, len(c.Receivers)) for idx, rec := range c.Receivers { if len(rec.Users) == 0 { return nil, newError("0 user configured for VMess outbound") } if rec.Address == nil { return nil, newError("address is not set in VMess outbound config") } spec := &protocol.ServerEndpoint{ Address: rec.Address.Build(), Port: uint32(rec.Port), } for _, rawUser := range rec.Users { user := new(protocol.User) if err := json.Unmarshal(rawUser, user); err != nil { return nil, newError("invalid VMess user").Base(err) } account := new(VMessAccount) if err := json.Unmarshal(rawUser, account); err != nil { return nil, newError("invalid VMess user").Base(err) } user.Account = serial.ToTypedMessage(account.Build()) spec.User = append(spec.User, user) } serverSpecs[idx] = spec } config.Receiver = serverSpecs return config, nil } ================================================ FILE: infra/conf/v4/vmess_test.go ================================================ package v4_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" ) func TestVMessOutbound(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.VMessOutboundConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "vnext": [{ "address": "127.0.0.1", "port": 80, "users": [ { "id": "e641f5ad-9397-41e3-bf1a-e8740dfed019", "email": "love@v2fly.org", "level": 255 } ] }] }`, Parser: testassist.LoadJSON(creator), Output: &outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: 80, User: []*protocol.User{ { Email: "love@v2fly.org", Level: 255, Account: serial.ToTypedMessage(&vmess.Account{ Id: "e641f5ad-9397-41e3-bf1a-e8740dfed019", AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AUTO, }, }), }, }, }, }, }, }, }) } func TestVMessInbound(t *testing.T) { creator := func() cfgcommon.Buildable { return new(v4.VMessInboundConfig) } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "clients": [ { "id": "27848739-7e62-4138-9fd3-098a63964b6b", "level": 0, "alterId": 16, "email": "love@v2fly.org", "security": "aes-128-gcm" } ], "default": { "level": 0, "alterId": 32 }, "detour": { "to": "tag_to_detour" }, "disableInsecureEncryption": true }`, Parser: testassist.LoadJSON(creator), Output: &inbound.Config{ User: []*protocol.User{ { Level: 0, Email: "love@v2fly.org", Account: serial.ToTypedMessage(&vmess.Account{ Id: "27848739-7e62-4138-9fd3-098a63964b6b", AlterId: 16, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, Default: &inbound.DefaultConfig{ Level: 0, AlterId: 32, }, Detour: &inbound.DetourConfig{ To: "tag_to_detour", }, SecureEncryptionOnly: true, }, }, }) } ================================================ FILE: infra/conf/v5cfg/common.go ================================================ package v5cfg import ( "context" "encoding/json" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/environment/envimpl" "github.com/v2fly/v2ray-core/v5/common/registry" ) func loadHeterogeneousConfigFromRawJSON(interfaceType, name string, rawJSON json.RawMessage) (proto.Message, error) { fsdef := envimpl.NewDefaultFileSystemDefaultImpl() ctx := envctx.ContextWithEnvironment(context.TODO(), fsdef) if len(rawJSON) == 0 { rawJSON = []byte("{}") } return registry.LoadImplementationByAlias(ctx, interfaceType, name, []byte(rawJSON)) } // LoadHeterogeneousConfigFromRawJSON private API func LoadHeterogeneousConfigFromRawJSON(ctx context.Context, interfaceType, name string, rawJSON json.RawMessage) (proto.Message, error) { return loadHeterogeneousConfigFromRawJSON(interfaceType, name, rawJSON) } ================================================ FILE: infra/conf/v5cfg/errors.generated.go ================================================ package v5cfg import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: infra/conf/v5cfg/inbound.go ================================================ package v5cfg import ( "context" "path/filepath" "github.com/golang/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func (c InboundConfig) BuildV5(ctx context.Context) (proto.Message, error) { receiverSettings := &proxyman.ReceiverConfig{} if c.ListenOn == nil { // Listen on anyip, must set PortRange if c.PortRange == nil { return nil, newError("Listen on AnyIP but no Port(s) set in InboundDetour.") } receiverSettings.PortRange = c.PortRange.Build() } else { // Listen on specific IP or Unix Domain Socket receiverSettings.Listen = c.ListenOn.Build() listenDS := c.ListenOn.Family().IsDomain() && (filepath.IsAbs(c.ListenOn.Domain()) || c.ListenOn.Domain()[0] == '@') listenIP := c.ListenOn.Family().IsIP() || (c.ListenOn.Family().IsDomain() && c.ListenOn.Domain() == "localhost") switch { case listenIP: // Listen on specific IP, must set PortRange if c.PortRange == nil { return nil, newError("Listen on specific ip without port in InboundDetour.") } // Listen on IP:Port receiverSettings.PortRange = c.PortRange.Build() case listenDS: if c.PortRange != nil { // Listen on Unix Domain Socket, PortRange should be nil receiverSettings.PortRange = nil } default: return nil, newError("unable to listen on domain address: ", c.ListenOn.Domain()) } } if c.StreamSetting != nil { ss, err := c.StreamSetting.BuildV5(ctx) if err != nil { return nil, err } receiverSettings.StreamSettings = ss.(*internet.StreamConfig) } if c.SniffingConfig != nil { s, err := c.SniffingConfig.Build() if err != nil { return nil, newError("failed to build sniffing config").Base(err) } receiverSettings.SniffingSettings = s } if c.Settings == nil { c.Settings = []byte("{}") } inboundConfigPack, err := loadHeterogeneousConfigFromRawJSON("inbound", c.Protocol, c.Settings) if err != nil { return nil, newError("unable to load inbound protocol config").Base(err) } if content, ok := inboundConfigPack.(*dokodemo.SimplifiedConfig); ok { receiverSettings.ReceiveOriginalDestination = content.FollowRedirect } if content, ok := inboundConfigPack.(*dokodemo.Config); ok { receiverSettings.ReceiveOriginalDestination = content.FollowRedirect } return &core.InboundHandlerConfig{ Tag: c.Tag, ReceiverSettings: serial.ToTypedMessage(receiverSettings), ProxySettings: serial.ToTypedMessage(inboundConfigPack), }, nil } ================================================ FILE: infra/conf/v5cfg/init.go ================================================ package v5cfg import ( "bytes" "io" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/cmdarg" "github.com/v2fly/v2ray-core/v5/infra/conf/json" ) const jsonV5 = "jsonv5" func init() { common.Must(core.RegisterConfigLoader(&core.ConfigFormat{ Name: []string{jsonV5}, Extension: []string{".v5.json", ".v5.jsonc"}, Loader: func(input interface{}) (*core.Config, error) { switch v := input.(type) { case string: r, err := cmdarg.LoadArg(v) if err != nil { return nil, err } data, err := buf.ReadAllToBytes(&json.Reader{ Reader: r, }) if err != nil { return nil, err } return loadJSONConfig(data) case []byte: r := &json.Reader{ Reader: bytes.NewReader(v), } data, err := buf.ReadAllToBytes(r) if err != nil { return nil, err } return loadJSONConfig(data) case io.Reader: data, err := buf.ReadAllToBytes(&json.Reader{ Reader: v, }) if err != nil { return nil, err } return loadJSONConfig(data) default: return nil, newError("unknown type") } }, })) } ================================================ FILE: infra/conf/v5cfg/outbound.go ================================================ package v5cfg import ( "context" "github.com/golang/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func (c OutboundConfig) BuildV5(ctx context.Context) (proto.Message, error) { senderSettings := &proxyman.SenderConfig{} if c.SendThrough != nil { address := c.SendThrough if address.Family().IsDomain() { return nil, newError("unable to send through: " + address.String()) } senderSettings.Via = address.Build() } if c.StreamSetting != nil { ss, err := c.StreamSetting.BuildV5(ctx) if err != nil { return nil, err } senderSettings.StreamSettings = ss.(*internet.StreamConfig) } if c.ProxySettings != nil { ps, err := c.ProxySettings.Build() if err != nil { return nil, newError("invalid outbound detour proxy settings.").Base(err) } senderSettings.ProxySettings = ps } if c.MuxSettings != nil { senderSettings.MultiplexSettings = c.MuxSettings.Build() } senderSettings.DomainStrategy = proxyman.SenderConfig_AS_IS switch c.DomainStrategy { case "UseIP": senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP case "UseIP4": senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP4 case "UseIP6": senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP6 case "AsIs", "": default: return nil, newError("unknown domain strategy: ", c.DomainStrategy) } if c.Settings == nil { c.Settings = []byte("{}") } outboundConfigPack, err := loadHeterogeneousConfigFromRawJSON("outbound", c.Protocol, c.Settings) if err != nil { return nil, newError("unable to load outbound protocol config").Base(err) } return &core.OutboundHandlerConfig{ SenderSettings: serial.ToTypedMessage(senderSettings), Tag: c.Tag, ProxySettings: serial.ToTypedMessage(outboundConfigPack), }, nil } ================================================ FILE: infra/conf/v5cfg/root.go ================================================ package v5cfg import ( "bytes" "context" "encoding/json" "fmt" "github.com/golang/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/geodata" "github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/log" ) func (c RootConfig) BuildV5(ctx context.Context) (proto.Message, error) { config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, } var logConfMsg *anypb.Any if c.LogConfig != nil { logConfMsgUnpacked, err := loadHeterogeneousConfigFromRawJSON("service", "log", c.LogConfig) if err != nil { return nil, newError("failed to parse Log config").Base(err) } logConfMsg = serial.ToTypedMessage(logConfMsgUnpacked) } else { logConfMsg = serial.ToTypedMessage(log.DefaultLogConfig()) } // let logger module be the first App to start, // so that other modules could print log during initiating config.App = append([]*anypb.Any{logConfMsg}, config.App...) if c.RouterConfig != nil { routerConfig, err := loadHeterogeneousConfigFromRawJSON("service", "router", c.RouterConfig) if err != nil { return nil, newError("failed to parse Router config").Base(err) } config.App = append(config.App, serial.ToTypedMessage(routerConfig)) } if c.DNSConfig != nil { dnsApp, err := loadHeterogeneousConfigFromRawJSON("service", "dns", c.DNSConfig) if err != nil { return nil, newError("failed to parse DNS config").Base(err) } config.App = append(config.App, serial.ToTypedMessage(dnsApp)) } for _, rawInboundConfig := range c.Inbounds { ic, err := rawInboundConfig.BuildV5(ctx) if err != nil { return nil, err } config.Inbound = append(config.Inbound, ic.(*core.InboundHandlerConfig)) } for _, rawOutboundConfig := range c.Outbounds { ic, err := rawOutboundConfig.BuildV5(ctx) if err != nil { return nil, err } config.Outbound = append(config.Outbound, ic.(*core.OutboundHandlerConfig)) } for serviceName, service := range c.Services { servicePackedConfig, err := loadHeterogeneousConfigFromRawJSON("service", serviceName, service) if err != nil { return nil, newError(fmt.Sprintf("failed to parse %v config in Services", serviceName)).Base(err) } config.App = append(config.App, serial.ToTypedMessage(servicePackedConfig)) } return config, nil } func loadJSONConfig(data []byte) (*core.Config, error) { rootConfig := &RootConfig{} rootConfDecoder := json.NewDecoder(bytes.NewReader(data)) rootConfDecoder.DisallowUnknownFields() err := rootConfDecoder.Decode(rootConfig) if err != nil { return nil, newError("unable to load json").Base(err) } buildctx := cfgcommon.NewConfigureLoadingContext(context.Background()) geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string { return "standard" }) if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil { cfgcommon.SetGeoDataLoader(buildctx, loader) } else { return nil, newError("unable to create geo data loader ").Base(err) } message, err := rootConfig.BuildV5(buildctx) if err != nil { return nil, newError("unable to build config").Base(err) } return message.(*core.Config), nil } ================================================ FILE: infra/conf/v5cfg/skeleton.go ================================================ package v5cfg import ( "encoding/json" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/muxcfg" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/proxycfg" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/sniffer" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/socketcfg" ) type RootConfig struct { LogConfig json.RawMessage `json:"log"` DNSConfig json.RawMessage `json:"dns"` RouterConfig json.RawMessage `json:"router"` Inbounds []InboundConfig `json:"inbounds"` Outbounds []OutboundConfig `json:"outbounds"` Services map[string]json.RawMessage `json:"services"` Extensions []json.RawMessage `json:"extension"` } type InboundConfig struct { Protocol string `json:"protocol"` PortRange *cfgcommon.PortRange `json:"port"` ListenOn *cfgcommon.Address `json:"listen"` Settings json.RawMessage `json:"settings"` Tag string `json:"tag"` SniffingConfig *sniffer.SniffingConfig `json:"sniffing"` StreamSetting *StreamConfig `json:"streamSettings"` } type OutboundConfig struct { Protocol string `json:"protocol"` SendThrough *cfgcommon.Address `json:"sendThrough"` Tag string `json:"tag"` Settings json.RawMessage `json:"settings"` StreamSetting *StreamConfig `json:"streamSettings"` ProxySettings *proxycfg.ProxyConfig `json:"proxySettings"` MuxSettings *muxcfg.MuxConfig `json:"mux"` DomainStrategy string `json:"domainStrategy"` } type StreamConfig struct { Transport string `json:"transport"` TransportSettings json.RawMessage `json:"transportSettings"` Security string `json:"security"` SecuritySettings json.RawMessage `json:"securitySettings"` SocketSettings socketcfg.SocketConfig `json:"socketSettings"` } ================================================ FILE: infra/conf/v5cfg/stream.go ================================================ package v5cfg import ( "context" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func (s StreamConfig) BuildV5(ctx context.Context) (proto.Message, error) { config := &internet.StreamConfig{} if s.Transport == "" { s.Transport = "tcp" } if s.Security == "" { s.Security = "none" } if s.TransportSettings == nil { s.TransportSettings = []byte("{}") } transportConfigPack, err := loadHeterogeneousConfigFromRawJSON("transport", s.Transport, s.TransportSettings) if err != nil { return nil, newError("unable to load transport config").Base(err) } config.ProtocolName = s.Transport config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{ ProtocolName: s.Transport, Settings: serial.ToTypedMessage(transportConfigPack), }) if s.Security != "none" { if s.SecuritySettings == nil { s.SecuritySettings = []byte("{}") } securityConfigPack, err := loadHeterogeneousConfigFromRawJSON("security", s.Security, s.SecuritySettings) if err != nil { return nil, newError("unable to load security config").Base(err) } securityConfigPackTypedMessage := serial.ToTypedMessage(securityConfigPack) config.SecurityType = serial.V2Type(securityConfigPackTypedMessage) config.SecuritySettings = append(config.SecuritySettings, securityConfigPackTypedMessage) } config.SocketSettings, err = s.SocketSettings.Build() if err != nil { return nil, newError("unable to build socket config").Base(err) } return config, nil } ================================================ FILE: infra/conf/v5cfg/v5cfg.go ================================================ package v5cfg //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: infra/vformat/main.go ================================================ package main import ( "fmt" "go/build" "os" "os/exec" "path/filepath" "runtime" "strings" ) // envFile returns the name of the Go environment configuration file. // Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166 func envFile() (string, error) { if file := os.Getenv("GOENV"); file != "" { if file == "off" { return "", fmt.Errorf("GOENV=off") } return file, nil } dir, err := os.UserConfigDir() if err != nil { return "", err } if dir == "" { return "", fmt.Errorf("missing user-config dir") } return filepath.Join(dir, "go", "env"), nil } // GetRuntimeEnv returns the value of runtime environment variable, // that is set by running following command: `go env -w key=value`. func GetRuntimeEnv(key string) (string, error) { file, err := envFile() if err != nil { return "", err } if file == "" { return "", fmt.Errorf("missing runtime env file") } var data []byte var runtimeEnv string data, readErr := os.ReadFile(file) if readErr != nil { return "", readErr } envStrings := strings.Split(string(data), "\n") for _, envItem := range envStrings { envItem = strings.TrimSuffix(envItem, "\r") envKeyValue := strings.Split(envItem, "=") if len(envKeyValue) == 2 && strings.TrimSpace(envKeyValue[0]) == key { runtimeEnv = strings.TrimSpace(envKeyValue[1]) } } return runtimeEnv, nil } // GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty. func GetGOBIN() string { // The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command` GOBIN := os.Getenv("GOBIN") if GOBIN == "" { var err error // The one set by user by running `go env -w GOBIN=/path` GOBIN, err = GetRuntimeEnv("GOBIN") if err != nil { // The default one that Golang uses return filepath.Join(build.Default.GOPATH, "bin") } if GOBIN == "" { return filepath.Join(build.Default.GOPATH, "bin") } return GOBIN } return GOBIN } func Run(binary string, args []string) ([]byte, error) { cmd := exec.Command(binary, args...) cmd.Env = append(cmd.Env, os.Environ()...) output, cmdErr := cmd.CombinedOutput() if cmdErr != nil { return nil, cmdErr } return output, nil } func RunMany(binary string, args, files []string) { fmt.Println("Processing...") maxTasks := make(chan struct{}, runtime.NumCPU()) for _, file := range files { maxTasks <- struct{}{} go func(file string) { output, err := Run(binary, append(args, file)) if err != nil { fmt.Println(err) } else if len(output) > 0 { fmt.Println(string(output)) } <-maxTasks }(file) } } func main() { pwd, err := os.Getwd() if err != nil { fmt.Println("Can not get current working directory.") os.Exit(1) } GOBIN := GetGOBIN() binPath := os.Getenv("PATH") pathSlice := []string{pwd, GOBIN, binPath} binPath = strings.Join(pathSlice, string(os.PathListSeparator)) os.Setenv("PATH", binPath) suffix := "" if runtime.GOOS == "windows" { suffix = ".exe" } gofmt := "gofmt" + suffix goimports := "gci" + suffix if gofmtPath, err := exec.LookPath(gofmt); err != nil { fmt.Println("Can not find", gofmt, "in system path or current working directory.") os.Exit(1) } else { gofmt = gofmtPath } if goimportsPath, err := exec.LookPath(goimports); err != nil { fmt.Println("Can not find", goimports, "in system path or current working directory.") os.Exit(1) } else { goimports = goimportsPath } rawFilesSlice := make([]string, 0, 1000) walkErr := filepath.Walk("./", func(path string, info os.FileInfo, err error) error { if err != nil { fmt.Println(err) return err } if info.IsDir() { return nil } dir := filepath.Dir(path) filename := filepath.Base(path) if strings.HasSuffix(filename, ".go") && !strings.HasSuffix(filename, ".pb.go") && !strings.Contains(dir, filepath.Join("testing", "mocks")) && !strings.Contains(path, filepath.Join("main", "distro", "all", "all.go")) { rawFilesSlice = append(rawFilesSlice, path) } return nil }) if walkErr != nil { fmt.Println(walkErr) os.Exit(1) } gofmtArgs := []string{ "-s", "-l", "-e", "-w", } goimportsArgs := []string{ "write", "--NoInlineComments", "--NoPrefixComments", "--Section", "Standard", "--Section", "Default", "--Section", "pkgPrefix(github.com/v2fly/v2ray-core)", } RunMany(gofmt, gofmtArgs, rawFilesSlice) RunMany(goimports, goimportsArgs, rawFilesSlice) fmt.Println("Do NOT forget to commit file changes.") } ================================================ FILE: main/commands/all/api/api.go ================================================ package api import ( "github.com/v2fly/v2ray-core/v5/main/commands/base" ) // CmdAPI calls an API in an V2Ray process var CmdAPI = &base.Command{ UsageLine: "{{.Exec}} api", Short: "call V2Ray API", Long: `{{.Exec}} {{.LongName}} provides tools to manipulate V2Ray via its API. `, Commands: []*base.Command{ cmdLog, cmdStats, cmdBalancerInfo, cmdBalancerOverride, }, } ================================================ FILE: main/commands/all/api/balancer_info.go ================================================ package api import ( "fmt" "os" "strings" routerService "github.com/v2fly/v2ray-core/v5/app/router/command" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) // TODO: support "-json" flag for json output var cmdBalancerInfo = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api bi [--server=127.0.0.1:8080] [balancer]...", Short: "balancer information", Long: ` Get information of specified balancers, including health, strategy and selecting. If no balancer tag specified, get information of all balancers. > Make sure you have "RoutingService" set in "config.api.services" of server config. Arguments: -json Use json output. -s, -server The API server address. Default 127.0.0.1:8080 -t, -timeout Timeout seconds to call API. Default 3 Example: {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 balancer1 balancer2 `, Run: executeBalancerInfo, } func executeBalancerInfo(cmd *base.Command, args []string) { setSharedFlags(cmd) cmd.Flag.Parse(args) conn, ctx, close := dialAPIServer() defer close() client := routerService.NewRoutingServiceClient(conn) r := &routerService.GetBalancerInfoRequest{Tag: cmd.Flag.Arg(0)} resp, err := client.GetBalancerInfo(ctx, r) if err != nil { base.Fatalf("failed to get health information: %s", err) } if apiJSON { showJSONResponse(resp) return } showBalancerInfo(resp.Balancer) } func showBalancerInfo(b *routerService.BalancerMsg) { const tableIndent = 4 sb := new(strings.Builder) // Override if b.Override != nil { sb.WriteString(" - Selecting Override:\n") for i, s := range []string{b.Override.Target} { writeRow(sb, tableIndent, i+1, []string{s}, nil) } } // Selects sb.WriteString(" - Selects:\n") for i, o := range b.PrincipleTarget.Tag { writeRow(sb, tableIndent, i+1, []string{o}, nil) } os.Stdout.WriteString(sb.String()) } func getColumnFormats(titles []string) []string { w := make([]string, len(titles)) for i, t := range titles { w[i] = fmt.Sprintf("%%-%ds ", len(t)) } return w } func writeRow(sb *strings.Builder, indent, index int, values, formats []string) { if index == 0 { // title line sb.WriteString(strings.Repeat(" ", indent+4)) } else { sb.WriteString(fmt.Sprintf("%s%-4d", strings.Repeat(" ", indent), index)) } for i, v := range values { format := "%-14s" if i < len(formats) { format = formats[i] } sb.WriteString(fmt.Sprintf(format, v)) } sb.WriteByte('\n') } ================================================ FILE: main/commands/all/api/balancer_override.go ================================================ package api import ( routerService "github.com/v2fly/v2ray-core/v5/app/router/command" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdBalancerOverride = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api bo [--server=127.0.0.1:8080] <-b balancer> outboundTag", Short: "balancer override", Long: ` Override a balancer's selection. > Make sure you have "RoutingService" set in "config.api.services" of server config. Once a balancer's selection is overridden: - The balancer's selection result will always be outboundTag Arguments: -b, -balancer Tag of the target balancer. Required. -r, -remove Remove the override -s, -server The API server address. Default 127.0.0.1:8080 -t, -timeout Timeout seconds to call API. Default 3 Example: {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -b balancer tag {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -b balancer -r `, Run: executeBalancerOverride, } func executeBalancerOverride(cmd *base.Command, args []string) { var ( balancer string remove bool ) cmd.Flag.StringVar(&balancer, "b", "", "") cmd.Flag.StringVar(&balancer, "balancer", "", "") cmd.Flag.BoolVar(&remove, "r", false, "") cmd.Flag.BoolVar(&remove, "remove", false, "") setSharedFlags(cmd) cmd.Flag.Parse(args) if balancer == "" { base.Fatalf("balancer tag not specified") } conn, ctx, close := dialAPIServer() defer close() client := routerService.NewRoutingServiceClient(conn) target := "" if !remove { target = cmd.Flag.Args()[0] } r := &routerService.OverrideBalancerTargetRequest{ BalancerTag: balancer, Target: target, } _, err := client.OverrideBalancerTarget(ctx, r) if err != nil { base.Fatalf("failed to override balancer: %s", err) } } ================================================ FILE: main/commands/all/api/jsonv4/inbounds_add.go ================================================ package jsonv4 import ( "fmt" handlerService "github.com/v2fly/v2ray-core/v5/app/proxyman/command" "github.com/v2fly/v2ray-core/v5/main/commands/all/api" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/main/commands/helpers" ) var cmdAddInbounds = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api adi [--server=127.0.0.1:8080] [c1.json] [dir1]...", Short: "add inbounds", Long: ` Add inbounds to V2Ray. > Make sure you have "HandlerService" set in "config.api.services" of server config. Arguments: -format The input format. Available values: "auto", "json", "toml", "yaml" Default: "auto" -r Load folders recursively. -s, -server The API server address. Default 127.0.0.1:8080 -t, -timeout Timeout seconds to call API. Default 3 Example: {{.Exec}} {{.LongName}} dir {{.Exec}} {{.LongName}} c1.json c2.yaml `, Run: executeAddInbounds, } func executeAddInbounds(cmd *base.Command, args []string) { api.SetSharedFlags(cmd) api.SetSharedConfigFlags(cmd) cmd.Flag.Parse(args) c, err := helpers.LoadConfig(cmd.Flag.Args(), api.APIConfigFormat, api.APIConfigRecursively) if err != nil { base.Fatalf("failed to load: %s", err) } if len(c.InboundConfigs) == 0 { base.Fatalf("no valid inbound found") } conn, ctx, close := api.DialAPIServer() defer close() client := handlerService.NewHandlerServiceClient(conn) for _, in := range c.InboundConfigs { fmt.Println("adding:", in.Tag) i, err := in.Build() if err != nil { base.Fatalf("failed to build conf: %s", err) } r := &handlerService.AddInboundRequest{ Inbound: i, } _, err = client.AddInbound(ctx, r) if err != nil { base.Fatalf("failed to add inbound: %s", err) } } } ================================================ FILE: main/commands/all/api/jsonv4/inbounds_remove.go ================================================ package jsonv4 import ( "fmt" handlerService "github.com/v2fly/v2ray-core/v5/app/proxyman/command" "github.com/v2fly/v2ray-core/v5/main/commands/all/api" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/main/commands/helpers" ) var cmdRemoveInbounds = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api rmi [--server=127.0.0.1:8080] [c1.json] [dir1]...", Short: "remove inbounds", Long: ` Remove inbounds from V2Ray. > Make sure you have "HandlerService" set in "config.api.services" of server config. Arguments: -format The input format. Available values: "auto", "json", "toml", "yaml" Default: "auto" -r Load folders recursively. -tags The input are tags instead of config files -s, -server The API server address. Default 127.0.0.1:8080 -t, -timeout Timeout seconds to call API. Default 3 Example: {{.Exec}} {{.LongName}} dir {{.Exec}} {{.LongName}} c1.json c2.yaml {{.Exec}} {{.LongName}} -tags tag1 tag2 `, Run: executeRemoveInbounds, } func executeRemoveInbounds(cmd *base.Command, args []string) { api.SetSharedFlags(cmd) api.SetSharedConfigFlags(cmd) isTags := cmd.Flag.Bool("tags", false, "") cmd.Flag.Parse(args) var tags []string if *isTags { tags = cmd.Flag.Args() } else { c, err := helpers.LoadConfig(cmd.Flag.Args(), api.APIConfigFormat, api.APIConfigRecursively) if err != nil { base.Fatalf("failed to load: %s", err) } tags = make([]string, 0) for _, c := range c.InboundConfigs { tags = append(tags, c.Tag) } } if len(tags) == 0 { base.Fatalf("no inbound to remove") } conn, ctx, close := api.DialAPIServer() defer close() client := handlerService.NewHandlerServiceClient(conn) for _, tag := range tags { fmt.Println("removing:", tag) r := &handlerService.RemoveInboundRequest{ Tag: tag, } _, err := client.RemoveInbound(ctx, r) if err != nil { base.Fatalf("failed to remove inbound: %s", err) } } } ================================================ FILE: main/commands/all/api/jsonv4/init.go ================================================ package jsonv4 import "github.com/v2fly/v2ray-core/v5/main/commands/all/api" func init() { api.CmdAPI.Commands = append(api.CmdAPI.Commands, cmdAddInbounds, cmdAddOutbounds, cmdRemoveInbounds, cmdRemoveOutbounds) } ================================================ FILE: main/commands/all/api/jsonv4/outbounds_add.go ================================================ package jsonv4 import ( "fmt" handlerService "github.com/v2fly/v2ray-core/v5/app/proxyman/command" "github.com/v2fly/v2ray-core/v5/main/commands/all/api" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/main/commands/helpers" ) var cmdAddOutbounds = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api ado [--server=127.0.0.1:8080] [c1.json] [dir1]...", Short: "add outbounds", Long: ` Add outbounds to V2Ray. > Make sure you have "HandlerService" set in "config.api.services" of server config. Arguments: -format The input format. Available values: "auto", "json", "toml", "yaml" Default: "auto" -r Load folders recursively. -s, -server The API server address. Default 127.0.0.1:8080 -t, -timeout Timeout seconds to call API. Default 3 Example: {{.Exec}} {{.LongName}} dir {{.Exec}} {{.LongName}} c1.json c2.yaml `, Run: executeAddOutbounds, } func executeAddOutbounds(cmd *base.Command, args []string) { api.SetSharedFlags(cmd) api.SetSharedConfigFlags(cmd) cmd.Flag.Parse(args) c, err := helpers.LoadConfig(cmd.Flag.Args(), api.APIConfigFormat, api.APIConfigRecursively) if err != nil { base.Fatalf("failed to load: %s", err) } if len(c.OutboundConfigs) == 0 { base.Fatalf("no valid outbound found") } conn, ctx, close := api.DialAPIServer() defer close() client := handlerService.NewHandlerServiceClient(conn) for _, out := range c.OutboundConfigs { fmt.Println("adding:", out.Tag) o, err := out.Build() if err != nil { base.Fatalf("failed to build conf: %s", err) } r := &handlerService.AddOutboundRequest{ Outbound: o, } _, err = client.AddOutbound(ctx, r) if err != nil { base.Fatalf("failed to add outbound: %s", err) } } } ================================================ FILE: main/commands/all/api/jsonv4/outbounds_remove.go ================================================ package jsonv4 import ( "fmt" handlerService "github.com/v2fly/v2ray-core/v5/app/proxyman/command" "github.com/v2fly/v2ray-core/v5/main/commands/all/api" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/main/commands/helpers" ) var cmdRemoveOutbounds = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api rmo [--server=127.0.0.1:8080] [c1.json] [dir1]...", Short: "remove outbounds", Long: ` Remove outbounds from V2Ray. > Make sure you have "HandlerService" set in "config.api.services" of server config. Arguments: -format The input format. Available values: "auto", "json", "toml", "yaml" Default: "auto" -r Load folders recursively. -tags The input are tags instead of config files -s, -server The API server address. Default 127.0.0.1:8080 -t, -timeout Timeout seconds to call API. Default 3 Example: {{.Exec}} {{.LongName}} dir {{.Exec}} {{.LongName}} c1.json c2.yaml {{.Exec}} {{.LongName}} -tags tag1 tag2 `, Run: executeRemoveOutbounds, } func executeRemoveOutbounds(cmd *base.Command, args []string) { api.SetSharedFlags(cmd) api.SetSharedConfigFlags(cmd) isTags := cmd.Flag.Bool("tags", false, "") cmd.Flag.Parse(args) var tags []string if *isTags { tags = cmd.Flag.Args() } else { c, err := helpers.LoadConfig(cmd.Flag.Args(), api.APIConfigFormat, api.APIConfigRecursively) if err != nil { base.Fatalf("failed to load: %s", err) } tags = make([]string, 0) for _, c := range c.OutboundConfigs { tags = append(tags, c.Tag) } } if len(tags) == 0 { base.Fatalf("no outbound to remove") } conn, ctx, close := api.DialAPIServer() defer close() client := handlerService.NewHandlerServiceClient(conn) for _, tag := range tags { fmt.Println("removing:", tag) r := &handlerService.RemoveOutboundRequest{ Tag: tag, } _, err := client.RemoveOutbound(ctx, r) if err != nil { base.Fatalf("failed to remove outbound: %s", err) } } } ================================================ FILE: main/commands/all/api/log.go ================================================ package api import ( "io" "log" "os" logService "github.com/v2fly/v2ray-core/v5/app/log/command" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdLog = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api log [--server=127.0.0.1:8080]", Short: "log operations", Long: ` Follow and print logs from v2ray. > Make sure you have "LoggerService" set in "config.api.services" of server config. > It ignores -timeout flag while following logs Arguments: -restart Restart the logger -s, -server The API server address. Default 127.0.0.1:8080 -t, -timeout Timeout seconds to call API. Default 3 Example: {{.Exec}} {{.LongName}} {{.Exec}} {{.LongName}} --restart `, Run: executeLog, } func executeLog(cmd *base.Command, args []string) { var restart bool cmd.Flag.BoolVar(&restart, "restart", false, "") setSharedFlags(cmd) cmd.Flag.Parse(args) if restart { restartLogger() return } followLogger() } func restartLogger() { conn, ctx, close := dialAPIServer() defer close() client := logService.NewLoggerServiceClient(conn) r := &logService.RestartLoggerRequest{} _, err := client.RestartLogger(ctx, r) if err != nil { base.Fatalf("failed to restart logger: %s", err) } } func followLogger() { conn, ctx, close := dialAPIServerWithoutTimeout() defer close() client := logService.NewLoggerServiceClient(conn) r := &logService.FollowLogRequest{} stream, err := client.FollowLog(ctx, r) if err != nil { base.Fatalf("failed to follow logger: %s", err) } // work with `v2ray api log | grep expr` log.SetOutput(os.Stdout) for { resp, err := stream.Recv() if err == io.EOF { break } if err != nil { base.Fatalf("failed to fetch log: %s", err) } log.Println(resp.Message) } } ================================================ FILE: main/commands/all/api/shared.go ================================================ package api import ( "context" "fmt" "os" "strings" "time" "google.golang.org/grpc" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var ( apiServerAddrPtr string apiTimeout int apiJSON bool // APIConfigFormat is an internal variable APIConfigFormat string // APIConfigRecursively is an internal variable APIConfigRecursively bool ) // SetSharedFlags is an internal API func SetSharedFlags(cmd *base.Command) { setSharedFlags(cmd) } func setSharedFlags(cmd *base.Command) { cmd.Flag.StringVar(&apiServerAddrPtr, "s", "127.0.0.1:8080", "") cmd.Flag.StringVar(&apiServerAddrPtr, "server", "127.0.0.1:8080", "") cmd.Flag.IntVar(&apiTimeout, "t", 3, "") cmd.Flag.IntVar(&apiTimeout, "timeout", 3, "") cmd.Flag.BoolVar(&apiJSON, "json", false, "") } // SetSharedConfigFlags is an internal API func SetSharedConfigFlags(cmd *base.Command) { setSharedConfigFlags(cmd) } func setSharedConfigFlags(cmd *base.Command) { cmd.Flag.StringVar(&APIConfigFormat, "format", core.FormatAuto, "") cmd.Flag.BoolVar(&APIConfigRecursively, "r", false, "") } // SetSharedFlags is an internal API func DialAPIServer() (conn *grpc.ClientConn, ctx context.Context, close func()) { return dialAPIServer() } func dialAPIServer() (conn *grpc.ClientConn, ctx context.Context, close func()) { ctx, cancel := context.WithTimeout(context.Background(), time.Duration(apiTimeout)*time.Second) conn = dialAPIServerWithContext(ctx) close = func() { cancel() conn.Close() } return } func dialAPIServerWithoutTimeout() (conn *grpc.ClientConn, ctx context.Context, close func()) { ctx = context.Background() conn = dialAPIServerWithContext(ctx) close = func() { conn.Close() } return } func dialAPIServerWithContext(ctx context.Context) (conn *grpc.ClientConn) { conn, err := grpc.DialContext(ctx, apiServerAddrPtr, grpc.WithInsecure(), grpc.WithBlock()) if err != nil { base.Fatalf("failed to dial %s", apiServerAddrPtr) } return } func protoToJSONString(m proto.Message, prefix, indent string) (string, error) { // nolint: unparam return strings.TrimSpace(protojson.MarshalOptions{Indent: indent}.Format(m)), nil } func showJSONResponse(m proto.Message) { output, err := protoToJSONString(m, "", "") if err != nil { fmt.Fprintf(os.Stdout, "%v\n", m) base.Fatalf("error encode json: %s", err) } fmt.Println(output) } ================================================ FILE: main/commands/all/api/stats.go ================================================ package api import ( "fmt" "os" "sort" "strings" "time" statsService "github.com/v2fly/v2ray-core/v5/app/stats/command" "github.com/v2fly/v2ray-core/v5/common/units" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdStats = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} api stats [--server=127.0.0.1:8080] [pattern]...", Short: "query statistics", Long: ` Query statistics from V2Ray. > Make sure you have "StatsService" set in "config.api.services" of server config. Arguments: -regexp The patterns are using regexp. -reset Reset counters to 0 after fetching their values. -runtime Get runtime statistics. -json Use json output. -s, -server The API server address. Default 127.0.0.1:8080 -t, -timeout Timeout seconds to call API. Default 3 Example: {{.Exec}} {{.LongName}} -runtime {{.Exec}} {{.LongName}} node1 {{.Exec}} {{.LongName}} -json node1 node2 {{.Exec}} {{.LongName}} -regexp 'node1.+downlink' `, Run: executeStats, } func executeStats(cmd *base.Command, args []string) { setSharedFlags(cmd) var ( runtime bool regexp bool reset bool ) cmd.Flag.BoolVar(&runtime, "runtime", false, "") cmd.Flag.BoolVar(®exp, "regexp", false, "") cmd.Flag.BoolVar(&reset, "reset", false, "") cmd.Flag.Parse(args) unnamed := cmd.Flag.Args() if runtime { getRuntimeStats(apiJSON) return } getStats(unnamed, regexp, reset, apiJSON) } func getRuntimeStats(jsonOutput bool) { conn, ctx, close := dialAPIServer() defer close() client := statsService.NewStatsServiceClient(conn) r := &statsService.SysStatsRequest{} resp, err := client.GetSysStats(ctx, r) if err != nil { base.Fatalf("failed to get sys stats: %s", err) } if jsonOutput { showJSONResponse(resp) return } showRuntimeStats(resp) } func showRuntimeStats(s *statsService.SysStatsResponse) { formats := []string{"%-22s", "%-10s"} rows := [][]string{ {"Up time", (time.Duration(s.Uptime) * time.Second).String()}, {"Memory obtained", units.ByteSize(s.Sys).String()}, {"Number of goroutines", fmt.Sprintf("%d", s.NumGoroutine)}, {"Heap allocated", units.ByteSize(s.Alloc).String()}, {"Live objects", fmt.Sprintf("%d", s.LiveObjects)}, {"Heap allocated total", units.ByteSize(s.TotalAlloc).String()}, {"Heap allocate count", fmt.Sprintf("%d", s.Mallocs)}, {"Heap free count", fmt.Sprintf("%d", s.Frees)}, {"Number of GC", fmt.Sprintf("%d", s.NumGC)}, {"Time of GC pause", (time.Duration(s.PauseTotalNs) * time.Nanosecond).String()}, } sb := new(strings.Builder) writeRow(sb, 0, 0, []string{"Item", "Value"}, formats, ) for i, r := range rows { writeRow(sb, 0, i+1, r, formats) } os.Stdout.WriteString(sb.String()) } func getStats(patterns []string, regexp, reset, jsonOutput bool) { conn, ctx, close := dialAPIServer() defer close() client := statsService.NewStatsServiceClient(conn) r := &statsService.QueryStatsRequest{ Patterns: patterns, Regexp: regexp, Reset_: reset, } resp, err := client.QueryStats(ctx, r) if err != nil { base.Fatalf("failed to query stats: %s", err) } if jsonOutput { showJSONResponse(resp) return } sort.Slice(resp.Stat, func(i, j int) bool { return resp.Stat[i].Name < resp.Stat[j].Name }) showStats(resp.Stat) } func showStats(stats []*statsService.Stat) { if len(stats) == 0 { return } formats := []string{"%-12s", "%s"} sum := int64(0) sb := new(strings.Builder) idx := 0 writeRow(sb, 0, 0, []string{"Value", "Name"}, formats, ) for _, stat := range stats { // if stat.Value == 0 { // continue // } idx++ sum += stat.Value writeRow( sb, 0, idx, []string{units.ByteSize(stat.Value).String(), stat.Name}, formats, ) } sb.WriteString( fmt.Sprintf("\nTotal: %s\n", units.ByteSize(sum)), ) os.Stdout.WriteString(sb.String()) } ================================================ FILE: main/commands/all/commands.go ================================================ package all import ( "github.com/v2fly/v2ray-core/v5/main/commands/all/api" "github.com/v2fly/v2ray-core/v5/main/commands/all/tls" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) //go:generate go run v2ray.com/core/common/errors/errorgen func init() { base.RootCommand.Commands = append( base.RootCommand.Commands, api.CmdAPI, cmdLove, tls.CmdTLS, cmdUUID, cmdVerify, // documents docFormat, docMerge, ) } ================================================ FILE: main/commands/all/engineering/convertpb.go ================================================ package engineering import ( "bytes" "fmt" "io" "os" "google.golang.org/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/cmdarg" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var ( configFiles cmdarg.Arg configDirs cmdarg.Arg configFormat *string configDirRecursively *bool ) func setConfigFlags(cmd *base.Command) { configFormat = cmd.Flag.String("format", core.FormatAuto, "") configDirRecursively = cmd.Flag.Bool("r", false, "") cmd.Flag.Var(&configFiles, "config", "") cmd.Flag.Var(&configFiles, "c", "") cmd.Flag.Var(&configDirs, "confdir", "") cmd.Flag.Var(&configDirs, "d", "") } var cmdConvertPb = &base.Command{ UsageLine: "{{.Exec}} engineering convertpb [-c config.json] [-d dir]", CustomFlags: true, Run: func(cmd *base.Command, args []string) { setConfigFlags(cmd) cmd.Flag.Parse(args) config, err := core.LoadConfig(*configFormat, configFiles) if err != nil { if len(configFiles) == 0 { base.Fatalf("%s", newError("failed to load config").Base(err)) return } base.Fatalf("%s", newError(fmt.Sprintf("failed to load config: %s", configFiles)).Base(err)) return } bytew, err := proto.Marshal(config) if err != nil { base.Fatalf("%s", newError("failed to marshal config").Base(err)) return } io.Copy(os.Stdout, bytes.NewReader(bytew)) }, } ================================================ FILE: main/commands/all/engineering/encodedataurl.go ================================================ package engineering import ( "flag" "io" "os" "github.com/vincent-petithory/dataurl" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdEncodeDataURLContentType *string var cmdEncodeDataURL = &base.Command{ UsageLine: "{{.Exec}} engineering encodeDataURL", Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) cmdEncodeDataURLContentType = fs.String("type", "application/vnd.v2ray.subscription-singular", "") return *fs }(), Run: func(cmd *base.Command, args []string) { cmd.Flag.Parse(args) content, err := io.ReadAll(os.Stdin) if err != nil { base.Fatalf("%s", err) } dataURL := dataurl.New(content, *cmdEncodeDataURLContentType) dataURL.WriteTo(os.Stdout) }, } ================================================ FILE: main/commands/all/engineering/engineering.go ================================================ package engineering import "github.com/v2fly/v2ray-core/v5/main/commands/base" //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen var cmdEngineering = &base.Command{ UsageLine: "{{.Exec}} engineering", Commands: []*base.Command{ cmdConvertPb, cmdReversePb, cmdNonNativeLinkExtract, cmdNonNativeLinkExec, cmdSubscriptionEntriesExtract, cmdEncodeDataURL, }, } func init() { base.RegisterCommand(cmdEngineering) } func AddCommand(cmd *base.Command) { cmdEngineering.Commands = append(cmdEngineering.Commands, cmd) } ================================================ FILE: main/commands/all/engineering/errors.generated.go ================================================ package engineering import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: main/commands/all/engineering/generateRandomData/errors.generated.go ================================================ package generateRandomData import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: main/commands/all/engineering/generateRandomData/generateRandomData.go ================================================ package generateRandomData import ( "crypto/rand" "encoding/base64" "flag" "fmt" "github.com/v2fly/v2ray-core/v5/main/commands/all/engineering" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen var length *int var cmdGenerateRandomData = &base.Command{ UsageLine: "{{.Exec}} engineering generate-random-data", Short: "generate random data and output as base64", Long: ` Generate random data of specified length and output as base64 encoded string. Usage: {{.Exec}} engineering generate-random-data -length Options: -length The number of random bytes to generate (required) Example: {{.Exec}} engineering generate-random-data -length 32 `, Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) length = fs.Int("length", 0, "number of random bytes to generate") return *fs }(), Run: func(cmd *base.Command, args []string) { err := cmd.Flag.Parse(args) if err != nil { base.Fatalf("failed to parse flags: %v", err) } if *length <= 0 { base.Fatalf("length must be a positive integer, got: %d", *length) } // Generate random data randomData := make([]byte, *length) _, err = rand.Read(randomData) if err != nil { base.Fatalf("failed to generate random data: %v", err) } // Encode to base64 encoded := base64.StdEncoding.EncodeToString(randomData) // Output the result fmt.Println(encoded) }, } func init() { engineering.AddCommand(cmdGenerateRandomData) } ================================================ FILE: main/commands/all/engineering/nonnativelinkexec.go ================================================ package engineering import ( "bytes" "flag" "io" "os" "github.com/v2fly/v2ray-core/v5/app/subscription/entries/nonnative" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdNonNativeLinkExecInputName *string var cmdNonNativeLinkExecTemplatePath *string var cmdNonNativeLinkExec = &base.Command{ UsageLine: "{{.Exec}} engineering nonnativelinkexec", Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) cmdNonNativeLinkExecInputName = fs.String("name", "", "") cmdNonNativeLinkExecTemplatePath = fs.String("templatePath", "", "path for template directory (WARNING: This will not stop templates from reading file outside this directory)") return *fs }(), Run: func(cmd *base.Command, args []string) { cmd.Flag.Parse(args) content, err := io.ReadAll(os.Stdin) if err != nil { base.Fatalf("%s", err) } flattenedLink := nonnative.ExtractAllValuesFromBytes(content) matcher := nonnative.NewDefMatcher() if *cmdNonNativeLinkExecTemplatePath != "" { osFs := os.DirFS(*cmdNonNativeLinkExecTemplatePath) err = matcher.LoadDefinitions(osFs) if err != nil { base.Fatalf("%s", err) } } else { err = matcher.LoadEmbeddedDefinitions() if err != nil { base.Fatalf("%s", err) } } spec, err := matcher.ExecuteNamed(flattenedLink, *cmdNonNativeLinkExecInputName) if err != nil { base.Fatalf("%s", err) } io.Copy(os.Stdout, bytes.NewReader(spec)) }, } ================================================ FILE: main/commands/all/engineering/nonnativelinkextract.go ================================================ package engineering import ( "flag" "fmt" "io" "os" "sort" "strings" "github.com/v2fly/v2ray-core/v5/app/subscription/entries/nonnative" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) type valueContainer struct { key, value string } type orderedValueContainer []valueContainer func (o *orderedValueContainer) Len() int { return len(*o) } func (o *orderedValueContainer) Less(i, j int) bool { return strings.Compare((*o)[i].key, (*o)[j].key) < 0 } func (o *orderedValueContainer) Swap(i, j int) { (*o)[i], (*o)[j] = (*o)[j], (*o)[i] } var cmdNonNativeLinkExtract = &base.Command{ UsageLine: "{{.Exec}} engineering nonnativelinkextract", Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) return *fs }(), Run: func(cmd *base.Command, args []string) { content, err := io.ReadAll(os.Stdin) if err != nil { base.Fatalf("%s", err) } flattenedLink := nonnative.ExtractAllValuesFromBytes(content) var valueContainerOrdered orderedValueContainer for key, value := range flattenedLink.Values { valueContainerOrdered = append(valueContainerOrdered, valueContainer{key, value}) } sort.Sort(&valueContainerOrdered) for _, valueContainer := range valueContainerOrdered { io.WriteString(os.Stdout, fmt.Sprintf("%s=%s\n", valueContainer.key, valueContainer.value)) } }, } ================================================ FILE: main/commands/all/engineering/reversepb.go ================================================ package engineering import ( "bytes" "flag" "io" "os" "github.com/golang/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/infra/conf/jsonpb" "github.com/v2fly/v2ray-core/v5/infra/conf/v2jsonpb" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdReversePb = &base.Command{ UsageLine: "{{.Exec}} engineering reversepb [-f format]", Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) configFormat = fs.String("f", "v2jsonpb", "") return *fs }(), Run: func(cmd *base.Command, args []string) { cmd.Flag.Parse(args) configIn := bytes.NewBuffer(nil) io.Copy(configIn, os.Stdin) var conf core.Config if err := proto.Unmarshal(configIn.Bytes(), &conf); err != nil { base.Fatalf("%s", err) } switch *configFormat { case "jsonpb": if err := jsonpb.DumpJSONPb(&conf, os.Stdout); err != nil { base.Fatalf("%s", err) } case "v2jsonpb": if value, err := v2jsonpb.DumpV2JsonPb(&conf); err != nil { base.Fatalf("%s", err) } else { io.Copy(os.Stdout, bytes.NewReader(value)) } } }, } ================================================ FILE: main/commands/all/engineering/subscriptionEntriesExtract.go ================================================ package engineering import ( "archive/zip" "encoding/json" "flag" "fmt" "io" "os" "golang.org/x/crypto/sha3" "github.com/v2fly/v2ray-core/v5/app/subscription/containers" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdSubscriptionEntriesExtractInputName *string var cmdSubscriptionEntriesExtract = &base.Command{ UsageLine: "{{.Exec}} engineering subscriptionEntriesExtract", Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) cmdSubscriptionEntriesExtractInputName = fs.String("input", "", "") return *fs }(), Run: func(cmd *base.Command, args []string) { cmd.Flag.Parse(args) inputReader := os.Stdin if *cmdSubscriptionEntriesExtractInputName != "" { file, err := os.Open(*cmdSubscriptionEntriesExtractInputName) if err != nil { base.Fatalf("%s", err) } inputReader = file defer file.Close() } content, err := io.ReadAll(inputReader) if err != nil { base.Fatalf("%s", err) } parsed, err := containers.TryAllParsers(content, "") if err != nil { base.Fatalf("%s", err) } zipWriter := zip.NewWriter(os.Stdout) { writer, err := zipWriter.Create("meta.json") if err != nil { base.Fatalf("%s", err) } err = json.NewEncoder(writer).Encode(parsed.Metadata) if err != nil { base.Fatalf("%s", err) } } for k, entry := range parsed.ServerSpecs { hash := sha3.Sum256(entry.Content) fileName := fmt.Sprintf("entry_%v_%x", k, hash[:8]) writer, err := zipWriter.Create(fileName) if err != nil { base.Fatalf("%s", err) } _, err = writer.Write(entry.Content) if err != nil { base.Fatalf("%s", err) } } zipWriter.Close() }, } ================================================ FILE: main/commands/all/errors.generated.go ================================================ package all import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: main/commands/all/format_doc.go ================================================ package all import ( "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var docFormat = &base.Command{ UsageLine: "{{.Exec}} format-loader", Short: "config formats and loading", Long: ` {{.Exec}} is equipped with multiple loaders to support different config formats: * auto The default loader, supports all formats listed below, with format detecting, and mixed fomats support. * json (.json, .jsonc) The json loader, multiple files support, mergeable. * toml (.toml) The toml loader, multiple files support, mergeable. * yaml (.yml, .yaml) The yaml loader, multiple files support, mergeable. * protobuf / pb (.pb) Single file support, unmergeable. The following explains how format loaders behaves. Examples: {{.Exec}} run -d dir (1) {{.Exec}} run -c c1.json -c c2.yaml (2) {{.Exec}} run -format=json -d dir (3) {{.Exec}} test -c c1.yml -c c2.pb (4) {{.Exec}} test -format=pb -d dir (5) {{.Exec}} test -format=protobuf -c c1.json (6) (1) Load all supported files in the "dir". (2) JSON and YAML are merged and loaded. (3) Load all JSON files in the "dir". (4) Goes error since .pb is not mergeable to others (5) Works only when single .pb file found, if not, failed due to unmergeable. (6) Force load "c1.json" as protobuf, no matter its extension. `, } ================================================ FILE: main/commands/all/jsonv4/convert.go ================================================ package jsonv4 import ( "bytes" "encoding/json" "os" "strings" "github.com/pelletier/go-toml" "google.golang.org/protobuf/proto" "gopkg.in/yaml.v3" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/infra/conf/jsonpb" "github.com/v2fly/v2ray-core/v5/infra/conf/merge" "github.com/v2fly/v2ray-core/v5/infra/conf/v2jsonpb" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/main/commands/helpers" ) var cmdConvert = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} convert [c1.json] [.json] [dir1] ...", Short: "convert config files", Long: ` Convert config files between different formats. Files are merged before convert. Arguments: -i, -input The input format. Available values: "auto", "json", "toml", "yaml" Default: "auto" -o, -output The output format Available values: "json", "toml", "yaml", "protobuf" / "pb" Default: "json" -r Load folders recursively. Examples: {{.Exec}} {{.LongName}} -output=protobuf "path/to/dir" (1) {{.Exec}} {{.LongName}} -o=yaml config.toml (2) {{.Exec}} {{.LongName}} c1.json c2.json (3) {{.Exec}} {{.LongName}} -output=yaml c1.yaml .yaml (4) (1) Merge all supported files in dir and convert to protobuf (2) Convert toml to yaml (3) Merge json files (4) Merge yaml files Use "{{.Exec}} help config-merge" for more information about merge. `, } func init() { cmdConvert.Run = executeConvert // break init loop } var ( inputFormat string outputFormat string confDirRecursively bool ) func setConfArgs(cmd *base.Command) { cmd.Flag.StringVar(&inputFormat, "input", core.FormatAuto, "") cmd.Flag.StringVar(&inputFormat, "i", core.FormatAuto, "") cmd.Flag.StringVar(&outputFormat, "output", "json", "") cmd.Flag.StringVar(&outputFormat, "o", "json", "") cmd.Flag.BoolVar(&confDirRecursively, "r", false, "") } func executeConvert(cmd *base.Command, args []string) { setConfArgs(cmd) cmd.Flag.Parse(args) inputFormat = strings.ToLower(inputFormat) outputFormat = strings.ToLower(outputFormat) inputFormatMerge := inputFormat if inputFormat == "jsonv5" { inputFormatMerge = "json" } m, err := helpers.LoadConfigToMap(cmd.Flag.Args(), inputFormatMerge, confDirRecursively) if err != nil { base.Fatalf("failed to merge: %s", err) } err = merge.ApplyRules(m) if err != nil { base.Fatalf("failed to apply merge rules: %s", err) } var out []byte switch outputFormat { case core.FormatJSON: out, err = json.Marshal(m) if err != nil { base.Fatalf("failed to convert to json: %s", err) } case core.FormatTOML: out, err = toml.Marshal(m) if err != nil { base.Fatalf("failed to convert to toml: %s", err) } case core.FormatYAML: out, err = yaml.Marshal(m) if err != nil { base.Fatalf("failed to convert to yaml: %s", err) } case core.FormatProtobuf, core.FormatProtobufShort: data, err := json.Marshal(m) if err != nil { base.Fatalf("failed to marshal json: %s", err) } r := bytes.NewReader(data) pbConfig, err := core.LoadConfig(inputFormat, r) if err != nil { base.Fatalf("%v", err.Error()) } out, err = proto.Marshal(pbConfig) if err != nil { base.Fatalf("failed to convert to protobuf: %s", err) } case jsonpb.FormatProtobufJSONPB: data, err := json.Marshal(m) if err != nil { base.Fatalf("failed to marshal json: %s", err) } r := bytes.NewReader(data) pbConfig, err := core.LoadConfig(inputFormat, r) if err != nil { base.Fatalf("%v", err.Error()) } w := bytes.NewBuffer(nil) err = jsonpb.DumpJSONPb(pbConfig, w) if err != nil { base.Fatalf("%v", err.Error()) } out = w.Bytes() case v2jsonpb.FormatProtobufV2JSONPB: data, err := json.Marshal(m) if err != nil { base.Fatalf("failed to marshal json: %s", err) } r := bytes.NewReader(data) pbConfig, err := core.LoadConfig(inputFormat, r) if err != nil { base.Fatalf("%v", err.Error()) } out, err = v2jsonpb.DumpV2JsonPb(pbConfig) if err != nil { base.Fatalf("%v", err.Error()) } default: base.Errorf("invalid output format: %s", outputFormat) base.Errorf("Run '%s help %s' for details.", base.CommandEnv.Exec, cmd.LongName()) base.Exit() } if _, err := os.Stdout.Write(out); err != nil { base.Fatalf("failed to write stdout: %s", err) } } ================================================ FILE: main/commands/all/jsonv4/init.go ================================================ package jsonv4 import "github.com/v2fly/v2ray-core/v5/main/commands/base" func init() { base.RegisterCommand(cmdConvert) } ================================================ FILE: main/commands/all/love.go ================================================ package all import ( "bufio" "bytes" "compress/gzip" "encoding/base64" "fmt" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdLove = &base.Command{ UsageLine: "{{.Exec}} lovevictoria", Short: "", // set Short to "" hides the command Long: "", Run: executeLove, } func executeLove(cmd *base.Command, args []string) { const content = "H4sIAAAAAAAC/4SVMaskNwzH+/kUW6izcSthMGrcqLhVk0rdQS5cSMg7Xu4S0vizB8meZd57M3ta2GHX/ukvyZZmY2ZKDMzCzJyY5yOlxKII1omsf+qkBiiC6WhbYsbkjDAfySQsJqD3jtrD0EBM3sBHzG3kUsrglIQREXonpd47kYIi4AHmgI9Wcq2jlJITC6JZJ+v3ECYzBMAHyYm392yuY4zWsjACmHZSh6l3A0JETzGlWZqDsnArpTg62mhJONhOdO90p97V1BAnteoaOcuummtrrtuERQwUiJwP8a4KGKcyxdOCw1spOY+WHueFqmakAIgUSSuhwKNgobxKXSLbtg6r5cFmBiAeF6yCkYycmv+BiCIiW8ScHa3DgxAuZQbRhFNrLTFo96RBmx9jKWWG5nBsjyJzuIkftUblonppZU5t5LzwIks5L1a4lijagQxLokbIYwxfytNDC+XQqrWW9fzAunhqh5/Tg8PuaMw0d/Tcw3iDO81bHfWM/AnutMh2xqSUntMzd3wHDy9iHMQz8bmUZYvqedTJ5GgOnrNt7FIbSlwXE3wDI19n/KA38MsLaP4l89b5F8AV3ESOMIEhIBgezHBc0H6xV9KbaXwMvPcNvIHcC0C7UPZQx4JVTb35/AneSQq+bAYXsBmY7TCRupF2NTdVm/+ch22xa0pvRERKqt1oxj9DUbXzU84Gvj5hc5a81SlAUwMwgEs4T9+7sg9lb9h+908MWiKV8xtWciVTmnB3tivRjNerfXdxpfEBbq2NUvLMM5R9NLuyQg8nXT0PIh1xPd/wrcV49oJ6zbZaPlj2V87IY9T3F2XCOcW2MbZyZd49H+9m81E1N9SxlU+ff/1y+/f3719vf7788+Ugv/ffbMIH7ZNj0dsT4WMHHwLPu/Rp2O75uh99AK+N2xn7ZHq1OK6gczkN+9ngdOl1Qvki5xwSR8vFX6D+9vXA97B/+fr5rz9u/738uP328urP19vfP759e3n9Xs6jamvqlfJ/AAAA//+YAMZjDgkAAA==" c, err := base64.StdEncoding.DecodeString(content) common.Must(err) reader, err := gzip.NewReader(bytes.NewBuffer(c)) common.Must(err) b := make([]byte, 4096) nBytes, _ := reader.Read(b) bb := bytes.NewBuffer(b[:nBytes]) scanner := bufio.NewScanner(bb) for scanner.Scan() { s := scanner.Text() fmt.Print(s + platform.LineSeparator()) } } ================================================ FILE: main/commands/all/merge_doc.go ================================================ package all import ( "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var docMerge = &base.Command{ UsageLine: "{{.Exec}} config-merge", Short: "config merge logic", Long: ` Merging of config files is applied in following commands: {{.Exec}} run -c c1.json -c c2.json ... {{.Exec}} test -c c1.yaml -c c2.yaml ... {{.Exec}} convert c1.json dir1 ... {{.Exec}} api ado c1.json dir1 ... {{.Exec}} api rmi c1.json dir1 ... ... and more ... Support of toml and yaml is implemented by converting them to json, both merge and load. So we take json as example here. Suppose we have 2 JSON files, The 1st one: { "log": {"access": "some_value", "loglevel": "debug"}, "inbounds": [{"tag": "in-1"}], "outbounds": [{"_priority": 100, "tag": "out-1"}], "routing": {"rules": [ {"_tag":"default_route","inboundTag":["in-1"],"outboundTag":"out-1"} ]} } The 2nd one: { "log": {"loglevel": "error"}, "inbounds": [{"tag": "in-2"}], "outbounds": [{"_priority": -100, "tag": "out-2"}], "routing": {"rules": [ {"inboundTag":["in-2"],"outboundTag":"out-2"}, {"_tag":"default_route","inboundTag":["in-1.1"],"outboundTag":"out-1.1"} ]} } Output: { // loglevel is overwritten "log": {"access": "some_value", "loglevel": "error"}, "inbounds": [{"tag": "in-1"}, {"tag": "in-2"}], "outbounds": [ {"tag": "out-2"}, // note the order is affected by priority {"tag": "out-1"} ], "routing": {"rules": [ // note 3 rules are merged into 2, and outboundTag is overwritten, // because 2 of them has same tag {"inboundTag":["in-1","in-1.1"],"outboundTag":"out-1.1"} {"inboundTag":["in-2"],"outboundTag":"out-2"} ]} } Explained: - Simple values (string, number, boolean) are overwritten, others are merged - Elements with same "tag" (or "_tag") in an array will be merged - Add "_priority" property to array elements will help sort the array `, } ================================================ FILE: main/commands/all/tls/cert.go ================================================ package tls import ( "context" "crypto/x509" "encoding/json" "os" "strings" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) // cmdCert is the tls cert command var cmdCert = &base.Command{ UsageLine: "{{.Exec}} tls cert [--ca] [--domain=v2fly.org] [--expire=240h]", Short: "Generate TLS certificates", Long: ` Generate TLS certificates. Arguments: -domain The domain name for the certificate. -name The common name of this certificate -org The organization name for the certificate. -ca The certificate is a CA -json To output certificate to JSON -file The certificate path to save. -expire Expire days of the certificate. Default 90 days. `, } func init() { cmdCert.Run = executeCert // break init loop } var ( certDomainNames stringList _ = func() bool { cmdCert.Flag.Var(&certDomainNames, "domain", "Domain name for the certificate") return true }() certCommonName = cmdCert.Flag.String("name", "V2Ray Inc", "") certOrganization = cmdCert.Flag.String("org", "V2Ray Inc", "") certIsCA = cmdCert.Flag.Bool("ca", false, "") certJSONOutput = cmdCert.Flag.Bool("json", true, "") certFileOutput = cmdCert.Flag.String("file", "", "") certExpire = cmdCert.Flag.Uint("expire", 90, "") ) func executeCert(cmd *base.Command, args []string) { var opts []cert.Option if *certIsCA { opts = append(opts, cert.Authority(*certIsCA)) opts = append(opts, cert.KeyUsage(x509.KeyUsageCertSign|x509.KeyUsageKeyEncipherment|x509.KeyUsageDigitalSignature)) } opts = append(opts, cert.NotAfter(time.Now().Add(time.Duration(*certExpire)*time.Hour*24))) opts = append(opts, cert.CommonName(*certCommonName)) if len(certDomainNames) > 0 { opts = append(opts, cert.DNSNames(certDomainNames...)) } opts = append(opts, cert.Organization(*certOrganization)) cert, err := cert.Generate(nil, opts...) if err != nil { base.Fatalf("failed to generate TLS certificate: %s", err) } if *certJSONOutput { printJSON(cert) } if len(*certFileOutput) > 0 { if err := printFile(cert, *certFileOutput); err != nil { base.Fatalf("failed to save file: %s", err) } } } func printJSON(certificate *cert.Certificate) { certPEM, keyPEM := certificate.ToPEM() jCert := &jsonCert{ Certificate: strings.Split(strings.TrimSpace(string(certPEM)), "\n"), Key: strings.Split(strings.TrimSpace(string(keyPEM)), "\n"), } content, err := json.MarshalIndent(jCert, "", " ") common.Must(err) os.Stdout.Write(content) os.Stdout.WriteString("\n") } func writeFile(content []byte, name string) error { f, err := os.Create(name) if err != nil { return err } defer f.Close() return common.Error2(f.Write(content)) } func printFile(certificate *cert.Certificate, name string) error { certPEM, keyPEM := certificate.ToPEM() return task.Run(context.Background(), func() error { return writeFile(certPEM, name+"_cert.pem") }, func() error { return writeFile(keyPEM, name+"_key.pem") }) } type stringList []string func (l *stringList) String() string { return "String list" } func (l *stringList) Set(v string) error { if v == "" { base.Fatalf("empty value") } *l = append(*l, v) return nil } type jsonCert struct { Certificate []string `json:"certificate"` Key []string `json:"key"` } ================================================ FILE: main/commands/all/tls/chainhash.go ================================================ package tls import ( "fmt" "os" "github.com/v2fly/v2ray-core/v5/main/commands/base" v2tls "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) var cmdChainHash = &base.Command{ UsageLine: "{{.Exec}} tls certChainHash [--cert ]", Short: "Generate certificate chain hash for given certificate bundle", } func init() { cmdChainHash.Run = executeChainHash // break init loop } var certFile = cmdChainHash.Flag.String("cert", "cert.pem", "") func executeChainHash(cmd *base.Command, args []string) { if len(*certFile) == 0 { base.Fatalf("cert file not specified") } certContent, err := os.ReadFile(*certFile) if err != nil { base.Fatalf("Failed to read cert file: %s", err) return } certChainHashB64 := v2tls.CalculatePEMCertChainSHA256Hash(certContent) fmt.Println(certChainHashB64) } ================================================ FILE: main/commands/all/tls/ping.go ================================================ package tls import ( "crypto/tls" "crypto/x509" "encoding/base64" "fmt" "net" "github.com/v2fly/v2ray-core/v5/main/commands/base" v2tls "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) // cmdPing is the tls ping command var cmdPing = &base.Command{ UsageLine: "{{.Exec}} tls ping [-ip ] ", Short: "ping the domain with TLS handshake", Long: ` Ping the domain with TLS handshake. Arguments: -ip The IP address of the domain. `, } func init() { cmdPing.Run = executePing // break init loop } var pingIPStr = cmdPing.Flag.String("ip", "", "") func executePing(cmd *base.Command, args []string) { if cmdPing.Flag.NArg() < 1 { base.Fatalf("domain not specified") } domain := cmdPing.Flag.Arg(0) fmt.Println("Tls ping: ", domain) var ip net.IP if len(*pingIPStr) > 0 { v := net.ParseIP(*pingIPStr) if v == nil { base.Fatalf("invalid IP: %s", *pingIPStr) } ip = v } else { v, err := net.ResolveIPAddr("ip", domain) if err != nil { base.Fatalf("Failed to resolve IP: %s", err) } ip = v.IP } fmt.Println("Using IP: ", ip.String()) fmt.Println("-------------------") fmt.Println("Pinging without SNI") { tcpConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: ip, Port: 443}) if err != nil { base.Fatalf("Failed to dial tcp: %s", err) } tlsConn := tls.Client(tcpConn, &tls.Config{ InsecureSkipVerify: true, NextProtos: []string{"http/1.1"}, MaxVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12, // Do not release tool before v5's refactor // VerifyPeerCertificate: showCert(), }) err = tlsConn.Handshake() if err != nil { fmt.Println("Handshake failure: ", err) } else { fmt.Println("Handshake succeeded") printCertificates(tlsConn.ConnectionState().PeerCertificates) } tlsConn.Close() } fmt.Println("-------------------") fmt.Println("Pinging with SNI") { tcpConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: ip, Port: 443}) if err != nil { base.Fatalf("Failed to dial tcp: %s", err) } tlsConn := tls.Client(tcpConn, &tls.Config{ ServerName: domain, NextProtos: []string{"http/1.1"}, MaxVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12, // Do not release tool before v5's refactor // VerifyPeerCertificate: showCert(), }) err = tlsConn.Handshake() if err != nil { fmt.Println("handshake failure: ", err) } else { fmt.Println("handshake succeeded") printCertificates(tlsConn.ConnectionState().PeerCertificates) } tlsConn.Close() } fmt.Println("Tls ping finished") } func printCertificates(certs []*x509.Certificate) { for _, cert := range certs { if len(cert.DNSNames) == 0 { continue } fmt.Println("Allowed domains: ", cert.DNSNames) } } func showCert() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { hash := v2tls.GenerateCertChainHash(rawCerts) fmt.Println("Certificate Chain Hash: ", base64.StdEncoding.EncodeToString(hash)) return nil } } ================================================ FILE: main/commands/all/tls/tls.go ================================================ package tls import ( "github.com/v2fly/v2ray-core/v5/main/commands/base" ) // CmdTLS holds all tls sub commands var CmdTLS = &base.Command{ UsageLine: "{{.Exec}} tls", Short: "TLS tools", Long: `{{.Exec}} {{.LongName}} provides tools for TLS. `, Commands: []*base.Command{ cmdCert, cmdPing, cmdChainHash, }, } ================================================ FILE: main/commands/all/uuid.go ================================================ package all import ( "fmt" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdUUID = &base.Command{ UsageLine: "{{.Exec}} uuid", Short: "generate new UUID", Long: `Generate new UUID. `, Run: executeUUID, } func executeUUID(cmd *base.Command, args []string) { u := uuid.New() fmt.Println(u.String()) } ================================================ FILE: main/commands/all/verify.go ================================================ package all import ( "os" "github.com/v2fly/VSign/signerVerify" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) var cmdVerify = &base.Command{ UsageLine: "{{.Exec}} verify [--sig=sig-file] file", Short: "verify if a binary is officially signed", Long: ` Verify if a binary is officially signed. Arguments: -sig The path to the signature file `, } func init() { cmdVerify.Run = executeVerify // break init loop } var verifySigFile = cmdVerify.Flag.String("sig", "", "Path to the signature file") func executeVerify(cmd *base.Command, args []string) { target := cmdVerify.Flag.Arg(0) if target == "" { base.Fatalf("empty file path.") } if *verifySigFile == "" { base.Fatalf("empty signature path.") } sigReader, err := os.Open(os.ExpandEnv(*verifySigFile)) if err != nil { base.Fatalf("failed to open file %s: %s ", *verifySigFile, err) } files := cmdVerify.Flag.Args() err = signerVerify.OutputAndJudge(signerVerify.CheckSignaturesV2Fly(sigReader, files)) if err != nil { base.Fatalf("file is not officially signed by V2Ray: %s", err) } } ================================================ FILE: main/commands/base/command.go ================================================ // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package base defines shared basic pieces of the commands, // in particular logging and the Command structure. package base import ( "flag" "fmt" "os" "strings" "sync" ) // A Command is an implementation of a v2ray command // like v2ray run or v2ray version. type Command struct { // Run runs the command. // The args are the arguments after the command name. Run func(cmd *Command, args []string) // UsageLine is the one-line usage message. // The words between the first word (the "executable name") and the first flag or argument in the line are taken to be the command name. // // UsageLine supports go template syntax. It's recommended to use "{{.Exec}}" instead of hardcoding name UsageLine string // Short is the short description shown in the 'go help' output. // // Note: Short does not support go template syntax. Short string // Long is the long message shown in the 'go help ' output. // // Long supports go template syntax. It's recommended to use "{{.Exec}}", "{{.LongName}}" instead of hardcoding strings Long string // Flag is a set of flags specific to this command. Flag flag.FlagSet // CustomFlags indicates that the command will do its own // flag parsing. CustomFlags bool // Commands lists the available commands and help topics. // The order here is the order in which they are printed by 'go help'. // Note that subcommands are in general best avoided. Commands []*Command } // LongName returns the command's long name: all the words in the usage line between first word (e.g. "v2ray") and a flag or argument, func (c *Command) LongName() string { name := c.UsageLine if i := strings.Index(name, " ["); i >= 0 { name = strings.TrimSpace(name[:i]) } if i := strings.Index(name, " "); i >= 0 { name = name[i+1:] } else { name = "" } return strings.TrimSpace(name) } // Name returns the command's short name: the last word in the usage line before a flag or argument. func (c *Command) Name() string { name := c.LongName() if i := strings.LastIndex(name, " "); i >= 0 { name = name[i+1:] } return strings.TrimSpace(name) } // Usage prints usage of the Command func (c *Command) Usage() { buildCommandText(c) fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine) fmt.Fprintf(os.Stderr, "Run '%s help %s' for details.\n", CommandEnv.Exec, c.LongName()) SetExitStatus(2) Exit() } // Runnable reports whether the command can be run; otherwise // it is a documentation pseudo-command such as importpath. func (c *Command) Runnable() bool { return c.Run != nil } // Exit exits with code set with SetExitStatus() func Exit() { os.Exit(exitStatus) } // Fatalf logs error and exit with code 1 func Fatalf(format string, args ...interface{}) { Errorf(format, args...) Exit() } // Errorf logs error and set exit status to 1, but not exit func Errorf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, format, args...) fmt.Fprintln(os.Stderr) SetExitStatus(1) } // ExitIfErrors exits if current status is not zero func ExitIfErrors() { if exitStatus != 0 { Exit() } } var ( exitStatus = 0 exitMu sync.Mutex ) // SetExitStatus set exit status code func SetExitStatus(n int) { exitMu.Lock() if exitStatus < n { exitStatus = n } exitMu.Unlock() } // GetExitStatus get exit status code func GetExitStatus() int { return exitStatus } ================================================ FILE: main/commands/base/env.go ================================================ package base import ( "os" "path" ) // CommandEnvHolder is a struct holds the environment info of commands type CommandEnvHolder struct { // Excutable name of current binary Exec string // commands column width of current command CommandsWidth int } // CommandEnv holds the environment info of commands var CommandEnv CommandEnvHolder func init() { exec, err := os.Executable() if err != nil { return } CommandEnv.Exec = path.Base(exec) } ================================================ FILE: main/commands/base/execute.go ================================================ package base import ( "flag" "fmt" "os" "sort" "strings" ) // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // copied from "github.com/golang/go/main.go" // Execute excute the commands func Execute() { flag.Usage = func() { PrintUsage(os.Stderr, RootCommand) } flag.Parse() args := flag.Args() if len(args) < 1 { PrintUsage(os.Stderr, RootCommand) return } cmdName := args[0] // for error messages if args[0] == "help" { Help(os.Stdout, args[1:]) return } BigCmdLoop: for bigCmd := RootCommand; ; { for _, cmd := range bigCmd.Commands { if cmd.Name() != args[0] { continue } if len(cmd.Commands) > 0 { // test sub commands bigCmd = cmd args = args[1:] if len(args) == 0 { PrintUsage(os.Stderr, bigCmd) SetExitStatus(2) Exit() } if args[0] == "help" { // Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'. Help(os.Stdout, append(strings.Split(cmdName, " "), args[1:]...)) return } cmdName += " " + args[0] continue BigCmdLoop } if !cmd.Runnable() { continue } cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { args = args[1:] } else { cmd.Flag.Parse(args[1:]) args = cmd.Flag.Args() } buildCommandText(cmd) cmd.Run(cmd, args) Exit() return } helpArg := "" if i := strings.LastIndex(cmdName, " "); i >= 0 { helpArg = " " + cmdName[:i] } fmt.Fprintf(os.Stderr, "%s %s: unknown command\nRun '%s help%s' for usage.\n", CommandEnv.Exec, cmdName, CommandEnv.Exec, helpArg) SetExitStatus(2) Exit() } } // SortCommands sorts the first level sub commands func SortCommands() { sort.Slice(RootCommand.Commands, func(i, j int) bool { return SortLessFunc(RootCommand.Commands[i], RootCommand.Commands[j]) }) } // SortLessFunc used for sort commands list, can be override from outside var SortLessFunc = func(i, j *Command) bool { return i.Name() < j.Name() } ================================================ FILE: main/commands/base/help.go ================================================ // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package base import ( "bufio" "bytes" "fmt" "io" "os" "strings" "text/template" "unicode" "unicode/utf8" ) // Help implements the 'help' command. func Help(w io.Writer, args []string) { cmd := RootCommand Args: for i, arg := range args { for _, sub := range cmd.Commands { if sub.Name() == arg { cmd = sub continue Args } } // helpSuccess is the help command using as many args as possible that would succeed. helpSuccess := CommandEnv.Exec + " help" if i > 0 { helpSuccess += " " + strings.Join(args[:i], " ") } fmt.Fprintf(os.Stderr, "%s help %s: unknown help topic. Run '%s'.\n", CommandEnv.Exec, strings.Join(args, " "), helpSuccess) SetExitStatus(2) // failed at 'v2ray help cmd' Exit() } if len(cmd.Commands) > 0 { PrintUsage(os.Stdout, cmd) } else { buildCommandText(cmd) tmpl(os.Stdout, helpTemplate, makeTmplData(cmd)) } } var usageTemplate = `{{.Long | trim}} Usage: {{.UsageLine}} [arguments] The commands are: {{range .Commands}}{{if and (ne .Short "") (or (.Runnable) .Commands)}} {{.Name | width $.CommandsWidth}} {{.Short}}{{end}}{{end}} Use "{{.Exec}} help{{with .LongName}} {{.}}{{end}} " for more information about a command. {{if eq (.UsageLine) (.Exec)}} Additional help topics: {{range .Commands}}{{if and (not .Runnable) (not .Commands)}} {{.Name | width $.CommandsWidth}} {{.Short}}{{end}}{{end}} Use "{{.Exec}} help{{with .LongName}} {{.}}{{end}} " for more information about that topic. {{end}} ` var helpTemplate = `{{if .Runnable}}usage: {{.UsageLine}} {{end}}{{.Long | trim}} ` // An errWriter wraps a writer, recording whether a write error occurred. type errWriter struct { w io.Writer err error } func (w *errWriter) Write(b []byte) (int, error) { n, err := w.w.Write(b) if err != nil { w.err = err } return n, err } // tmpl executes the given template text on data, writing the result to w. func tmpl(w io.Writer, text string, data interface{}) { t := template.New("top") t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize, "width": width}) template.Must(t.Parse(text)) ew := &errWriter{w: w} err := t.Execute(ew, data) if ew.err != nil { // I/O error writing. Ignore write on closed pipe. if strings.Contains(ew.err.Error(), "pipe") { SetExitStatus(1) Exit() } Fatalf("writing output: %v", ew.err) } if err != nil { panic(err) } } func capitalize(s string) string { if s == "" { return s } r, n := utf8.DecodeRuneInString(s) return string(unicode.ToTitle(r)) + s[n:] } func width(width int, value string) string { format := fmt.Sprintf("%%-%ds", width) return fmt.Sprintf(format, value) } // PrintUsage prints usage of cmd to w func PrintUsage(w io.Writer, cmd *Command) { buildCommandText(cmd) bw := bufio.NewWriter(w) tmpl(bw, usageTemplate, makeTmplData(cmd)) bw.Flush() } // buildCommandText build command text as template func buildCommandText(cmd *Command) { data := makeTmplData(cmd) cmd.UsageLine = buildText(cmd.UsageLine, data) // DO NOT SUPPORT ".Short": // - It's not necessary // - Or, we have to build text for all sub commands of current command, like "v2ray help api" // cmd.Short = buildText(cmd.Short, data) cmd.Long = buildText(cmd.Long, data) } func buildText(text string, data interface{}) string { buf := bytes.NewBuffer([]byte{}) text = strings.ReplaceAll(text, "\t", " ") tmpl(buf, text, data) return buf.String() } type tmplData struct { *Command *CommandEnvHolder } func makeTmplData(cmd *Command) tmplData { // Minimum width of the command column width := 12 for _, c := range cmd.Commands { l := len(c.Name()) if width < l { width = l } } CommandEnv.CommandsWidth = width return tmplData{ Command: cmd, CommandEnvHolder: &CommandEnv, } } ================================================ FILE: main/commands/base/root.go ================================================ package base // RootCommand is the root command of all commands var RootCommand *Command func init() { RootCommand = &Command{ UsageLine: CommandEnv.Exec, Long: "The root command", } } // RegisterCommand register a command to RootCommand func RegisterCommand(cmd *Command) { RootCommand.Commands = append(RootCommand.Commands, cmd) } ================================================ FILE: main/commands/errors.generated.go ================================================ package commands import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: main/commands/helpers/config_load.go ================================================ package helpers import ( "bytes" "os" "github.com/v2fly/v2ray-core/v5/infra/conf/merge" "github.com/v2fly/v2ray-core/v5/infra/conf/mergers" "github.com/v2fly/v2ray-core/v5/infra/conf/serial" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" ) // LoadConfig load config files to *conf.Config, it will: // - resolve folder to files // - try to read stdin if no file specified func LoadConfig(files []string, format string, recursively bool) (*v4.Config, error) { m, err := LoadConfigToMap(files, format, recursively) if err != nil { return nil, err } bs, err := merge.FromMap(m) if err != nil { return nil, err } r := bytes.NewReader(bs) return serial.DecodeJSONConfig(r) } // LoadConfigToMap load config files to map, it will: // - resolve folder to files // - try to read stdin if no file specified func LoadConfigToMap(files []string, format string, recursively bool) (map[string]interface{}, error) { var err error if len(files) > 0 { var extensions []string extensions, err := mergers.GetExtensions(format) if err != nil { return nil, err } files, err = ResolveFolderToFiles(files, extensions, recursively) if err != nil { return nil, err } } m := make(map[string]interface{}) if len(files) == 0 { err = mergers.MergeAs(format, os.Stdin, m) } else { err = mergers.MergeAs(format, files, m) } if err != nil { return nil, err } return m, nil } ================================================ FILE: main/commands/helpers/fs.go ================================================ package helpers import ( "fmt" "os" "path/filepath" "strings" ) // ReadDir finds files according to extensions in the dir func ReadDir(dir string, extensions []string) ([]string, error) { confs, err := os.ReadDir(dir) if err != nil { return nil, err } files := make([]string, 0) for _, f := range confs { ext := filepath.Ext(f.Name()) for _, e := range extensions { if strings.EqualFold(ext, e) { files = append(files, filepath.Join(dir, f.Name())) break } } } return files, nil } // ReadDirRecursively finds files according to extensions in the dir recursively func ReadDirRecursively(dir string, extensions []string) ([]string, error) { files := make([]string, 0) err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { ext := filepath.Ext(path) for _, e := range extensions { if strings.EqualFold(ext, e) { files = append(files, path) break } } return nil }) if err != nil { return nil, err } return files, nil } // ResolveFolderToFiles expands folder path (if any and it exists) to file paths. // Any other paths, like file, even URL, it returns them as is. func ResolveFolderToFiles(paths []string, extensions []string, recursively bool) ([]string, error) { dirReader := ReadDir if recursively { dirReader = ReadDirRecursively } files := make([]string, 0) for _, p := range paths { i, err := os.Stat(p) if err == nil && i.IsDir() { fs, err := dirReader(p, extensions) if err != nil { return nil, fmt.Errorf("failed to read dir %s: %s", p, err) } files = append(files, fs...) continue } files = append(files, p) } return files, nil } ================================================ FILE: main/commands/run.go ================================================ package commands import ( "fmt" "log" "os" "os/signal" "path/filepath" "runtime" "strings" "syscall" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/cmdarg" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/main/plugins" ) // CmdRun runs V2Ray with config var CmdRun = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} run [-c config.json] [-d dir]", Short: "run V2Ray with config", Long: ` Run V2Ray with config. {{.Exec}} will also use the config directory specified by environment variable "v2ray.location.confdir". If no config found, it tries to load config from one of below: 1. The default "config.json" in the current directory 2. The config file from ENV "v2ray.location.config" 3. The stdin if all failed above Arguments: -c, -config Config file for V2Ray. Multiple assign is accepted. -d, -confdir A directory with config files. Multiple assign is accepted. -r Load confdir recursively. -format Format of config input. (default "auto") Examples: {{.Exec}} {{.LongName}} -c config.json {{.Exec}} {{.LongName}} -d path/to/dir Use "{{.Exec}} help format-loader" for more information about format. `, Run: executeRun, } var ( configFiles cmdarg.Arg configDirs cmdarg.Arg configFormat *string configDirRecursively *bool ) func setConfigFlags(cmd *base.Command) { configFormat = cmd.Flag.String("format", core.FormatAuto, "") configDirRecursively = cmd.Flag.Bool("r", false, "") cmd.Flag.Var(&configFiles, "config", "") cmd.Flag.Var(&configFiles, "c", "") cmd.Flag.Var(&configDirs, "confdir", "") cmd.Flag.Var(&configDirs, "d", "") } func executeRun(cmd *base.Command, args []string) { setConfigFlags(cmd) var pluginFuncs []func() error for _, plugin := range plugins.Plugins { if f := plugin(cmd); f != nil { pluginFuncs = append(pluginFuncs, f) } } cmd.Flag.Parse(args) printVersion() configFiles = getConfigFilePath() server, err := startV2Ray() if err != nil { base.Fatalf("Failed to start: %s", err) } for _, f := range pluginFuncs { go func(f func() error) { if err := f(); err != nil { log.Print(err) } }(f) } if err := server.Start(); err != nil { base.Fatalf("Failed to start: %s", err) } defer server.Close() // Explicitly triggering GC to remove garbage from config loading. runtime.GC() { osSignals := make(chan os.Signal, 1) signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM) <-osSignals } } func fileExists(file string) bool { info, err := os.Stat(file) return err == nil && !info.IsDir() } func dirExists(file string) bool { if file == "" { return false } info, err := os.Stat(file) return err == nil && info.IsDir() } func readConfDir(dirPath string, extension []string) cmdarg.Arg { confs, err := os.ReadDir(dirPath) if err != nil { base.Fatalf("failed to read dir %s: %s", dirPath, err) } files := make(cmdarg.Arg, 0) for _, f := range confs { ext := filepath.Ext(f.Name()) for _, e := range extension { if strings.EqualFold(e, ext) { files.Set(filepath.Join(dirPath, f.Name())) break } } } return files } // getFolderFiles get files in the folder and it's children func readConfDirRecursively(dirPath string, extension []string) cmdarg.Arg { files := make(cmdarg.Arg, 0) err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { ext := filepath.Ext(path) for _, e := range extension { if strings.EqualFold(e, ext) { files.Set(path) break } } return nil }) if err != nil { base.Fatalf("failed to read dir %s: %s", dirPath, err) } return files } func getConfigFilePath() cmdarg.Arg { extension, err := core.GetLoaderExtensions(*configFormat) if err != nil { base.Fatalf("%v", err.Error()) } dirReader := readConfDir if *configDirRecursively { dirReader = readConfDirRecursively } if len(configDirs) > 0 { for _, d := range configDirs { log.Println("Using confdir from arg:", d) configFiles = append(configFiles, dirReader(d, extension)...) } } else if envConfDir := platform.GetConfDirPath(); dirExists(envConfDir) { log.Println("Using confdir from env:", envConfDir) configFiles = append(configFiles, dirReader(envConfDir, extension)...) } if len(configFiles) > 0 { return configFiles } if len(configFiles) == 0 && len(configDirs) > 0 { base.Fatalf("no config file found with extension: %s", extension) } if workingDir, err := os.Getwd(); err == nil { configFile := filepath.Join(workingDir, "config.json") if fileExists(configFile) { log.Println("Using default config: ", configFile) return cmdarg.Arg{configFile} } } if configFile := platform.GetConfigurationPath(); fileExists(configFile) { log.Println("Using config from env: ", configFile) return cmdarg.Arg{configFile} } return nil } func startV2Ray() (core.Server, error) { config, err := core.LoadConfig(*configFormat, configFiles) if err != nil { if len(configFiles) == 0 { err = newError("failed to load config").Base(err) } else { err = newError(fmt.Sprintf("failed to load config: %s", configFiles)).Base(err) } return nil, err } server, err := core.New(config) if err != nil { return nil, newError("failed to create server").Base(err) } return server, nil } ================================================ FILE: main/commands/test.go ================================================ package commands import ( "fmt" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) // CmdTest tests config files var CmdTest = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} test [-format=json] [-c config.json] [-d dir]", Short: "test config files", Long: ` Test config files, without launching V2Ray server. {{.Exec}} will also use the config directory specified by environment variable "v2ray.location.confdir". If no config found, it tries to load config from one of below: 1. The default "config.json" in the current directory 2. The config file from ENV "v2ray.location.config" 3. The stdin if all failed above Arguments: -c, -config Config file for V2Ray. Multiple assign is accepted. -d, -confdir A directory with config files. Multiple assign is accepted. -r Load confdir recursively. -format Format of config input. (default "auto") Examples: {{.Exec}} {{.LongName}} -c config.json {{.Exec}} {{.LongName}} -d path/to/dir Use "{{.Exec}} help format-loader" for more information about format. `, Run: executeTest, } func executeTest(cmd *base.Command, args []string) { setConfigFlags(cmd) cmd.Flag.Parse(args) printVersion() configFiles = getConfigFilePath() _, err := startV2Ray() if err != nil { base.Fatalf("Test failed: %s", err) } fmt.Println("Configuration OK.") } ================================================ FILE: main/commands/version.go ================================================ package commands import ( "fmt" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/main/commands/base" ) // CmdVersion prints V2Ray Versions var CmdVersion = &base.Command{ UsageLine: "{{.Exec}} version", Short: "print V2Ray version", Long: `Prints the build information for V2Ray. `, Run: executeVersion, } func executeVersion(cmd *base.Command, args []string) { printVersion() } func printVersion() { version := core.VersionStatement() for _, s := range version { fmt.Println(s) } } ================================================ FILE: main/distro/all/all.go ================================================ package all import ( // The following are necessary as they register handlers in their init functions. // Mandatory features. Can't remove unless there are replacements. _ "github.com/v2fly/v2ray-core/v5/app/dispatcher" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/inbound" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound" // Default commander and all its services. This is an optional feature. _ "github.com/v2fly/v2ray-core/v5/app/commander" _ "github.com/v2fly/v2ray-core/v5/app/log/command" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/command" _ "github.com/v2fly/v2ray-core/v5/app/stats/command" // Developer preview services _ "github.com/v2fly/v2ray-core/v5/app/instman/command" _ "github.com/v2fly/v2ray-core/v5/app/observatory/command" // Other optional features. _ "github.com/v2fly/v2ray-core/v5/app/dns" _ "github.com/v2fly/v2ray-core/v5/app/dns/fakedns" _ "github.com/v2fly/v2ray-core/v5/app/log" _ "github.com/v2fly/v2ray-core/v5/app/policy" _ "github.com/v2fly/v2ray-core/v5/app/reverse" _ "github.com/v2fly/v2ray-core/v5/app/router" _ "github.com/v2fly/v2ray-core/v5/app/stats" // Fix dependency cycle caused by core import in internet package _ "github.com/v2fly/v2ray-core/v5/transport/internet/tagged/taggedimpl" // Developer preview features _ "github.com/v2fly/v2ray-core/v5/app/commander/webcommander" _ "github.com/v2fly/v2ray-core/v5/app/instman" _ "github.com/v2fly/v2ray-core/v5/app/observatory" _ "github.com/v2fly/v2ray-core/v5/app/persistentstorage/filesystemstorage" _ "github.com/v2fly/v2ray-core/v5/app/tun" // Inbound and outbound proxies. _ "github.com/v2fly/v2ray-core/v5/proxy/blackhole" _ "github.com/v2fly/v2ray-core/v5/proxy/dns" _ "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" _ "github.com/v2fly/v2ray-core/v5/proxy/freedom" _ "github.com/v2fly/v2ray-core/v5/proxy/http" _ "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" _ "github.com/v2fly/v2ray-core/v5/proxy/socks" _ "github.com/v2fly/v2ray-core/v5/proxy/trojan" _ "github.com/v2fly/v2ray-core/v5/proxy/vless/inbound" _ "github.com/v2fly/v2ray-core/v5/proxy/vless/outbound" _ "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" _ "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" // Developer preview proxies _ "github.com/v2fly/v2ray-core/v5/proxy/vlite/inbound" _ "github.com/v2fly/v2ray-core/v5/proxy/vlite/outbound" _ "github.com/v2fly/v2ray-core/v5/proxy/hysteria2" _ "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks2022" // Transports _ "github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket" _ "github.com/v2fly/v2ray-core/v5/transport/internet/grpc" _ "github.com/v2fly/v2ray-core/v5/transport/internet/http" _ "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" _ "github.com/v2fly/v2ray-core/v5/transport/internet/quic" _ "github.com/v2fly/v2ray-core/v5/transport/internet/tcp" _ "github.com/v2fly/v2ray-core/v5/transport/internet/tls" _ "github.com/v2fly/v2ray-core/v5/transport/internet/tls/utls" _ "github.com/v2fly/v2ray-core/v5/transport/internet/udp" _ "github.com/v2fly/v2ray-core/v5/transport/internet/websocket" // Developer preview transports _ "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembly" _ "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/simple" _ "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripper/httprt" _ "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/packetconn" _ "github.com/v2fly/v2ray-core/v5/transport/internet/request/stereotype/meek" _ "github.com/v2fly/v2ray-core/v5/transport/internet/request/stereotype/mekya" _ "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripperreverserserver/clicommand" _ "github.com/v2fly/v2ray-core/v5/transport/internet/dtls" _ "github.com/v2fly/v2ray-core/v5/transport/internet/httpupgrade" _ "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2" _ "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation" _ "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/server" _ "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment/clicommand" // Transport headers _ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http" _ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/noop" _ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/srtp" _ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/tls" _ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/utp" _ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wechat" _ "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wireguard" // Geo loaders _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/memconservative" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/standard" // JSON, TOML, YAML config support. (jsonv4) This disable selective compile _ "github.com/v2fly/v2ray-core/v5/main/formats" // commands _ "github.com/v2fly/v2ray-core/v5/main/commands/all" // engineering commands _ "github.com/v2fly/v2ray-core/v5/main/commands/all/engineering" _ "github.com/v2fly/v2ray-core/v5/main/commands/all/engineering/generateRandomData" // Commands that rely on jsonv4 format This disable selective compile _ "github.com/v2fly/v2ray-core/v5/main/commands/all/api/jsonv4" _ "github.com/v2fly/v2ray-core/v5/main/commands/all/jsonv4" // V5 version of json configure file parser _ "github.com/v2fly/v2ray-core/v5/infra/conf/v5cfg" // Simplified config _ "github.com/v2fly/v2ray-core/v5/proxy/http/simplified" _ "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks/simplified" _ "github.com/v2fly/v2ray-core/v5/proxy/socks/simplified" _ "github.com/v2fly/v2ray-core/v5/proxy/trojan/simplified" // Subscription Supports _ "github.com/v2fly/v2ray-core/v5/app/subscription/subscriptionmanager" _ "github.com/v2fly/v2ray-core/v5/app/subscription/subscriptionmanager/command" // Subscription Containers: general purpose _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/base64urlline" _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/dataurlsingle" _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/jsonfieldarray" _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/jsonfieldarray/jsonified" _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/urlline" // Subscription Fetchers _ "github.com/v2fly/v2ray-core/v5/app/subscription/documentfetcher/dataurlfetcher" _ "github.com/v2fly/v2ray-core/v5/app/subscription/documentfetcher/httpfetcher" // Subscription Entries Converters _ "github.com/v2fly/v2ray-core/v5/app/subscription/entries/nonnative" _ "github.com/v2fly/v2ray-core/v5/app/subscription/entries/outbound" // Natively Supported Outbound Format ) ================================================ FILE: main/distro/all/pion.go ================================================ //go:build !android package all import _ "github.com/v2fly/v2ray-core/v5/common/natTraversal/stun/stuncli" ================================================ FILE: main/distro/all/wireguard.go ================================================ //go:build !dragonfly package all import ( // WireGuard Outbound is unreleased. _ "github.com/v2fly/v2ray-core/v5/proxy/wireguard/outbound" ) ================================================ FILE: main/distro/debug/debug.go ================================================ package debug import ( "net/http" ) func init() { go func() { http.ListenAndServe(":6060", nil) }() } ================================================ FILE: main/errors.generated.go ================================================ package main import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: main/formats/errors.generated.go ================================================ package formats import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: main/formats/formats.go ================================================ package formats import ( "bytes" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/infra/conf/merge" "github.com/v2fly/v2ray-core/v5/infra/conf/mergers" "github.com/v2fly/v2ray-core/v5/infra/conf/serial" ) func init() { for _, formatName := range mergers.GetAllNames() { loader, err := makeMergeLoader(formatName) if err != nil { panic(err) } if formatName == core.FormatAuto { loader.Extension = nil } common.Must(core.RegisterConfigLoader(loader)) } } func makeMergeLoader(formatName string) (*core.ConfigFormat, error) { extensions, err := mergers.GetExtensions(formatName) if err != nil { return nil, err } return &core.ConfigFormat{ Name: []string{formatName}, Extension: extensions, Loader: makeLoaderFunc(formatName), }, nil } func makeLoaderFunc(formatName string) core.ConfigLoader { return func(input interface{}) (*core.Config, error) { m := make(map[string]interface{}) err := mergers.MergeAs(formatName, input, m) if err != nil { return nil, err } data, err := merge.FromMap(m) if err != nil { return nil, err } r := bytes.NewReader(data) cf, err := serial.DecodeJSONConfig(r) if err != nil { return nil, err } return cf.Build() } } ================================================ FILE: main/main.go ================================================ package main import ( "github.com/v2fly/v2ray-core/v5/main/commands" "github.com/v2fly/v2ray-core/v5/main/commands/base" _ "github.com/v2fly/v2ray-core/v5/main/distro/all" ) func main() { base.RootCommand.Long = "A unified platform for anti-censorship." base.RegisterCommand(commands.CmdRun) base.RegisterCommand(commands.CmdVersion) base.RegisterCommand(commands.CmdTest) base.SortLessFunc = runIsTheFirst base.SortCommands() base.Execute() } func runIsTheFirst(i, j *base.Command) bool { left := i.Name() right := j.Name() if left == "run" { return true } if right == "run" { return false } return left < right } ================================================ FILE: main/main_test.go ================================================ //go:build coveragemain // +build coveragemain package main import ( "testing" ) func TestRunMainForCoverage(t *testing.T) { main() } ================================================ FILE: main/plugins/plugin.go ================================================ package plugins import "github.com/v2fly/v2ray-core/v5/main/commands/base" var Plugins []Plugin type Plugin func(*base.Command) func() error func RegisterPlugin(plugin Plugin) { Plugins = append(Plugins, plugin) } ================================================ FILE: main/plugins/plugin_pprof/plugin_pprof.go ================================================ package plugin_pprof //nolint: stylecheck import ( "net/http" "net/http/pprof" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/main/plugins" ) var pprofPlugin plugins.Plugin = func(cmd *base.Command) func() error { addr := cmd.Flag.String("pprof", "", "") return func() error { if *addr != "" { h := http.NewServeMux() h.HandleFunc("/debug/pprof/", pprof.Index) h.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) h.HandleFunc("/debug/pprof/profile", pprof.Profile) h.HandleFunc("/debug/pprof/symbol", pprof.Symbol) h.HandleFunc("/debug/pprof/trace", pprof.Trace) return (&http.Server{Addr: *addr, Handler: h}).ListenAndServe() } return nil } } func init() { plugins.RegisterPlugin(pprofPlugin) } ================================================ FILE: main/v2binding/startUp.go ================================================ package v2binding import ( "context" "fmt" "os" "path" "github.com/v2fly/v2ray-core/v5/features/extension" ) func (b *bindingInstance) setBaseDir(baseDir string) { b.baseDir = baseDir attentionFile := path.Join(baseDir, "place your config file here.txt") { f, err := os.OpenFile(attentionFile, os.O_RDONLY|os.O_CREATE, 0o666) if err != nil { return } _ = f.Close() } _ = os.Chdir(baseDir) } func (b *bindingInstance) loadDefaultConfigIfExists() error { config, err := os.ReadFile(path.Join(b.baseDir, "config.json")) if err != nil { return err } instanceManagement := b.instance.GetFeature(extension.InstanceManagementType()) if instanceManagement == nil { return fmt.Errorf("instance management type not found") } instance, ok := instanceManagement.(extension.InstanceManagement) if !ok { return fmt.Errorf("instance management instance is invalid") } ctx := context.TODO() err = instance.AddInstance(ctx, "default", config, "jsonv5") if err != nil { return err } err = instance.StartInstance(ctx, "default") if err != nil { return err } return nil } ================================================ FILE: main/v2binding/v2api/api.go ================================================ package main import ( "time" "github.com/v2fly/v2ray-core/v5/main/v2binding" ) func main() { v2binding.StartAPIInstance(".") for { time.Sleep(time.Minute) } } ================================================ FILE: main/v2binding/v2binding.go ================================================ package v2binding import ( "fmt" "os" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/commander" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/instman" "github.com/v2fly/v2ray-core/v5/app/instman/command" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" _ "github.com/v2fly/v2ray-core/v5/main/distro/all" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" ) type bindingInstance struct { started bool instance *core.Instance baseDir string } var binding bindingInstance func (b *bindingInstance) startAPIInstance() { bindConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&instman.Config{}), serial.ToTypedMessage(&commander.Config{ Tag: "api", Service: []*anypb.Any{ serial.ToTypedMessage(&command.Config{}), }, }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { InboundTag: []string{"api"}, TargetTag: &router.RoutingRule_Tag{ Tag: "api", }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { Tag: "api", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(10999), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(10999), Networks: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { Tag: "default-outbound", ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, }, } bindConfig = withDefaultApps(bindConfig) instance, err := core.New(bindConfig) if err != nil { panic(err) } err = instance.Start() if err != nil { panic(err) } b.instance = instance } func withDefaultApps(config *core.Config) *core.Config { config.App = append(config.App, serial.ToTypedMessage(&dispatcher.Config{})) config.App = append(config.App, serial.ToTypedMessage(&proxyman.InboundConfig{})) config.App = append(config.App, serial.ToTypedMessage(&proxyman.OutboundConfig{})) return config } func StartAPIInstance(basedir string) { if binding.started { return } binding.started = true binding.setBaseDir(basedir) binding.startAPIInstance() { err := binding.loadDefaultConfigIfExists() if err != nil { fmt.Println(err) os.WriteFile("last_err.txt", []byte(err.Error()), 0o644) } } } ================================================ FILE: mocks.go ================================================ package core //go:generate env GOBIN=$PWD go install -v github.com/golang/mock/mockgen@latest //go:generate ./mockgen -package mocks -destination testing/mocks/io.go -mock_names Reader=Reader,Writer=Writer io Reader,Writer //go:generate ./mockgen -package mocks -destination testing/mocks/log.go -mock_names Handler=LogHandler github.com/v2fly/v2ray-core/v5/common/log Handler //go:generate ./mockgen -package mocks -destination testing/mocks/mux.go -mock_names ClientWorkerFactory=MuxClientWorkerFactory github.com/v2fly/v2ray-core/v5/common/mux ClientWorkerFactory //go:generate ./mockgen -package mocks -destination testing/mocks/dns.go -mock_names Client=DNSClient github.com/v2fly/v2ray-core/v5/features/dns Client //go:generate ./mockgen -package mocks -destination testing/mocks/outbound.go -mock_names Manager=OutboundManager,HandlerSelector=OutboundHandlerSelector github.com/v2fly/v2ray-core/v5/features/outbound Manager,HandlerSelector //go:generate ./mockgen -package mocks -destination testing/mocks/proxy.go -mock_names Inbound=ProxyInbound,Outbound=ProxyOutbound github.com/v2fly/v2ray-core/v5/proxy Inbound,Outbound ================================================ FILE: proto.go ================================================ package core //go:generate go install -v google.golang.org/protobuf/cmd/protoc-gen-go@latest //go:generate go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest //go:generate go run ./infra/vprotogen/ ================================================ FILE: proxy/blackhole/blackhole.go ================================================ // Package blackhole is an outbound handler that blocks all connections. package blackhole //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" ) // Handler is an outbound connection that silently swallow the entire payload. type Handler struct { response ResponseConfig } // New creates a new blackhole handler. func New(ctx context.Context, config *Config) (*Handler, error) { response, err := config.GetInternalResponse() if err != nil { return nil, err } return &Handler{ response: response, }, nil } // Process implements OutboundHandler.Dispatch(). func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { nBytes := h.response.WriteTo(link.Writer) if nBytes > 0 { // Sleep a little here to make sure the response is sent to client. time.Sleep(time.Second) } common.Interrupt(link.Writer) return nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*SimplifiedConfig) _ = simplifiedServer fullConfig := &Config{} return common.CreateObject(ctx, fullConfig) })) } ================================================ FILE: proxy/blackhole/blackhole_test.go ================================================ package blackhole_test import ( "context" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) func TestBlackHoleHTTPResponse(t *testing.T) { handler, err := blackhole.New(context.Background(), &blackhole.Config{ Response: serial.ToTypedMessage(&blackhole.HTTPResponse{}), }) common.Must(err) reader, writer := pipe.New(pipe.WithoutSizeLimit()) readerError := make(chan error) var mb buf.MultiBuffer go func() { b, e := reader.ReadMultiBuffer() mb = b readerError <- e }() link := transport.Link{ Reader: reader, Writer: writer, } common.Must(handler.Process(context.Background(), &link, nil)) common.Must(<-readerError) if mb.IsEmpty() { t.Error("expect http response, but nothing") } } ================================================ FILE: proxy/blackhole/config.go ================================================ package blackhole import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/serial" ) const ( http403response = `HTTP/1.1 403 Forbidden Connection: close Cache-Control: max-age=3600, public Content-Length: 0 ` ) // ResponseConfig is the configuration for blackhole responses. type ResponseConfig interface { // WriteTo writes predefined response to the give buffer. WriteTo(buf.Writer) int32 } // WriteTo implements ResponseConfig.WriteTo(). func (*NoneResponse) WriteTo(buf.Writer) int32 { return 0 } // WriteTo implements ResponseConfig.WriteTo(). func (*HTTPResponse) WriteTo(writer buf.Writer) int32 { b := buf.New() common.Must2(b.WriteString(http403response)) n := b.Len() writer.WriteMultiBuffer(buf.MultiBuffer{b}) return n } // GetInternalResponse converts response settings from proto to internal data structure. func (c *Config) GetInternalResponse() (ResponseConfig, error) { if c.GetResponse() == nil { return new(NoneResponse), nil } config, err := serial.GetInstanceOf(c.GetResponse()) if err != nil { return nil, err } return config.(ResponseConfig), nil } ================================================ FILE: proxy/blackhole/config.pb.go ================================================ package blackhole import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type NoneResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NoneResponse) Reset() { *x = NoneResponse{} mi := &file_proxy_blackhole_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NoneResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*NoneResponse) ProtoMessage() {} func (x *NoneResponse) ProtoReflect() protoreflect.Message { mi := &file_proxy_blackhole_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NoneResponse.ProtoReflect.Descriptor instead. func (*NoneResponse) Descriptor() ([]byte, []int) { return file_proxy_blackhole_config_proto_rawDescGZIP(), []int{0} } type HTTPResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HTTPResponse) Reset() { *x = HTTPResponse{} mi := &file_proxy_blackhole_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HTTPResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*HTTPResponse) ProtoMessage() {} func (x *HTTPResponse) ProtoReflect() protoreflect.Message { mi := &file_proxy_blackhole_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HTTPResponse.ProtoReflect.Descriptor instead. func (*HTTPResponse) Descriptor() ([]byte, []int) { return file_proxy_blackhole_config_proto_rawDescGZIP(), []int{1} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Response *anypb.Any `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_blackhole_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_blackhole_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_blackhole_config_proto_rawDescGZIP(), []int{2} } func (x *Config) GetResponse() *anypb.Any { if x != nil { return x.Response } return nil } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_proxy_blackhole_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_blackhole_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_proxy_blackhole_config_proto_rawDescGZIP(), []int{3} } var File_proxy_blackhole_config_proto protoreflect.FileDescriptor const file_proxy_blackhole_config_proto_rawDesc = "" + "\n" + "\x1cproxy/blackhole/config.proto\x12\x1av2ray.core.proxy.blackhole\x1a\x19google/protobuf/any.proto\x1a common/protoext/extensions.proto\"\x0e\n" + "\fNoneResponse\"\x0e\n" + "\fHTTPResponse\":\n" + "\x06Config\x120\n" + "\bresponse\x18\x01 \x01(\v2\x14.google.protobuf.AnyR\bresponse\"-\n" + "\x10SimplifiedConfig:\x19\x82\xb5\x18\x15\n" + "\boutbound\x12\tblackholeBo\n" + "\x1ecom.v2ray.core.proxy.blackholeP\x01Z.github.com/v2fly/v2ray-core/v5/proxy/blackhole\xaa\x02\x1aV2Ray.Core.Proxy.Blackholeb\x06proto3" var ( file_proxy_blackhole_config_proto_rawDescOnce sync.Once file_proxy_blackhole_config_proto_rawDescData []byte ) func file_proxy_blackhole_config_proto_rawDescGZIP() []byte { file_proxy_blackhole_config_proto_rawDescOnce.Do(func() { file_proxy_blackhole_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_blackhole_config_proto_rawDesc), len(file_proxy_blackhole_config_proto_rawDesc))) }) return file_proxy_blackhole_config_proto_rawDescData } var file_proxy_blackhole_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_proxy_blackhole_config_proto_goTypes = []any{ (*NoneResponse)(nil), // 0: v2ray.core.proxy.blackhole.NoneResponse (*HTTPResponse)(nil), // 1: v2ray.core.proxy.blackhole.HTTPResponse (*Config)(nil), // 2: v2ray.core.proxy.blackhole.Config (*SimplifiedConfig)(nil), // 3: v2ray.core.proxy.blackhole.SimplifiedConfig (*anypb.Any)(nil), // 4: google.protobuf.Any } var file_proxy_blackhole_config_proto_depIdxs = []int32{ 4, // 0: v2ray.core.proxy.blackhole.Config.response:type_name -> google.protobuf.Any 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_proxy_blackhole_config_proto_init() } func file_proxy_blackhole_config_proto_init() { if File_proxy_blackhole_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_blackhole_config_proto_rawDesc), len(file_proxy_blackhole_config_proto_rawDesc)), NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_blackhole_config_proto_goTypes, DependencyIndexes: file_proxy_blackhole_config_proto_depIdxs, MessageInfos: file_proxy_blackhole_config_proto_msgTypes, }.Build() File_proxy_blackhole_config_proto = out.File file_proxy_blackhole_config_proto_goTypes = nil file_proxy_blackhole_config_proto_depIdxs = nil } ================================================ FILE: proxy/blackhole/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.blackhole; option csharp_namespace = "V2Ray.Core.Proxy.Blackhole"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/blackhole"; option java_package = "com.v2ray.core.proxy.blackhole"; option java_multiple_files = true; import "google/protobuf/any.proto"; import "common/protoext/extensions.proto"; message NoneResponse {} message HTTPResponse {} message Config { google.protobuf.Any response = 1; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "blackhole"; } ================================================ FILE: proxy/blackhole/config_test.go ================================================ package blackhole_test import ( "bufio" "net/http" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/proxy/blackhole" ) func TestHTTPResponse(t *testing.T) { buffer := buf.New() httpResponse := new(HTTPResponse) httpResponse.WriteTo(buf.NewWriter(buffer)) reader := bufio.NewReader(buffer) response, err := http.ReadResponse(reader, nil) common.Must(err) defer response.Body.Close() if response.StatusCode != 403 { t.Error("expected status code 403, but got ", response.StatusCode) } } ================================================ FILE: proxy/blackhole/errors.generated.go ================================================ package blackhole import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/dns/config.pb.go ================================================ package dns import ( net "github.com/v2fly/v2ray-core/v5/common/net" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // Server is the DNS server address. If specified, this address overrides the // original one. Server *net.Endpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` UserLevel uint32 `protobuf:"varint,2,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` OverrideResponseTtl bool `protobuf:"varint,4,opt,name=override_response_ttl,json=overrideResponseTtl,proto3" json:"override_response_ttl,omitempty"` ResponseTtl uint32 `protobuf:"varint,3,opt,name=response_ttl,json=responseTtl,proto3" json:"response_ttl,omitempty"` Non_IPQuery string `protobuf:"bytes,5,opt,name=non_IP_query,json=nonIPQuery,proto3" json:"non_IP_query,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_dns_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_dns_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_dns_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetServer() *net.Endpoint { if x != nil { return x.Server } return nil } func (x *Config) GetUserLevel() uint32 { if x != nil { return x.UserLevel } return 0 } func (x *Config) GetOverrideResponseTtl() bool { if x != nil { return x.OverrideResponseTtl } return false } func (x *Config) GetResponseTtl() uint32 { if x != nil { return x.ResponseTtl } return 0 } func (x *Config) GetNon_IPQuery() string { if x != nil { return x.Non_IPQuery } return "" } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` OverrideResponseTtl bool `protobuf:"varint,4,opt,name=override_response_ttl,json=overrideResponseTtl,proto3" json:"override_response_ttl,omitempty"` ResponseTtl uint32 `protobuf:"varint,3,opt,name=response_ttl,json=responseTtl,proto3" json:"response_ttl,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_proxy_dns_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_dns_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_proxy_dns_config_proto_rawDescGZIP(), []int{1} } func (x *SimplifiedConfig) GetOverrideResponseTtl() bool { if x != nil { return x.OverrideResponseTtl } return false } func (x *SimplifiedConfig) GetResponseTtl() uint32 { if x != nil { return x.ResponseTtl } return 0 } var File_proxy_dns_config_proto protoreflect.FileDescriptor const file_proxy_dns_config_proto_rawDesc = "" + "\n" + "\x16proxy/dns/config.proto\x12\x14v2ray.core.proxy.dns\x1a\x1ccommon/net/destination.proto\x1a common/protoext/extensions.proto\"\xd9\x01\n" + "\x06Config\x127\n" + "\x06server\x18\x01 \x01(\v2\x1f.v2ray.core.common.net.EndpointR\x06server\x12\x1d\n" + "\n" + "user_level\x18\x02 \x01(\rR\tuserLevel\x122\n" + "\x15override_response_ttl\x18\x04 \x01(\bR\x13overrideResponseTtl\x12!\n" + "\fresponse_ttl\x18\x03 \x01(\rR\vresponseTtl\x12 \n" + "\fnon_IP_query\x18\x05 \x01(\tR\n" + "nonIPQuery\"~\n" + "\x10SimplifiedConfig\x122\n" + "\x15override_response_ttl\x18\x04 \x01(\bR\x13overrideResponseTtl\x12!\n" + "\fresponse_ttl\x18\x03 \x01(\rR\vresponseTtl:\x13\x82\xb5\x18\x0f\n" + "\boutbound\x12\x03dnsB]\n" + "\x18com.v2ray.core.proxy.dnsP\x01Z(github.com/v2fly/v2ray-core/v5/proxy/dns\xaa\x02\x14V2Ray.Core.Proxy.Dnsb\x06proto3" var ( file_proxy_dns_config_proto_rawDescOnce sync.Once file_proxy_dns_config_proto_rawDescData []byte ) func file_proxy_dns_config_proto_rawDescGZIP() []byte { file_proxy_dns_config_proto_rawDescOnce.Do(func() { file_proxy_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_dns_config_proto_rawDesc), len(file_proxy_dns_config_proto_rawDesc))) }) return file_proxy_dns_config_proto_rawDescData } var file_proxy_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_dns_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.proxy.dns.Config (*SimplifiedConfig)(nil), // 1: v2ray.core.proxy.dns.SimplifiedConfig (*net.Endpoint)(nil), // 2: v2ray.core.common.net.Endpoint } var file_proxy_dns_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.dns.Config.server:type_name -> v2ray.core.common.net.Endpoint 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_proxy_dns_config_proto_init() } func file_proxy_dns_config_proto_init() { if File_proxy_dns_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_dns_config_proto_rawDesc), len(file_proxy_dns_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_dns_config_proto_goTypes, DependencyIndexes: file_proxy_dns_config_proto_depIdxs, MessageInfos: file_proxy_dns_config_proto_msgTypes, }.Build() File_proxy_dns_config_proto = out.File file_proxy_dns_config_proto_goTypes = nil file_proxy_dns_config_proto_depIdxs = nil } ================================================ FILE: proxy/dns/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.dns; option csharp_namespace = "V2Ray.Core.Proxy.Dns"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/dns"; option java_package = "com.v2ray.core.proxy.dns"; option java_multiple_files = true; import "common/net/destination.proto"; import "common/protoext/extensions.proto"; message Config { // Server is the DNS server address. If specified, this address overrides the // original one. v2ray.core.common.net.Endpoint server = 1; uint32 user_level = 2; bool override_response_ttl = 4; uint32 response_ttl = 3; string non_IP_query = 5; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "dns"; bool override_response_ttl = 4; uint32 response_ttl = 3; } ================================================ FILE: proxy/dns/dns.go ================================================ package dns import ( "context" "io" "sync" "time" "golang.org/x/net/dns/dnsmessage" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" dns_proto "github.com/v2fly/v2ray-core/v5/common/protocol/dns" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/strmatcher" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { h := new(Handler) if err := core.RequireFeatures(ctx, func(dnsClient dns.Client, policyManager policy.Manager) error { return h.Init(config.(*Config), dnsClient, policyManager) }); err != nil { return nil, err } return h, nil })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*SimplifiedConfig) _ = simplifiedServer fullConfig := &Config{} fullConfig.OverrideResponseTtl = simplifiedServer.OverrideResponseTtl fullConfig.ResponseTtl = simplifiedServer.ResponseTtl return common.CreateObject(ctx, fullConfig) })) } type ownLinkVerifier interface { IsOwnLink(ctx context.Context) bool } type Handler struct { client dns.Client ipv4Lookup dns.IPv4Lookup ipv6Lookup dns.IPv6Lookup ownLinkVerifier ownLinkVerifier server net.Destination timeout time.Duration config *Config nonIPQuery string } func (h *Handler) Init(config *Config, dnsClient dns.Client, policyManager policy.Manager) error { // Enable FakeDNS for DNS outbound if clientWithFakeDNS, ok := dnsClient.(dns.ClientWithFakeDNS); ok { dnsClient = clientWithFakeDNS.AsFakeDNSClient() } h.client = dnsClient h.timeout = policyManager.ForLevel(config.UserLevel).Timeouts.ConnectionIdle if ipv4lookup, ok := dnsClient.(dns.IPv4Lookup); ok { h.ipv4Lookup = ipv4lookup } else { return newError("dns.Client doesn't implement IPv4Lookup") } if ipv6lookup, ok := dnsClient.(dns.IPv6Lookup); ok { h.ipv6Lookup = ipv6lookup } else { return newError("dns.Client doesn't implement IPv6Lookup") } if v, ok := dnsClient.(ownLinkVerifier); ok { h.ownLinkVerifier = v } if config.Server != nil { h.server = config.Server.AsDestination() } h.config = config h.nonIPQuery = config.Non_IPQuery return nil } func (h *Handler) isOwnLink(ctx context.Context) bool { return h.ownLinkVerifier != nil && h.ownLinkVerifier.IsOwnLink(ctx) } func parseIPQuery(b []byte) (r bool, domain string, id uint16, qType dnsmessage.Type) { var parser dnsmessage.Parser header, err := parser.Start(b) if err != nil { newError("parser start").Base(err).WriteToLog() return } id = header.ID q, err := parser.Question() if err != nil { newError("question").Base(err).WriteToLog() return } qType = q.Type if qType != dnsmessage.TypeA && qType != dnsmessage.TypeAAAA { return } domain = q.Name.String() r = true return } // Process implements proxy.Outbound. func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("invalid outbound") } srcNetwork := outbound.Target.Network dest := outbound.Target if h.server.Network != net.Network_Unknown { dest.Network = h.server.Network } if h.server.Address != nil { dest.Address = h.server.Address } if h.server.Port != 0 { dest.Port = h.server.Port } newError("handling DNS traffic to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn := &outboundConn{ dialer: func() (internet.Connection, error) { return d.Dial(ctx, dest) }, connReady: make(chan struct{}, 1), } var reader dns_proto.MessageReader var writer dns_proto.MessageWriter if srcNetwork == net.Network_TCP { reader = dns_proto.NewTCPReader(link.Reader) writer = &dns_proto.TCPWriter{ Writer: link.Writer, } } else { reader = &dns_proto.UDPReader{ Reader: link.Reader, } writer = &dns_proto.UDPWriter{ Writer: link.Writer, } } var connReader dns_proto.MessageReader var connWriter dns_proto.MessageWriter if dest.Network == net.Network_TCP { connReader = dns_proto.NewTCPReader(buf.NewReader(conn)) connWriter = &dns_proto.TCPWriter{ Writer: buf.NewWriter(conn), } } else { connReader = &dns_proto.UDPReader{ Reader: buf.NewPacketReader(conn), } connWriter = &dns_proto.UDPWriter{ Writer: buf.NewWriter(conn), } } ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, h.timeout) request := func() error { defer conn.Close() for { b, err := reader.ReadMessage() if err == io.EOF { return nil } if err != nil { return err } timer.Update() if !h.isOwnLink(ctx) { isIPQuery, domain, id, qType := parseIPQuery(b.Bytes()) if isIPQuery || h.nonIPQuery != "drop" { if domain, err := strmatcher.ToDomain(domain); err == nil { go h.handleIPQuery(id, qType, domain, writer) } else { h.handleDNSError(id, dnsmessage.RCodeFormatError, writer) } } else { h.handleDNSError(id, dnsmessage.RCodeNotImplemented, writer) } } else if err := connWriter.WriteMessage(b); err != nil { return err } } } response := func() error { for { b, err := connReader.ReadMessage() if err == io.EOF { return nil } if err != nil { return err } timer.Update() if err := writer.WriteMessage(b); err != nil { return err } } } if err := task.Run(ctx, request, response); err != nil { return newError("connection ends").Base(err) } return nil } func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) { var ips []net.IP var err error var ttl uint32 = 600 if h.config.OverrideResponseTtl { ttl = h.config.ResponseTtl } switch qType { case dnsmessage.TypeA: ips, err = h.ipv4Lookup.LookupIPv4(domain) case dnsmessage.TypeAAAA: ips, err = h.ipv6Lookup.LookupIPv6(domain) } rcode := dns.RCodeFromError(err) if rcode == 0 && len(ips) == 0 && err != dns.ErrEmptyResponse { newError("ip query").Base(err).WriteToLog() return } b := buf.New() rawBytes := b.Extend(buf.Size) builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{ ID: id, RCode: dnsmessage.RCode(rcode), RecursionAvailable: true, RecursionDesired: true, Response: true, }) builder.EnableCompression() common.Must(builder.StartQuestions()) common.Must(builder.Question(dnsmessage.Question{ Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, Type: qType, })) common.Must(builder.StartAnswers()) rHeader := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl} for _, ip := range ips { if len(ip) == net.IPv4len { var r dnsmessage.AResource copy(r.A[:], ip) common.Must(builder.AResource(rHeader, r)) } else { var r dnsmessage.AAAAResource copy(r.AAAA[:], ip) common.Must(builder.AAAAResource(rHeader, r)) } } msgBytes, err := builder.Finish() if err != nil { newError("pack message").Base(err).WriteToLog() b.Release() return } b.Resize(0, int32(len(msgBytes))) if err := writer.WriteMessage(b); err != nil { newError("write IP answer").Base(err).WriteToLog() } } func (h *Handler) handleDNSError(id uint16, rCode dnsmessage.RCode, writer dns_proto.MessageWriter) { var err error b := buf.New() rawBytes := b.Extend(buf.Size) builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{ ID: id, RCode: rCode, RecursionAvailable: true, RecursionDesired: true, Response: true, }) builder.EnableCompression() common.Must(builder.StartQuestions()) common.Must(builder.StartAnswers()) msgBytes, err := builder.Finish() if err != nil { newError("pack message").Base(err).WriteToLog() b.Release() return } b.Resize(0, int32(len(msgBytes))) if err := writer.WriteMessage(b); err != nil { newError("write IP answer").Base(err).WriteToLog() } } type outboundConn struct { access sync.Mutex dialer func() (internet.Connection, error) conn net.Conn connReady chan struct{} } func (c *outboundConn) dial() error { conn, err := c.dialer() if err != nil { return err } c.conn = conn c.connReady <- struct{}{} return nil } func (c *outboundConn) Write(b []byte) (int, error) { c.access.Lock() if c.conn == nil { if err := c.dial(); err != nil { c.access.Unlock() newError("failed to dial outbound connection").Base(err).AtWarning().WriteToLog() return len(b), nil } } c.access.Unlock() return c.conn.Write(b) } func (c *outboundConn) Read(b []byte) (int, error) { var conn net.Conn c.access.Lock() conn = c.conn c.access.Unlock() if conn == nil { _, open := <-c.connReady if !open { return 0, io.EOF } conn = c.conn } return conn.Read(b) } func (c *outboundConn) Close() error { c.access.Lock() close(c.connReady) if c.conn != nil { c.conn.Close() } c.access.Unlock() return nil } ================================================ FILE: proxy/dns/dns_test.go ================================================ package dns_test import ( "strconv" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/miekg/dns" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" dnsapp "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/app/policy" "github.com/v2fly/v2ray-core/v5/app/proxyman" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/inbound" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" dns_proxy "github.com/v2fly/v2ray-core/v5/proxy/dns" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" ) type staticHandler struct{} func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { ans := new(dns.Msg) ans.Id = r.Id var clientIP net.IP opt := r.IsEdns0() if opt != nil { for _, o := range opt.Option { if o.Option() == dns.EDNS0SUBNET { subnet := o.(*dns.EDNS0_SUBNET) clientIP = subnet.Address } } } for _, q := range r.Question { switch { case q.Name == "google.com." && q.Qtype == dns.TypeA: if clientIP == nil { rr, _ := dns.NewRR("google.com. IN A 8.8.8.8") ans.Answer = append(ans.Answer, rr) } else { rr, _ := dns.NewRR("google.com. IN A 8.8.4.4") ans.Answer = append(ans.Answer, rr) } case q.Name == "facebook.com." && q.Qtype == dns.TypeA: rr, _ := dns.NewRR("facebook.com. IN A 9.9.9.9") ans.Answer = append(ans.Answer, rr) case q.Name == "ipv6.google.com." && q.Qtype == dns.TypeA: rr, err := dns.NewRR("ipv6.google.com. IN A 8.8.8.7") common.Must(err) ans.Answer = append(ans.Answer, rr) case q.Name == "ipv6.google.com." && q.Qtype == dns.TypeAAAA: rr, err := dns.NewRR("ipv6.google.com. IN AAAA 2001:4860:4860::8888") common.Must(err) ans.Answer = append(ans.Answer, rr) case q.Name == "notexist.google.com." && q.Qtype == dns.TypeAAAA: ans.MsgHdr.Rcode = dns.RcodeNameError } } w.WriteMsg(ans) } func TestUDPDNSTunnel(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, UDPSize: 1200, } defer dnsServer.Shutdown() go dnsServer.ListenAndServe() time.Sleep(time.Second) serverPort := udp.PickPort() config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dnsapp.Config{ NameServers: []*net.Endpoint{ { Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Inbound: []*core.InboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(port), Networks: []net.Network{net.Network_UDP}, }), ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{}), }, }, } v, err := core.New(config) common.Must(err) common.Must(v.Start()) defer v.Close() { m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{Name: "google.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET} c := new(dns.Client) in, _, err := c.Exchange(m1, "127.0.0.1:"+strconv.Itoa(int(serverPort))) common.Must(err) if len(in.Answer) != 1 { t.Fatal("len(answer): ", len(in.Answer)) } rr, ok := in.Answer[0].(*dns.A) if !ok { t.Fatal("not A record") } if r := cmp.Diff(rr.A[:], net.IP{8, 8, 8, 8}); r != "" { t.Error(r) } } { m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{Name: "ipv4only.google.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET} c := new(dns.Client) c.Timeout = 10 * time.Second in, _, err := c.Exchange(m1, "127.0.0.1:"+strconv.Itoa(int(serverPort))) common.Must(err) if len(in.Answer) != 0 { t.Fatal("len(answer): ", len(in.Answer)) } } { m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{Name: "notexist.google.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET} c := new(dns.Client) in, _, err := c.Exchange(m1, "127.0.0.1:"+strconv.Itoa(int(serverPort))) common.Must(err) if in.Rcode != dns.RcodeNameError { t.Error("expected NameError, but got ", in.Rcode) } } } func TestTCPDNSTunnel(t *testing.T) { port := udp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "udp", Handler: &staticHandler{}, } defer dnsServer.Shutdown() go dnsServer.ListenAndServe() time.Sleep(time.Second) serverPort := tcp.PickPort() config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dnsapp.Config{ NameServer: []*dnsapp.NameServer{ { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Inbound: []*core.InboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(port), Networks: []net.Network{net.Network_TCP}, }), ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{}), }, }, } v, err := core.New(config) common.Must(err) common.Must(v.Start()) defer v.Close() m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{Name: "google.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET} c := &dns.Client{ Net: "tcp", } in, _, err := c.Exchange(m1, "127.0.0.1:"+serverPort.String()) common.Must(err) if len(in.Answer) != 1 { t.Fatal("len(answer): ", len(in.Answer)) } rr, ok := in.Answer[0].(*dns.A) if !ok { t.Fatal("not A record") } if r := cmp.Diff(rr.A[:], net.IP{8, 8, 8, 8}); r != "" { t.Error(r) } } func TestUDP2TCPDNSTunnel(t *testing.T) { port := tcp.PickPort() dnsServer := dns.Server{ Addr: "127.0.0.1:" + port.String(), Net: "tcp", Handler: &staticHandler{}, } defer dnsServer.Shutdown() go dnsServer.ListenAndServe() time.Sleep(time.Second) serverPort := tcp.PickPort() config := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dnsapp.Config{ NameServer: []*dnsapp.NameServer{ { Address: &net.Endpoint{ Network: net.Network_UDP, Address: &net.IPOrDomain{ Address: &net.IPOrDomain_Ip{ Ip: []byte{127, 0, 0, 1}, }, }, Port: uint32(port), }, }, }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&policy.Config{}), }, Inbound: []*core.InboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(port), Networks: []net.Network{net.Network_TCP}, }), ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{ Server: &net.Endpoint{ Network: net.Network_TCP, }, }), }, }, } v, err := core.New(config) common.Must(err) common.Must(v.Start()) defer v.Close() m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{Name: "google.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET} c := &dns.Client{ Net: "tcp", } in, _, err := c.Exchange(m1, "127.0.0.1:"+serverPort.String()) common.Must(err) if len(in.Answer) != 1 { t.Fatal("len(answer): ", len(in.Answer)) } rr, ok := in.Answer[0].(*dns.A) if !ok { t.Fatal("not A record") } if r := cmp.Diff(rr.A[:], net.IP{8, 8, 8, 8}); r != "" { t.Error(r) } } ================================================ FILE: proxy/dns/errors.generated.go ================================================ package dns import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/dokodemo/config.go ================================================ package dokodemo import ( "github.com/v2fly/v2ray-core/v5/common/net" ) // GetPredefinedAddress returns the defined address from proto config. Null if address is not valid. func (v *Config) GetPredefinedAddress() net.Address { addr := v.Address.AsAddress() if addr == nil { return nil } return addr } ================================================ FILE: proxy/dokodemo/config.pb.go ================================================ package dokodemo import ( net "github.com/v2fly/v2ray-core/v5/common/net" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` // List of networks that the Dokodemo accepts. // Deprecated. Use networks. // // Deprecated: Marked as deprecated in proxy/dokodemo/config.proto. NetworkList *net.NetworkList `protobuf:"bytes,3,opt,name=network_list,json=networkList,proto3" json:"network_list,omitempty"` // List of networks that the Dokodemo accepts. Networks []net.Network `protobuf:"varint,7,rep,packed,name=networks,proto3,enum=v2ray.core.common.net.Network" json:"networks,omitempty"` // Deprecated: Marked as deprecated in proxy/dokodemo/config.proto. Timeout uint32 `protobuf:"varint,4,opt,name=timeout,proto3" json:"timeout,omitempty"` FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"` UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_dokodemo_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_dokodemo_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_dokodemo_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *Config) GetPort() uint32 { if x != nil { return x.Port } return 0 } // Deprecated: Marked as deprecated in proxy/dokodemo/config.proto. func (x *Config) GetNetworkList() *net.NetworkList { if x != nil { return x.NetworkList } return nil } func (x *Config) GetNetworks() []net.Network { if x != nil { return x.Networks } return nil } // Deprecated: Marked as deprecated in proxy/dokodemo/config.proto. func (x *Config) GetTimeout() uint32 { if x != nil { return x.Timeout } return 0 } func (x *Config) GetFollowRedirect() bool { if x != nil { return x.FollowRedirect } return false } func (x *Config) GetUserLevel() uint32 { if x != nil { return x.UserLevel } return 0 } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Networks *net.NetworkList `protobuf:"bytes,3,opt,name=networks,proto3" json:"networks,omitempty"` FollowRedirect bool `protobuf:"varint,4,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_proxy_dokodemo_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_dokodemo_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_proxy_dokodemo_config_proto_rawDescGZIP(), []int{1} } func (x *SimplifiedConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *SimplifiedConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *SimplifiedConfig) GetNetworks() *net.NetworkList { if x != nil { return x.Networks } return nil } func (x *SimplifiedConfig) GetFollowRedirect() bool { if x != nil { return x.FollowRedirect } return false } var File_proxy_dokodemo_config_proto protoreflect.FileDescriptor const file_proxy_dokodemo_config_proto_rawDesc = "" + "\n" + "\x1bproxy/dokodemo/config.proto\x12\x19v2ray.core.proxy.dokodemo\x1a\x18common/net/address.proto\x1a\x18common/net/network.proto\x1a common/protoext/extensions.proto\"\xc6\x02\n" + "\x06Config\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x12I\n" + "\fnetwork_list\x18\x03 \x01(\v2\".v2ray.core.common.net.NetworkListB\x02\x18\x01R\vnetworkList\x12:\n" + "\bnetworks\x18\a \x03(\x0e2\x1e.v2ray.core.common.net.NetworkR\bnetworks\x12\x1c\n" + "\atimeout\x18\x04 \x01(\rB\x02\x18\x01R\atimeout\x12'\n" + "\x0ffollow_redirect\x18\x05 \x01(\bR\x0efollowRedirect\x12\x1d\n" + "\n" + "user_level\x18\x06 \x01(\rR\tuserLevel\"\xea\x01\n" + "\x10SimplifiedConfig\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x12>\n" + "\bnetworks\x18\x03 \x01(\v2\".v2ray.core.common.net.NetworkListR\bnetworks\x12'\n" + "\x0ffollow_redirect\x18\x04 \x01(\bR\x0efollowRedirect:\x1c\x82\xb5\x18\x18\n" + "\ainbound\x12\rdokodemo-doorBl\n" + "\x1dcom.v2ray.core.proxy.dokodemoP\x01Z-github.com/v2fly/v2ray-core/v5/proxy/dokodemo\xaa\x02\x19V2Ray.Core.Proxy.Dokodemob\x06proto3" var ( file_proxy_dokodemo_config_proto_rawDescOnce sync.Once file_proxy_dokodemo_config_proto_rawDescData []byte ) func file_proxy_dokodemo_config_proto_rawDescGZIP() []byte { file_proxy_dokodemo_config_proto_rawDescOnce.Do(func() { file_proxy_dokodemo_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_dokodemo_config_proto_rawDesc), len(file_proxy_dokodemo_config_proto_rawDesc))) }) return file_proxy_dokodemo_config_proto_rawDescData } var file_proxy_dokodemo_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_dokodemo_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.proxy.dokodemo.Config (*SimplifiedConfig)(nil), // 1: v2ray.core.proxy.dokodemo.SimplifiedConfig (*net.IPOrDomain)(nil), // 2: v2ray.core.common.net.IPOrDomain (*net.NetworkList)(nil), // 3: v2ray.core.common.net.NetworkList (net.Network)(0), // 4: v2ray.core.common.net.Network } var file_proxy_dokodemo_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.dokodemo.Config.address:type_name -> v2ray.core.common.net.IPOrDomain 3, // 1: v2ray.core.proxy.dokodemo.Config.network_list:type_name -> v2ray.core.common.net.NetworkList 4, // 2: v2ray.core.proxy.dokodemo.Config.networks:type_name -> v2ray.core.common.net.Network 2, // 3: v2ray.core.proxy.dokodemo.SimplifiedConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 3, // 4: v2ray.core.proxy.dokodemo.SimplifiedConfig.networks:type_name -> v2ray.core.common.net.NetworkList 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_proxy_dokodemo_config_proto_init() } func file_proxy_dokodemo_config_proto_init() { if File_proxy_dokodemo_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_dokodemo_config_proto_rawDesc), len(file_proxy_dokodemo_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_dokodemo_config_proto_goTypes, DependencyIndexes: file_proxy_dokodemo_config_proto_depIdxs, MessageInfos: file_proxy_dokodemo_config_proto_msgTypes, }.Build() File_proxy_dokodemo_config_proto = out.File file_proxy_dokodemo_config_proto_goTypes = nil file_proxy_dokodemo_config_proto_depIdxs = nil } ================================================ FILE: proxy/dokodemo/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.dokodemo; option csharp_namespace = "V2Ray.Core.Proxy.Dokodemo"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/dokodemo"; option java_package = "com.v2ray.core.proxy.dokodemo"; option java_multiple_files = true; import "common/net/address.proto"; import "common/net/network.proto"; import "common/protoext/extensions.proto"; message Config { v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; // List of networks that the Dokodemo accepts. // Deprecated. Use networks. v2ray.core.common.net.NetworkList network_list = 3 [deprecated = true]; // List of networks that the Dokodemo accepts. repeated v2ray.core.common.net.Network networks = 7; uint32 timeout = 4 [deprecated = true]; bool follow_redirect = 5; uint32 user_level = 6; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "dokodemo-door"; v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; v2ray.core.common.net.NetworkList networks = 3; bool follow_redirect = 4; } ================================================ FILE: proxy/dokodemo/dokodemo.go ================================================ package dokodemo //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "sync/atomic" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { d := new(Door) err := core.RequireFeatures(ctx, func(pm policy.Manager) error { return d.Init(config.(*Config), pm, session.SockoptFromContext(ctx)) }) return d, err })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*SimplifiedConfig) fullConfig := &Config{ Address: simplifiedServer.Address, Port: simplifiedServer.Port, Networks: simplifiedServer.Networks.Network, FollowRedirect: simplifiedServer.FollowRedirect, } return common.CreateObject(ctx, fullConfig) })) } type Door struct { policyManager policy.Manager config *Config address net.Address port net.Port sockopt *session.Sockopt } // Init initializes the Door instance with necessary parameters. func (d *Door) Init(config *Config, pm policy.Manager, sockopt *session.Sockopt) error { if (config.NetworkList == nil || len(config.NetworkList.Network) == 0) && len(config.Networks) == 0 { return newError("no network specified") } d.config = config d.address = config.GetPredefinedAddress() d.port = net.Port(config.Port) d.policyManager = pm d.sockopt = sockopt return nil } // Network implements proxy.Inbound. func (d *Door) Network() []net.Network { if len(d.config.Networks) > 0 { return d.config.Networks } return d.config.NetworkList.GetNetwork() } func (d *Door) policy() policy.Session { config := d.config p := d.policyManager.ForLevel(config.UserLevel) if config.Timeout > 0 && config.UserLevel == 0 { p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second } return p } type hasHandshakeAddress interface { HandshakeAddress() net.Address } // Process implements proxy.Inbound. func (d *Door) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error { newError("processing connection from: ", conn.RemoteAddr()).AtDebug().WriteToLog(session.ExportIDToError(ctx)) dest := net.Destination{ Network: network, Address: d.address, Port: d.port, } destinationOverridden := false if d.config.FollowRedirect { if outbound := session.OutboundFromContext(ctx); outbound != nil && outbound.Target.IsValid() { dest = outbound.Target destinationOverridden = true } else if handshake, ok := conn.(hasHandshakeAddress); ok { addr := handshake.HandshakeAddress() if addr != nil { dest.Address = addr destinationOverridden = true } } } if !dest.IsValid() || dest.Address == nil { return newError("unable to get destination") } if inbound := session.InboundFromContext(ctx); inbound != nil { inbound.User = &protocol.MemoryUser{ Level: d.config.UserLevel, } } ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: conn.RemoteAddr(), To: dest, Status: log.AccessAccepted, Reason: "", }) newError("received request for ", conn.RemoteAddr()).WriteToLog(session.ExportIDToError(ctx)) plcy := d.policy() ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, plcy.Buffer) link, err := dispatcher.Dispatch(ctx, dest) if err != nil { return newError("failed to dispatch request").Base(err) } requestCount := int32(1) requestDone := func() error { defer func() { if atomic.AddInt32(&requestCount, -1) == 0 { timer.SetTimeout(plcy.Timeouts.DownlinkOnly) } }() var reader buf.Reader if dest.Network == net.Network_UDP { reader = buf.NewPacketReader(conn) } else { reader = buf.NewReader(conn) } if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport request").Base(err) } return nil } tproxyRequest := func() error { return nil } var writer buf.Writer if network == net.Network_TCP { writer = buf.NewWriter(conn) } else { // if we are in TPROXY mode, use linux's udp forging functionality if !destinationOverridden { writer = &buf.SequentialWriter{Writer: conn} } else { sockopt := &internet.SocketConfig{ Tproxy: internet.SocketConfig_TProxy, } if dest.Address.Family().IsIP() { sockopt.BindAddress = dest.Address.IP() sockopt.BindPort = uint32(dest.Port) } if d.sockopt != nil { sockopt.Mark = d.sockopt.Mark } tConn, err := internet.DialSystem(ctx, net.DestinationFromAddr(conn.RemoteAddr()), sockopt) if err != nil { return err } defer tConn.Close() writer = &buf.SequentialWriter{Writer: tConn} tReader := buf.NewPacketReader(tConn) requestCount++ tproxyRequest = func() error { defer func() { if atomic.AddInt32(&requestCount, -1) == 0 { timer.SetTimeout(plcy.Timeouts.DownlinkOnly) } }() if err := buf.Copy(tReader, link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport request (TPROXY conn)").Base(err) } return nil } } } responseDone := func() error { defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport response").Base(err) } return nil } if err := task.Run(ctx, task.OnSuccess(func() error { return task.Run(ctx, requestDone, tproxyRequest) }, task.Close(link.Writer)), responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) return newError("connection ends").Base(err) } return nil } ================================================ FILE: proxy/dokodemo/errors.generated.go ================================================ package dokodemo import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/freedom/config.go ================================================ package freedom func (c *Config) useIP() bool { return c.DomainStrategy == Config_USE_IP || c.DomainStrategy == Config_USE_IP4 || c.DomainStrategy == Config_USE_IP6 } ================================================ FILE: proxy/freedom/config.pb.go ================================================ package freedom import ( protocol "github.com/v2fly/v2ray-core/v5/common/protocol" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ProtocolReplacement int32 const ( ProtocolReplacement_IDENTITY ProtocolReplacement = 0 ProtocolReplacement_FORCE_TCP ProtocolReplacement = 1 ProtocolReplacement_FORCE_UDP ProtocolReplacement = 2 ) // Enum value maps for ProtocolReplacement. var ( ProtocolReplacement_name = map[int32]string{ 0: "IDENTITY", 1: "FORCE_TCP", 2: "FORCE_UDP", } ProtocolReplacement_value = map[string]int32{ "IDENTITY": 0, "FORCE_TCP": 1, "FORCE_UDP": 2, } ) func (x ProtocolReplacement) Enum() *ProtocolReplacement { p := new(ProtocolReplacement) *p = x return p } func (x ProtocolReplacement) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ProtocolReplacement) Descriptor() protoreflect.EnumDescriptor { return file_proxy_freedom_config_proto_enumTypes[0].Descriptor() } func (ProtocolReplacement) Type() protoreflect.EnumType { return &file_proxy_freedom_config_proto_enumTypes[0] } func (x ProtocolReplacement) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ProtocolReplacement.Descriptor instead. func (ProtocolReplacement) EnumDescriptor() ([]byte, []int) { return file_proxy_freedom_config_proto_rawDescGZIP(), []int{0} } type Config_DomainStrategy int32 const ( Config_AS_IS Config_DomainStrategy = 0 Config_USE_IP Config_DomainStrategy = 1 Config_USE_IP4 Config_DomainStrategy = 2 Config_USE_IP6 Config_DomainStrategy = 3 ) // Enum value maps for Config_DomainStrategy. var ( Config_DomainStrategy_name = map[int32]string{ 0: "AS_IS", 1: "USE_IP", 2: "USE_IP4", 3: "USE_IP6", } Config_DomainStrategy_value = map[string]int32{ "AS_IS": 0, "USE_IP": 1, "USE_IP4": 2, "USE_IP6": 3, } ) func (x Config_DomainStrategy) Enum() *Config_DomainStrategy { p := new(Config_DomainStrategy) *p = x return p } func (x Config_DomainStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Config_DomainStrategy) Descriptor() protoreflect.EnumDescriptor { return file_proxy_freedom_config_proto_enumTypes[1].Descriptor() } func (Config_DomainStrategy) Type() protoreflect.EnumType { return &file_proxy_freedom_config_proto_enumTypes[1] } func (x Config_DomainStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Config_DomainStrategy.Descriptor instead. func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) { return file_proxy_freedom_config_proto_rawDescGZIP(), []int{1, 0} } type DestinationOverride struct { state protoimpl.MessageState `protogen:"open.v1"` Server *protocol.ServerEndpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DestinationOverride) Reset() { *x = DestinationOverride{} mi := &file_proxy_freedom_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DestinationOverride) String() string { return protoimpl.X.MessageStringOf(x) } func (*DestinationOverride) ProtoMessage() {} func (x *DestinationOverride) ProtoReflect() protoreflect.Message { mi := &file_proxy_freedom_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DestinationOverride.ProtoReflect.Descriptor instead. func (*DestinationOverride) Descriptor() ([]byte, []int) { return file_proxy_freedom_config_proto_rawDescGZIP(), []int{0} } func (x *DestinationOverride) GetServer() *protocol.ServerEndpoint { if x != nil { return x.Server } return nil } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` DomainStrategy Config_DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=v2ray.core.proxy.freedom.Config_DomainStrategy" json:"domain_strategy,omitempty"` // Deprecated: Marked as deprecated in proxy/freedom/config.proto. Timeout uint32 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` ProtocolReplacement ProtocolReplacement `protobuf:"varint,5,opt,name=protocol_replacement,json=protocolReplacement,proto3,enum=v2ray.core.proxy.freedom.ProtocolReplacement" json:"protocol_replacement,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_freedom_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_freedom_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_freedom_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetDomainStrategy() Config_DomainStrategy { if x != nil { return x.DomainStrategy } return Config_AS_IS } // Deprecated: Marked as deprecated in proxy/freedom/config.proto. func (x *Config) GetTimeout() uint32 { if x != nil { return x.Timeout } return 0 } func (x *Config) GetDestinationOverride() *DestinationOverride { if x != nil { return x.DestinationOverride } return nil } func (x *Config) GetUserLevel() uint32 { if x != nil { return x.UserLevel } return 0 } func (x *Config) GetProtocolReplacement() ProtocolReplacement { if x != nil { return x.ProtocolReplacement } return ProtocolReplacement_IDENTITY } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` ProtocolReplacement ProtocolReplacement `protobuf:"varint,5,opt,name=protocol_replacement,json=protocolReplacement,proto3,enum=v2ray.core.proxy.freedom.ProtocolReplacement" json:"protocol_replacement,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_proxy_freedom_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_freedom_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_proxy_freedom_config_proto_rawDescGZIP(), []int{2} } func (x *SimplifiedConfig) GetDestinationOverride() *DestinationOverride { if x != nil { return x.DestinationOverride } return nil } func (x *SimplifiedConfig) GetProtocolReplacement() ProtocolReplacement { if x != nil { return x.ProtocolReplacement } return ProtocolReplacement_IDENTITY } var File_proxy_freedom_config_proto protoreflect.FileDescriptor const file_proxy_freedom_config_proto_rawDesc = "" + "\n" + "\x1aproxy/freedom/config.proto\x12\x18v2ray.core.proxy.freedom\x1a!common/protocol/server_spec.proto\x1a common/protoext/extensions.proto\"Y\n" + "\x13DestinationOverride\x12B\n" + "\x06server\x18\x01 \x01(\v2*.v2ray.core.common.protocol.ServerEndpointR\x06server\"\xa6\x03\n" + "\x06Config\x12X\n" + "\x0fdomain_strategy\x18\x01 \x01(\x0e2/.v2ray.core.proxy.freedom.Config.DomainStrategyR\x0edomainStrategy\x12\x1c\n" + "\atimeout\x18\x02 \x01(\rB\x02\x18\x01R\atimeout\x12`\n" + "\x14destination_override\x18\x03 \x01(\v2-.v2ray.core.proxy.freedom.DestinationOverrideR\x13destinationOverride\x12\x1d\n" + "\n" + "user_level\x18\x04 \x01(\rR\tuserLevel\x12`\n" + "\x14protocol_replacement\x18\x05 \x01(\x0e2-.v2ray.core.proxy.freedom.ProtocolReplacementR\x13protocolReplacement\"A\n" + "\x0eDomainStrategy\x12\t\n" + "\x05AS_IS\x10\x00\x12\n" + "\n" + "\x06USE_IP\x10\x01\x12\v\n" + "\aUSE_IP4\x10\x02\x12\v\n" + "\aUSE_IP6\x10\x03\"\xef\x01\n" + "\x10SimplifiedConfig\x12`\n" + "\x14destination_override\x18\x03 \x01(\v2-.v2ray.core.proxy.freedom.DestinationOverrideR\x13destinationOverride\x12`\n" + "\x14protocol_replacement\x18\x05 \x01(\x0e2-.v2ray.core.proxy.freedom.ProtocolReplacementR\x13protocolReplacement:\x17\x82\xb5\x18\x13\n" + "\boutbound\x12\afreedom*A\n" + "\x13ProtocolReplacement\x12\f\n" + "\bIDENTITY\x10\x00\x12\r\n" + "\tFORCE_TCP\x10\x01\x12\r\n" + "\tFORCE_UDP\x10\x02Bi\n" + "\x1ccom.v2ray.core.proxy.freedomP\x01Z,github.com/v2fly/v2ray-core/v5/proxy/freedom\xaa\x02\x18V2Ray.Core.Proxy.Freedomb\x06proto3" var ( file_proxy_freedom_config_proto_rawDescOnce sync.Once file_proxy_freedom_config_proto_rawDescData []byte ) func file_proxy_freedom_config_proto_rawDescGZIP() []byte { file_proxy_freedom_config_proto_rawDescOnce.Do(func() { file_proxy_freedom_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_freedom_config_proto_rawDesc), len(file_proxy_freedom_config_proto_rawDesc))) }) return file_proxy_freedom_config_proto_rawDescData } var file_proxy_freedom_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_proxy_freedom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_proxy_freedom_config_proto_goTypes = []any{ (ProtocolReplacement)(0), // 0: v2ray.core.proxy.freedom.ProtocolReplacement (Config_DomainStrategy)(0), // 1: v2ray.core.proxy.freedom.Config.DomainStrategy (*DestinationOverride)(nil), // 2: v2ray.core.proxy.freedom.DestinationOverride (*Config)(nil), // 3: v2ray.core.proxy.freedom.Config (*SimplifiedConfig)(nil), // 4: v2ray.core.proxy.freedom.SimplifiedConfig (*protocol.ServerEndpoint)(nil), // 5: v2ray.core.common.protocol.ServerEndpoint } var file_proxy_freedom_config_proto_depIdxs = []int32{ 5, // 0: v2ray.core.proxy.freedom.DestinationOverride.server:type_name -> v2ray.core.common.protocol.ServerEndpoint 1, // 1: v2ray.core.proxy.freedom.Config.domain_strategy:type_name -> v2ray.core.proxy.freedom.Config.DomainStrategy 2, // 2: v2ray.core.proxy.freedom.Config.destination_override:type_name -> v2ray.core.proxy.freedom.DestinationOverride 0, // 3: v2ray.core.proxy.freedom.Config.protocol_replacement:type_name -> v2ray.core.proxy.freedom.ProtocolReplacement 2, // 4: v2ray.core.proxy.freedom.SimplifiedConfig.destination_override:type_name -> v2ray.core.proxy.freedom.DestinationOverride 0, // 5: v2ray.core.proxy.freedom.SimplifiedConfig.protocol_replacement:type_name -> v2ray.core.proxy.freedom.ProtocolReplacement 6, // [6:6] is the sub-list for method output_type 6, // [6:6] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name } func init() { file_proxy_freedom_config_proto_init() } func file_proxy_freedom_config_proto_init() { if File_proxy_freedom_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_freedom_config_proto_rawDesc), len(file_proxy_freedom_config_proto_rawDesc)), NumEnums: 2, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_freedom_config_proto_goTypes, DependencyIndexes: file_proxy_freedom_config_proto_depIdxs, EnumInfos: file_proxy_freedom_config_proto_enumTypes, MessageInfos: file_proxy_freedom_config_proto_msgTypes, }.Build() File_proxy_freedom_config_proto = out.File file_proxy_freedom_config_proto_goTypes = nil file_proxy_freedom_config_proto_depIdxs = nil } ================================================ FILE: proxy/freedom/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.freedom; option csharp_namespace = "V2Ray.Core.Proxy.Freedom"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/freedom"; option java_package = "com.v2ray.core.proxy.freedom"; option java_multiple_files = true; import "common/protocol/server_spec.proto"; import "common/protoext/extensions.proto"; message DestinationOverride { v2ray.core.common.protocol.ServerEndpoint server = 1; } enum ProtocolReplacement { IDENTITY = 0; FORCE_TCP = 1; FORCE_UDP = 2; } message Config { enum DomainStrategy { AS_IS = 0; USE_IP = 1; USE_IP4 = 2; USE_IP6 = 3; } DomainStrategy domain_strategy = 1; uint32 timeout = 2 [deprecated = true]; DestinationOverride destination_override = 3; uint32 user_level = 4; ProtocolReplacement protocol_replacement = 5; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "freedom"; DestinationOverride destination_override = 3; ProtocolReplacement protocol_replacement = 5; } ================================================ FILE: proxy/freedom/errors.generated.go ================================================ package freedom import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/freedom/freedom.go ================================================ package freedom //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { h := new(Handler) if err := core.RequireFeatures(ctx, func(pm policy.Manager, d dns.Client) error { return h.Init(config.(*Config), pm, d) }); err != nil { return nil, err } return h, nil })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*SimplifiedConfig) _ = simplifiedServer fullConfig := &Config{ DestinationOverride: simplifiedServer.DestinationOverride, ProtocolReplacement: simplifiedServer.ProtocolReplacement, } return common.CreateObject(ctx, fullConfig) })) } // Handler handles Freedom connections. type Handler struct { policyManager policy.Manager dns dns.Client config *Config } // Init initializes the Handler with necessary parameters. func (h *Handler) Init(config *Config, pm policy.Manager, d dns.Client) error { h.config = config h.policyManager = pm h.dns = d return nil } func (h *Handler) policy() policy.Session { p := h.policyManager.ForLevel(h.config.UserLevel) if h.config.Timeout > 0 && h.config.UserLevel == 0 { p.Timeouts.ConnectionIdle = time.Duration(h.config.Timeout) * time.Second } return p } func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { ips, err := dns.LookupIPWithOption(h.dns, domain, dns.IPOption{ IPv4Enable: h.config.DomainStrategy == Config_USE_IP || h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()), IPv6Enable: h.config.DomainStrategy == Config_USE_IP || h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()), FakeEnable: false, }) if err != nil { newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) } if len(ips) == 0 { return nil } return net.IPAddress(ips[dice.Roll(len(ips))]) } func isValidAddress(addr *net.IPOrDomain) bool { if addr == nil { return false } a := addr.AsAddress() return a != net.AnyIP } // Process implements proxy.Outbound. func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified.") } destination := outbound.Target if h.config.DestinationOverride != nil { server := h.config.DestinationOverride.Server if isValidAddress(server.Address) { destination.Address = server.Address.AsAddress() } if server.Port != 0 { destination.Port = net.Port(server.Port) } } if h.config.ProtocolReplacement != ProtocolReplacement_IDENTITY { if h.config.ProtocolReplacement == ProtocolReplacement_FORCE_TCP { destination.Network = net.Network_TCP } if h.config.ProtocolReplacement == ProtocolReplacement_FORCE_UDP { destination.Network = net.Network_UDP } } if h.config.useIP() { outbound.Resolver = func(ctx context.Context, domain string) net.Address { return h.resolveIP(ctx, domain, dialer.Address()) } } newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx)) input := link.Reader output := link.Writer var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { rawConn, err := dialer.Dial(ctx, destination) if err != nil { return err } conn = rawConn return nil }) if err != nil { return newError("failed to open connection to ", destination).Base(err) } defer conn.Close() plcy := h.policy() ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle) requestDone := func() error { defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly) var writer buf.Writer if destination.Network == net.Network_TCP { writer = buf.NewWriter(conn) } else { writer = &buf.SequentialWriter{Writer: conn} } if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to process request").Base(err) } return nil } responseDone := func() error { defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) var reader buf.Reader if destination.Network == net.Network_TCP && h.config.ProtocolReplacement == ProtocolReplacement_IDENTITY { reader = buf.NewReader(conn) } else { reader = buf.NewPacketReader(conn) } if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil { return newError("failed to process response").Base(err) } return nil } if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil { return newError("connection ends").Base(err) } return nil } ================================================ FILE: proxy/http/client.go ================================================ package http import ( "bufio" "bytes" "context" "encoding/base64" "io" "net/http" "net/url" "sync" "time" "golang.org/x/net/http2" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/bytespool" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/security" ) type Client struct { serverPicker protocol.ServerPicker policyManager policy.Manager h1SkipWaitForReply bool } type h2Conn struct { rawConn net.Conn h2Conn *http2.ClientConn } var ( cachedH2Mutex sync.Mutex cachedH2Conns map[net.Destination]h2Conn ) // NewClient create a new http client based on the given config. func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { serverList := protocol.NewServerList() for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { return nil, newError("failed to get server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { return nil, newError("0 target server") } v := core.MustFromContext(ctx) return &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), h1SkipWaitForReply: config.H1SkipWaitForReply, }, nil } // Process implements proxy.Outbound.Process. We first create a socket tunnel via HTTP CONNECT method, then redirect all inbound traffic to that tunnel. func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified.") } target := outbound.Target targetAddr := target.NetAddr() if target.Network == net.Network_UDP { return newError("UDP is not supported by HTTP outbound") } var user *protocol.MemoryUser var conn internet.Connection var firstPayload []byte if reader, ok := link.Reader.(buf.TimeoutReader); ok { // 0-RTT optimization for HTTP/2: If the payload comes very soon, it can be // transmitted together. Note we should not get stuck here, as the payload may // not exist (considering to access MySQL database via a HTTP proxy, where the // server sends hello to the client first). waitTime := proxy.FirstPayloadTimeout if c.h1SkipWaitForReply { // Some server require first write to be present in client hello. // Increase timeout to if the client have explicitly requested to skip waiting for reply. waitTime = time.Second } if mbuf, _ := reader.ReadMultiBufferTimeout(waitTime); mbuf != nil { mlen := mbuf.Len() firstPayload = bytespool.Alloc(mlen) mbuf, _ = buf.SplitBytes(mbuf, firstPayload) firstPayload = firstPayload[:mlen] buf.ReleaseMulti(mbuf) defer bytespool.Free(firstPayload) } } if err := retry.ExponentialBackoff(5, 100).On(func() error { server := c.serverPicker.PickServer() dest := server.Destination() user = server.PickUser() netConn, firstResp, err := setUpHTTPTunnel(ctx, dest, targetAddr, user, dialer, firstPayload, c.h1SkipWaitForReply) if netConn != nil { if _, ok := netConn.(*http2Conn); !ok && !c.h1SkipWaitForReply { if _, err := netConn.Write(firstPayload); err != nil { netConn.Close() return err } } if firstResp != nil { if err := link.Writer.WriteMultiBuffer(firstResp); err != nil { return err } } conn = internet.Connection(netConn) } return err }); err != nil { return newError("failed to find an available destination").Base(err) } defer func() { if err := conn.Close(); err != nil { newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } }() p := c.policyManager.ForLevel(0) if user != nil { p = c.policyManager.ForLevel(user.Level) } ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle) requestFunc := func() error { defer timer.SetTimeout(p.Timeouts.DownlinkOnly) return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) } responseFunc := func() error { defer timer.SetTimeout(p.Timeouts.UplinkOnly) return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) } responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { return newError("connection ends").Base(err) } return nil } // setUpHTTPTunnel will create a socket tunnel via HTTP CONNECT method func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, user *protocol.MemoryUser, dialer internet.Dialer, firstPayload []byte, writeFirstPayloadInH1 bool, ) (net.Conn, buf.MultiBuffer, error) { req := &http.Request{ Method: http.MethodConnect, URL: &url.URL{Host: target}, Header: make(http.Header), Host: target, } if user != nil && user.Account != nil { account := user.Account.(*Account) auth := account.GetUsername() + ":" + account.GetPassword() req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth))) } connectHTTP1 := func(rawConn net.Conn) (net.Conn, buf.MultiBuffer, error) { req.Header.Set("Proxy-Connection", "Keep-Alive") if !writeFirstPayloadInH1 { err := req.Write(rawConn) if err != nil { rawConn.Close() return nil, nil, err } } else { buffer := bytes.NewBuffer(nil) err := req.Write(buffer) if err != nil { rawConn.Close() return nil, nil, err } _, err = io.Copy(buffer, bytes.NewReader(firstPayload)) if err != nil { rawConn.Close() return nil, nil, err } _, err = rawConn.Write(buffer.Bytes()) if err != nil { rawConn.Close() return nil, nil, err } } bufferedReader := bufio.NewReader(rawConn) resp, err := http.ReadResponse(bufferedReader, req) if err != nil { rawConn.Close() return nil, nil, err } if resp.StatusCode != http.StatusOK { rawConn.Close() return nil, nil, newError("Proxy responded with non 200 code: " + resp.Status) } if bufferedReader.Buffered() > 0 { payload, err := buf.ReadFrom(io.LimitReader(bufferedReader, int64(bufferedReader.Buffered()))) if err != nil { return nil, nil, newError("unable to drain buffer: ").Base(err) } return rawConn, payload, nil } return rawConn, nil, nil } connectHTTP2 := func(rawConn net.Conn, h2clientConn *http2.ClientConn) (net.Conn, error) { pr, pw := io.Pipe() req.Body = pr var pErr error var wg sync.WaitGroup wg.Add(1) go func() { _, pErr = pw.Write(firstPayload) wg.Done() }() resp, err := h2clientConn.RoundTrip(req) // nolint: bodyclose if err != nil { rawConn.Close() return nil, err } wg.Wait() if pErr != nil { rawConn.Close() return nil, pErr } if resp.StatusCode != http.StatusOK { rawConn.Close() return nil, newError("Proxy responded with non 200 code: " + resp.Status) } return newHTTP2Conn(rawConn, pw, resp.Body), nil } cachedH2Mutex.Lock() cachedConn, cachedConnFound := cachedH2Conns[dest] cachedH2Mutex.Unlock() if cachedConnFound { rc, cc := cachedConn.rawConn, cachedConn.h2Conn if cc.CanTakeNewRequest() { proxyConn, err := connectHTTP2(rc, cc) if err != nil { return nil, nil, err } return proxyConn, nil, nil } } rawConn, err := dialer.Dial(ctx, dest) if err != nil { return nil, nil, err } iConn := rawConn if statConn, ok := iConn.(*internet.StatCouterConnection); ok { iConn = statConn.Connection } nextProto := "" if connALPNGetter, ok := iConn.(security.ConnectionApplicationProtocol); ok { nextProto, err = connALPNGetter.GetConnectionApplicationProtocol() if err != nil { rawConn.Close() return nil, nil, err } } switch nextProto { case "", "http/1.1": return connectHTTP1(rawConn) case "h2": t := http2.Transport{} h2clientConn, err := t.NewClientConn(rawConn) if err != nil { rawConn.Close() return nil, nil, err } proxyConn, err := connectHTTP2(rawConn, h2clientConn) if err != nil { rawConn.Close() return nil, nil, err } cachedH2Mutex.Lock() if cachedH2Conns == nil { cachedH2Conns = make(map[net.Destination]h2Conn) } cachedH2Conns[dest] = h2Conn{ rawConn: rawConn, h2Conn: h2clientConn, } cachedH2Mutex.Unlock() return proxyConn, nil, err default: return nil, nil, newError("negotiated unsupported application layer protocol: " + nextProto) } } func newHTTP2Conn(c net.Conn, pipedReqBody *io.PipeWriter, respBody io.ReadCloser) net.Conn { return &http2Conn{Conn: c, in: pipedReqBody, out: respBody} } type http2Conn struct { net.Conn in *io.PipeWriter out io.ReadCloser } func (h *http2Conn) Read(p []byte) (n int, err error) { return h.out.Read(p) } func (h *http2Conn) Write(p []byte) (n int, err error) { return h.in.Write(p) } func (h *http2Conn) Close() error { h.in.Close() return h.out.Close() } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewClient(ctx, config.(*ClientConfig)) })) } ================================================ FILE: proxy/http/config.go ================================================ package http import ( "github.com/v2fly/v2ray-core/v5/common/protocol" ) func (a *Account) Equals(another protocol.Account) bool { if account, ok := another.(*Account); ok { return a.Username == account.Username } return false } func (a *Account) AsAccount() (protocol.Account, error) { return a, nil } func (sc *ServerConfig) HasAccount(username, password string) bool { if sc.Accounts == nil { return false } p, found := sc.Accounts[username] if !found { return false } return p == password } ================================================ FILE: proxy/http/config.pb.go ================================================ package http import ( protocol "github.com/v2fly/v2ray-core/v5/common/protocol" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Account struct { state protoimpl.MessageState `protogen:"open.v1"` Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Account) Reset() { *x = Account{} mi := &file_proxy_http_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Account) String() string { return protoimpl.X.MessageStringOf(x) } func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Account.ProtoReflect.Descriptor instead. func (*Account) Descriptor() ([]byte, []int) { return file_proxy_http_config_proto_rawDescGZIP(), []int{0} } func (x *Account) GetUsername() string { if x != nil { return x.Username } return "" } func (x *Account) GetPassword() string { if x != nil { return x.Password } return "" } // Config for HTTP proxy server. type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Deprecated: Marked as deprecated in proxy/http/config.proto. Timeout uint32 `protobuf:"varint,1,opt,name=timeout,proto3" json:"timeout,omitempty"` Accounts map[string]string `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` AllowTransparent bool `protobuf:"varint,3,opt,name=allow_transparent,json=allowTransparent,proto3" json:"allow_transparent,omitempty"` UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_http_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_http_config_proto_rawDescGZIP(), []int{1} } // Deprecated: Marked as deprecated in proxy/http/config.proto. func (x *ServerConfig) GetTimeout() uint32 { if x != nil { return x.Timeout } return 0 } func (x *ServerConfig) GetAccounts() map[string]string { if x != nil { return x.Accounts } return nil } func (x *ServerConfig) GetAllowTransparent() bool { if x != nil { return x.AllowTransparent } return false } func (x *ServerConfig) GetUserLevel() uint32 { if x != nil { return x.UserLevel } return 0 } // ClientConfig is the protobuf config for HTTP proxy client. type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Sever is a list of HTTP server addresses. Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` H1SkipWaitForReply bool `protobuf:"varint,2,opt,name=h1_skip_wait_for_reply,json=h1SkipWaitForReply,proto3" json:"h1_skip_wait_for_reply,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_http_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_http_config_proto_rawDescGZIP(), []int{2} } func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint { if x != nil { return x.Server } return nil } func (x *ClientConfig) GetH1SkipWaitForReply() bool { if x != nil { return x.H1SkipWaitForReply } return false } var File_proxy_http_config_proto protoreflect.FileDescriptor const file_proxy_http_config_proto_rawDesc = "" + "\n" + "\x17proxy/http/config.proto\x12\x15v2ray.core.proxy.http\x1a!common/protocol/server_spec.proto\"A\n" + "\aAccount\x12\x1a\n" + "\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" + "\bpassword\x18\x02 \x01(\tR\bpassword\"\x84\x02\n" + "\fServerConfig\x12\x1c\n" + "\atimeout\x18\x01 \x01(\rB\x02\x18\x01R\atimeout\x12M\n" + "\baccounts\x18\x02 \x03(\v21.v2ray.core.proxy.http.ServerConfig.AccountsEntryR\baccounts\x12+\n" + "\x11allow_transparent\x18\x03 \x01(\bR\x10allowTransparent\x12\x1d\n" + "\n" + "user_level\x18\x04 \x01(\rR\tuserLevel\x1a;\n" + "\rAccountsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x86\x01\n" + "\fClientConfig\x12B\n" + "\x06server\x18\x01 \x03(\v2*.v2ray.core.common.protocol.ServerEndpointR\x06server\x122\n" + "\x16h1_skip_wait_for_reply\x18\x02 \x01(\bR\x12h1SkipWaitForReplyB`\n" + "\x19com.v2ray.core.proxy.httpP\x01Z)github.com/v2fly/v2ray-core/v5/proxy/http\xaa\x02\x15V2Ray.Core.Proxy.Httpb\x06proto3" var ( file_proxy_http_config_proto_rawDescOnce sync.Once file_proxy_http_config_proto_rawDescData []byte ) func file_proxy_http_config_proto_rawDescGZIP() []byte { file_proxy_http_config_proto_rawDescOnce.Do(func() { file_proxy_http_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_http_config_proto_rawDesc), len(file_proxy_http_config_proto_rawDesc))) }) return file_proxy_http_config_proto_rawDescData } var file_proxy_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_proxy_http_config_proto_goTypes = []any{ (*Account)(nil), // 0: v2ray.core.proxy.http.Account (*ServerConfig)(nil), // 1: v2ray.core.proxy.http.ServerConfig (*ClientConfig)(nil), // 2: v2ray.core.proxy.http.ClientConfig nil, // 3: v2ray.core.proxy.http.ServerConfig.AccountsEntry (*protocol.ServerEndpoint)(nil), // 4: v2ray.core.common.protocol.ServerEndpoint } var file_proxy_http_config_proto_depIdxs = []int32{ 3, // 0: v2ray.core.proxy.http.ServerConfig.accounts:type_name -> v2ray.core.proxy.http.ServerConfig.AccountsEntry 4, // 1: v2ray.core.proxy.http.ClientConfig.server:type_name -> v2ray.core.common.protocol.ServerEndpoint 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_proxy_http_config_proto_init() } func file_proxy_http_config_proto_init() { if File_proxy_http_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_http_config_proto_rawDesc), len(file_proxy_http_config_proto_rawDesc)), NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_http_config_proto_goTypes, DependencyIndexes: file_proxy_http_config_proto_depIdxs, MessageInfos: file_proxy_http_config_proto_msgTypes, }.Build() File_proxy_http_config_proto = out.File file_proxy_http_config_proto_goTypes = nil file_proxy_http_config_proto_depIdxs = nil } ================================================ FILE: proxy/http/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.http; option csharp_namespace = "V2Ray.Core.Proxy.Http"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/http"; option java_package = "com.v2ray.core.proxy.http"; option java_multiple_files = true; import "common/protocol/server_spec.proto"; message Account { string username = 1; string password = 2; } // Config for HTTP proxy server. message ServerConfig { uint32 timeout = 1 [deprecated = true]; map accounts = 2; bool allow_transparent = 3; uint32 user_level = 4; } // ClientConfig is the protobuf config for HTTP proxy client. message ClientConfig { // Sever is a list of HTTP server addresses. repeated v2ray.core.common.protocol.ServerEndpoint server = 1; bool h1_skip_wait_for_reply = 2; } ================================================ FILE: proxy/http/errors.generated.go ================================================ package http import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/http/http.go ================================================ package http //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: proxy/http/server.go ================================================ package http import ( "bufio" "bytes" "context" "encoding/base64" "io" "net/http" "strings" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" http_proto "github.com/v2fly/v2ray-core/v5/common/protocol/http" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet" ) // Server is an HTTP proxy server. type Server struct { config *ServerConfig policyManager policy.Manager } // NewServer creates a new HTTP inbound handler. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { v := core.MustFromContext(ctx) s := &Server{ config: config, policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return s, nil } func (s *Server) policy() policy.Session { config := s.config p := s.policyManager.ForLevel(config.UserLevel) if config.Timeout > 0 && config.UserLevel == 0 { p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second } return p } // Network implements proxy.Inbound. func (*Server) Network() []net.Network { return []net.Network{net.Network_TCP, net.Network_UNIX} } func isTimeout(err error) bool { nerr, ok := errors.Cause(err).(net.Error) return ok && nerr.Timeout() } func parseBasicAuth(auth string) (username, password string, ok bool) { const prefix = "Basic " if !strings.HasPrefix(auth, prefix) { return } c, err := base64.StdEncoding.DecodeString(auth[len(prefix):]) if err != nil { return } cs := string(c) s := strings.IndexByte(cs, ':') if s < 0 { return } return cs[:s], cs[s+1:], true } type readerOnly struct { io.Reader } func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error { inbound := session.InboundFromContext(ctx) if inbound != nil { inbound.User = &protocol.MemoryUser{ Level: s.config.UserLevel, } } reader := bufio.NewReaderSize(readerOnly{conn}, buf.Size) Start: if err := conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake)); err != nil { newError("failed to set read deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } request, err := http.ReadRequest(reader) if err != nil { trace := newError("failed to read http request").Base(err) if errors.Cause(err) != io.EOF && !isTimeout(errors.Cause(err)) { trace.AtWarning() } return trace } if len(s.config.Accounts) > 0 { user, pass, ok := parseBasicAuth(request.Header.Get("Proxy-Authorization")) if !ok || !s.config.HasAccount(user, pass) { return common.Error2(conn.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic realm=\"proxy\"\r\n\r\n"))) } if inbound != nil { inbound.User.Email = user } } newError("request to Method [", request.Method, "] Host [", request.Host, "] with URL [", request.URL, "]").WriteToLog(session.ExportIDToError(ctx)) if err := conn.SetReadDeadline(time.Time{}); err != nil { newError("failed to clear read deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } defaultPort := net.Port(80) if strings.EqualFold(request.URL.Scheme, "https") { defaultPort = net.Port(443) } host := request.Host if host == "" { host = request.URL.Host } dest, err := http_proto.ParseHost(host, defaultPort) if err != nil { return newError("malformed proxy host: ", host).AtWarning().Base(err) } ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: conn.RemoteAddr(), To: request.URL, Status: log.AccessAccepted, Reason: "", }) if strings.EqualFold(request.Method, "CONNECT") { return s.handleConnect(ctx, request, reader, conn, dest, dispatcher) } keepAlive := (strings.TrimSpace(strings.ToLower(request.Header.Get("Proxy-Connection"))) == "keep-alive") err = s.handlePlainHTTP(ctx, request, conn, dest, dispatcher) if err == errWaitAnother { if keepAlive { goto Start } err = nil } return err } func (s *Server) handleConnect(ctx context.Context, _ *http.Request, reader *bufio.Reader, conn internet.Connection, dest net.Destination, dispatcher routing.Dispatcher) error { _, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) if err != nil { return newError("failed to write back OK response").Base(err) } plcy := s.policy() ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, plcy.Buffer) link, err := dispatcher.Dispatch(ctx, dest) if err != nil { return err } if reader.Buffered() > 0 { payload, err := buf.ReadFrom(io.LimitReader(reader, int64(reader.Buffered()))) if err != nil { return err } if err := link.Writer.WriteMultiBuffer(payload); err != nil { return err } reader = nil } requestDone := func() error { defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly) return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) } responseDone := func() error { defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) v2writer := buf.NewWriter(conn) if err := buf.Copy(link.Reader, v2writer, buf.UpdateActivity(timer)); err != nil { return err } return nil } closeWriter := task.OnSuccess(requestDone, task.Close(link.Writer)) if err := task.Run(ctx, closeWriter, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) return newError("connection ends").Base(err) } return nil } var errWaitAnother = newError("keep alive") func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, writer io.Writer, dest net.Destination, dispatcher routing.Dispatcher) error { if !s.config.AllowTransparent && request.URL.Host == "" { // RFC 2068 (HTTP/1.1) requires URL to be absolute URL in HTTP proxy. response := &http.Response{ Status: "Bad Request", StatusCode: 400, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: http.Header(make(map[string][]string)), Body: nil, ContentLength: 0, Close: true, } response.Header.Set("Proxy-Connection", "close") response.Header.Set("Connection", "close") return response.Write(writer) } if len(request.URL.Host) > 0 { request.Host = request.URL.Host } http_proto.RemoveHopByHopHeaders(request.Header) // Prevent UA from being set to golang's default ones if request.Header.Get("User-Agent") == "" { request.Header.Set("User-Agent", "") } content := &session.Content{} content.SetAttribute(":method", strings.ToUpper(request.Method)) content.SetAttribute(":path", request.URL.Path) for key := range request.Header { value := request.Header.Get(key) content.SetAttribute(strings.ToLower(key), value) } ctx = session.ContextWithContent(ctx, content) link, err := dispatcher.Dispatch(ctx, dest) if err != nil { return err } // Plain HTTP request is not a stream. The request always finishes before response. Hence, request has to be closed later. defer common.Close(link.Writer) var result error = errWaitAnother requestDone := func() error { request.Header.Set("Connection", "close") requestWriter := buf.NewBufferedWriter(link.Writer) common.Must(requestWriter.SetBuffered(false)) if err := request.Write(requestWriter); err != nil { return newError("failed to write whole request").Base(err).AtWarning() } return nil } responseDone := func() error { responseReader := bufio.NewReaderSize(&buf.BufferedReader{Reader: link.Reader}, buf.Size) response, err := readResponseAndHandle100Continue(responseReader, request, writer) if err == nil { http_proto.RemoveHopByHopHeaders(response.Header) if response.ContentLength >= 0 { response.Header.Set("Proxy-Connection", "keep-alive") response.Header.Set("Connection", "keep-alive") response.Header.Set("Keep-Alive", "timeout=4") response.Close = false } else { response.Close = true result = nil } defer response.Body.Close() } else { newError("failed to read response from ", request.Host).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) response = &http.Response{ Status: "Service Unavailable", StatusCode: 503, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: http.Header(make(map[string][]string)), Body: nil, ContentLength: 0, Close: true, } response.Header.Set("Connection", "close") response.Header.Set("Proxy-Connection", "close") } if err := response.Write(writer); err != nil { return newError("failed to write response").Base(err).AtWarning() } return nil } if err := task.Run(ctx, requestDone, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) return newError("connection ends").Base(err) } return result } // Sometimes, server might send 1xx response to client // it should not be processed by http proxy handler, just forward it to client func readResponseAndHandle100Continue(r *bufio.Reader, req *http.Request, writer io.Writer) (*http.Response, error) { // have a little look of response peekBytes, err := r.Peek(56) if err == nil || err == bufio.ErrBufferFull { str := string(peekBytes) ResponseLine := strings.Split(str, "\r\n")[0] _, status, _ := strings.Cut(ResponseLine, " ") // only handle 1xx response if strings.HasPrefix(status, "1") { ResponseHeader1xx := []byte{} // read until \r\n\r\n (end of http response header) for { data, err := r.ReadSlice('\n') if err != nil { return nil, newError("failed to read http 1xx response").Base(err).AtError() } ResponseHeader1xx = append(ResponseHeader1xx, data...) if bytes.Equal(ResponseHeader1xx[len(ResponseHeader1xx)-4:], []byte{'\r', '\n', '\r', '\n'}) { break } if len(ResponseHeader1xx) > 1024 { return nil, newError("too big http 1xx response").AtError() } } writer.Write(ResponseHeader1xx) } } return http.ReadResponse(r, req) } func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) })) } ================================================ FILE: proxy/http/simplified/config.go ================================================ package simplified import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/proxy/http" ) func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*ServerConfig) _ = simplifiedServer fullServer := &http.ServerConfig{} return common.CreateObject(ctx, fullServer) })) common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedClient := config.(*ClientConfig) fullClient := &http.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: simplifiedClient.Address, Port: simplifiedClient.Port, }, }, H1SkipWaitForReply: simplifiedClient.H1SkipWaitForReply, } return common.CreateObject(ctx, fullClient) })) } ================================================ FILE: proxy/http/simplified/config.pb.go ================================================ package simplified import ( net "github.com/v2fly/v2ray-core/v5/common/net" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_http_simplified_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_simplified_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_http_simplified_config_proto_rawDescGZIP(), []int{0} } type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` H1SkipWaitForReply bool `protobuf:"varint,3,opt,name=h1_skip_wait_for_reply,json=h1SkipWaitForReply,proto3" json:"h1_skip_wait_for_reply,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_http_simplified_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_http_simplified_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_http_simplified_config_proto_rawDescGZIP(), []int{1} } func (x *ClientConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *ClientConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *ClientConfig) GetH1SkipWaitForReply() bool { if x != nil { return x.H1SkipWaitForReply } return false } var File_proxy_http_simplified_config_proto protoreflect.FileDescriptor const file_proxy_http_simplified_config_proto_rawDesc = "" + "\n" + "\"proxy/http/simplified/config.proto\x12 v2ray.core.proxy.http.simplified\x1a common/protoext/extensions.proto\x1a\x18common/net/address.proto\"#\n" + "\fServerConfig:\x13\x82\xb5\x18\x0f\n" + "\ainbound\x12\x04http\"\xad\x01\n" + "\fClientConfig\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x122\n" + "\x16h1_skip_wait_for_reply\x18\x03 \x01(\bR\x12h1SkipWaitForReply:\x18\x82\xb5\x18\x14\n" + "\boutbound\x12\x04http\x90\xff)\x01B\x81\x01\n" + "$com.v2ray.core.proxy.http.simplifiedP\x01Z4github.com/v2fly/v2ray-core/v5/proxy/http/simplified\xaa\x02 V2Ray.Core.Proxy.Http.Simplifiedb\x06proto3" var ( file_proxy_http_simplified_config_proto_rawDescOnce sync.Once file_proxy_http_simplified_config_proto_rawDescData []byte ) func file_proxy_http_simplified_config_proto_rawDescGZIP() []byte { file_proxy_http_simplified_config_proto_rawDescOnce.Do(func() { file_proxy_http_simplified_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_http_simplified_config_proto_rawDesc), len(file_proxy_http_simplified_config_proto_rawDesc))) }) return file_proxy_http_simplified_config_proto_rawDescData } var file_proxy_http_simplified_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_http_simplified_config_proto_goTypes = []any{ (*ServerConfig)(nil), // 0: v2ray.core.proxy.http.simplified.ServerConfig (*ClientConfig)(nil), // 1: v2ray.core.proxy.http.simplified.ClientConfig (*net.IPOrDomain)(nil), // 2: v2ray.core.common.net.IPOrDomain } var file_proxy_http_simplified_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.http.simplified.ClientConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_proxy_http_simplified_config_proto_init() } func file_proxy_http_simplified_config_proto_init() { if File_proxy_http_simplified_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_http_simplified_config_proto_rawDesc), len(file_proxy_http_simplified_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_http_simplified_config_proto_goTypes, DependencyIndexes: file_proxy_http_simplified_config_proto_depIdxs, MessageInfos: file_proxy_http_simplified_config_proto_msgTypes, }.Build() File_proxy_http_simplified_config_proto = out.File file_proxy_http_simplified_config_proto_goTypes = nil file_proxy_http_simplified_config_proto_depIdxs = nil } ================================================ FILE: proxy/http/simplified/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.http.simplified; option csharp_namespace = "V2Ray.Core.Proxy.Http.Simplified"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/http/simplified"; option java_package = "com.v2ray.core.proxy.http.simplified"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "common/net/address.proto"; message ServerConfig{ option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "http"; } message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "http"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; bool h1_skip_wait_for_reply = 3; } ================================================ FILE: proxy/hysteria2/client.go ================================================ package hysteria2 import ( "context" hyProtocol "github.com/v2fly/hysteria/core/v2/international/protocol" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" hyTransport "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) // Client is an inbound handler type Client struct { serverPicker protocol.ServerPicker policyManager policy.Manager } // NewClient create a new client. func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { serverList := protocol.NewServerList() for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { return nil, newError("failed to parse server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { return nil, newError("0 server") } v := core.MustFromContext(ctx) client := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return client, nil } // Process implements OutboundHandler.Process(). func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified") } destination := outbound.Target network := destination.Network var server *protocol.ServerSpec var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { server = c.serverPicker.PickServer() rawConn, err := dialer.Dial(ctx, server.Destination()) if err != nil { return err } conn = rawConn return nil }) if err != nil { return newError("failed to find an available destination").AtWarning().Base(err) } newError("tunneling request to ", destination, " via ", server.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx)) defer conn.Close() iConn := conn if statConn, ok := conn.(*internet.StatCouterConnection); ok { iConn = statConn.Connection // will not count the UDP traffic. } hyConn, IsHy2Transport := iConn.(*hyTransport.HyConn) if !IsHy2Transport && network == net.Network_UDP { // hysteria2 need to use udp extension to proxy UDP. return newError(hyTransport.CanNotUseUDPExtension) } user := server.PickUser() userLevel := uint32(0) if user != nil { userLevel = user.Level } sessionPolicy := c.policyManager.ForLevel(userLevel) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil { postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) var buffer [2048]byte n, addr, err := packetConn.ReadFrom(buffer[:]) if err != nil { return newError("failed to read a packet").Base(err) } dest := net.DestinationFromAddr(addr) bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) connWriter := &ConnWriter{Writer: bufferWriter, Target: dest} packetWriter := &PacketWriter{Writer: connWriter, Target: dest, HyConn: hyConn} // write some request payload to buffer if _, err := packetWriter.WriteTo(buffer[:n], addr); err != nil { return newError("failed to write a request payload").Base(err) } // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer if err = bufferWriter.SetBuffered(false); err != nil { return newError("failed to flush payload").Base(err).AtWarning() } return udp.CopyPacketConn(packetWriter, packetConn, udp.UpdateActivity(timer)) } getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) packetReader := &PacketReader{Reader: conn, HyConn: hyConn} packetConnectionReader := &PacketConnectionReader{reader: packetReader} return udp.CopyPacketConn(packetConn, packetConnectionReader, udp.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer)) if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) var bodyWriter buf.Writer bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) connWriter := &ConnWriter{Writer: bufferWriter, Target: destination} bodyWriter = connWriter if network == net.Network_UDP { bodyWriter = &PacketWriter{Writer: connWriter, Target: destination, HyConn: hyConn} } else { // write some request payload to buffer err = buf.CopyOnceTimeout(link.Reader, bodyWriter, proxy.FirstPayloadTimeout) switch err { case buf.ErrNotTimeoutReader, buf.ErrReadTimeout: if err := connWriter.WriteTCPHeader(); err != nil { return newError("failed to write request header").Base(err).AtWarning() } case nil: default: return newError("failed to write a request payload").Base(err).AtWarning() } // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer if err = bufferWriter.SetBuffered(false); err != nil { return newError("failed to flush payload").Base(err).AtWarning() } } if err = buf.Copy(link.Reader, bodyWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer request payload").Base(err).AtInfo() } return nil } getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) var reader buf.Reader if network == net.Network_UDP { reader = &PacketReader{ Reader: conn, HyConn: hyConn, } } else { ok, msg, err := hyProtocol.ReadTCPResponse(conn) if err != nil { return err } if !ok { return newError(msg) } reader = buf.NewReader(conn) } return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer)) if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewClient(ctx, config.(*ClientConfig)) })) } ================================================ FILE: proxy/hysteria2/config.go ================================================ package hysteria2 import ( "github.com/v2fly/v2ray-core/v5/common/protocol" ) // MemoryAccount is an account type converted from Account. type MemoryAccount struct{} // AsAccount implements protocol.AsAccount. func (a *Account) AsAccount() (protocol.Account, error) { return &MemoryAccount{}, nil } // Equals implements protocol.Account.Equals(). func (a *MemoryAccount) Equals(another protocol.Account) bool { return false } ================================================ FILE: proxy/hysteria2/config.pb.go ================================================ package hysteria2 import ( packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" protocol "github.com/v2fly/v2ray-core/v5/common/protocol" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Account struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Account) Reset() { *x = Account{} mi := &file_proxy_hysteria2_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Account) String() string { return protoimpl.X.MessageStringOf(x) } func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_hysteria2_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Account.ProtoReflect.Descriptor instead. func (*Account) Descriptor() ([]byte, []int) { return file_proxy_hysteria2_config_proto_rawDescGZIP(), []int{0} } type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_hysteria2_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_hysteria2_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_hysteria2_config_proto_rawDescGZIP(), []int{1} } func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint { if x != nil { return x.Server } return nil } type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,1,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_hysteria2_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_hysteria2_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_hysteria2_config_proto_rawDescGZIP(), []int{2} } func (x *ServerConfig) GetPacketEncoding() packetaddr.PacketAddrType { if x != nil { return x.PacketEncoding } return packetaddr.PacketAddrType(0) } var File_proxy_hysteria2_config_proto protoreflect.FileDescriptor const file_proxy_hysteria2_config_proto_rawDesc = "" + "\n" + "\x1cproxy/hysteria2/config.proto\x12\x1av2ray.core.proxy.hysteria2\x1a\"common/net/packetaddr/config.proto\x1a!common/protocol/server_spec.proto\x1a common/protoext/extensions.proto\"\t\n" + "\aAccount\"m\n" + "\fClientConfig\x12B\n" + "\x06server\x18\x01 \x03(\v2*.v2ray.core.common.protocol.ServerEndpointR\x06server:\x19\x82\xb5\x18\x15\n" + "\boutbound\x12\thysteria2\"|\n" + "\fServerConfig\x12R\n" + "\x0fpacket_encoding\x18\x01 \x01(\x0e2).v2ray.core.net.packetaddr.PacketAddrTypeR\x0epacketEncoding:\x18\x82\xb5\x18\x14\n" + "\ainbound\x12\thysteria2Bo\n" + "\x1ecom.v2ray.core.proxy.hysteria2P\x01Z.github.com/v2fly/v2ray-core/v5/proxy/hysteria2\xaa\x02\x1aV2Ray.Core.Proxy.Hysteria2b\x06proto3" var ( file_proxy_hysteria2_config_proto_rawDescOnce sync.Once file_proxy_hysteria2_config_proto_rawDescData []byte ) func file_proxy_hysteria2_config_proto_rawDescGZIP() []byte { file_proxy_hysteria2_config_proto_rawDescOnce.Do(func() { file_proxy_hysteria2_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_hysteria2_config_proto_rawDesc), len(file_proxy_hysteria2_config_proto_rawDesc))) }) return file_proxy_hysteria2_config_proto_rawDescData } var file_proxy_hysteria2_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_proxy_hysteria2_config_proto_goTypes = []any{ (*Account)(nil), // 0: v2ray.core.proxy.hysteria2.Account (*ClientConfig)(nil), // 1: v2ray.core.proxy.hysteria2.ClientConfig (*ServerConfig)(nil), // 2: v2ray.core.proxy.hysteria2.ServerConfig (*protocol.ServerEndpoint)(nil), // 3: v2ray.core.common.protocol.ServerEndpoint (packetaddr.PacketAddrType)(0), // 4: v2ray.core.net.packetaddr.PacketAddrType } var file_proxy_hysteria2_config_proto_depIdxs = []int32{ 3, // 0: v2ray.core.proxy.hysteria2.ClientConfig.server:type_name -> v2ray.core.common.protocol.ServerEndpoint 4, // 1: v2ray.core.proxy.hysteria2.ServerConfig.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_proxy_hysteria2_config_proto_init() } func file_proxy_hysteria2_config_proto_init() { if File_proxy_hysteria2_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_hysteria2_config_proto_rawDesc), len(file_proxy_hysteria2_config_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_hysteria2_config_proto_goTypes, DependencyIndexes: file_proxy_hysteria2_config_proto_depIdxs, MessageInfos: file_proxy_hysteria2_config_proto_msgTypes, }.Build() File_proxy_hysteria2_config_proto = out.File file_proxy_hysteria2_config_proto_goTypes = nil file_proxy_hysteria2_config_proto_depIdxs = nil } ================================================ FILE: proxy/hysteria2/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.hysteria2; option csharp_namespace = "V2Ray.Core.Proxy.Hysteria2"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/hysteria2"; option java_package = "com.v2ray.core.proxy.hysteria2"; option java_multiple_files = true; import "common/net/packetaddr/config.proto"; import "common/protocol/server_spec.proto"; import "common/protoext/extensions.proto"; message Account { } message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "hysteria2"; repeated v2ray.core.common.protocol.ServerEndpoint server = 1; } message ServerConfig { option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "hysteria2"; v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 1; } ================================================ FILE: proxy/hysteria2/errors.generated.go ================================================ package hysteria2 import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/hysteria2/hysteria2.go ================================================ package hysteria2 ================================================ FILE: proxy/hysteria2/protocol.go ================================================ package hysteria2 import ( "io" "math/rand" "github.com/apernet/quic-go/quicvarint" hyProtocol "github.com/v2fly/hysteria/core/v2/international/protocol" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" hyTransport "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2" ) const ( paddingChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ) // ConnWriter is TCP Connection Writer Wrapper type ConnWriter struct { io.Writer Target net.Destination TCPHeaderSent bool } // Write implements io.Writer func (c *ConnWriter) Write(p []byte) (n int, err error) { if !c.TCPHeaderSent { if err := c.writeTCPHeader(); err != nil { return 0, newError("failed to write request header").Base(err) } } return c.Writer.Write(p) } // WriteMultiBuffer implements buf.Writer func (c *ConnWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { defer buf.ReleaseMulti(mb) for _, b := range mb { if !b.IsEmpty() { if _, err := c.Write(b.Bytes()); err != nil { return err } } } return nil } func (c *ConnWriter) WriteTCPHeader() error { if !c.TCPHeaderSent { if err := c.writeTCPHeader(); err != nil { return err } } return nil } func QuicLen(s int) int { return quicvarint.Len(uint64(s)) } func (c *ConnWriter) writeTCPHeader() error { c.TCPHeaderSent = true paddingLen := 64 + rand.Intn(512-64) padding := make([]byte, paddingLen) for i := range padding { padding[i] = paddingChars[rand.Intn(len(paddingChars))] } addressAndPort := c.Target.NetAddr() addressLen := len(addressAndPort) if addressLen > hyProtocol.MaxAddressLength { return newError("address length too large: ", addressLen) } size := QuicLen(addressLen) + addressLen + QuicLen(paddingLen) + paddingLen buf := make([]byte, size) i := hyProtocol.VarintPut(buf, uint64(addressLen)) i += copy(buf[i:], addressAndPort) i += hyProtocol.VarintPut(buf[i:], uint64(paddingLen)) copy(buf[i:], padding) _, err := c.Writer.Write(buf) return err } // PacketWriter UDP Connection Writer Wrapper type PacketWriter struct { io.Writer HyConn *hyTransport.HyConn Target net.Destination } // WriteMultiBuffer implements buf.Writer func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { for _, b := range mb { if b.IsEmpty() { continue } if _, err := w.writePacket(b.Bytes(), w.Target); err != nil { buf.ReleaseMulti(mb) return err } } return nil } // WriteMultiBufferWithMetadata writes udp packet with destination specified func (w *PacketWriter) WriteMultiBufferWithMetadata(mb buf.MultiBuffer, dest net.Destination) error { for _, b := range mb { if b.IsEmpty() { continue } if _, err := w.writePacket(b.Bytes(), dest); err != nil { buf.ReleaseMulti(mb) return err } } return nil } func (w *PacketWriter) WriteTo(payload []byte, addr net.Addr) (int, error) { dest := net.DestinationFromAddr(addr) return w.writePacket(payload, dest) } func (w *PacketWriter) writePacket(payload []byte, dest net.Destination) (int, error) { return w.HyConn.WritePacket(payload, dest) } // ConnReader is TCP Connection Reader Wrapper type ConnReader struct { io.Reader Target net.Destination } // Read implements io.Reader func (c *ConnReader) Read(p []byte) (int, error) { return c.Reader.Read(p) } // ReadMultiBuffer implements buf.Reader func (c *ConnReader) ReadMultiBuffer() (buf.MultiBuffer, error) { b := buf.New() _, err := b.ReadFrom(c) if err != nil { return nil, err } return buf.MultiBuffer{b}, nil } // PacketPayload combines udp payload and destination type PacketPayload struct { Target net.Destination Buffer buf.MultiBuffer } // PacketReader is UDP Connection Reader Wrapper type PacketReader struct { io.Reader HyConn *hyTransport.HyConn } // ReadMultiBuffer implements buf.Reader func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { p, err := r.ReadMultiBufferWithMetadata() if p != nil { return p.Buffer, err } return nil, err } // ReadMultiBufferWithMetadata reads udp packet with destination func (r *PacketReader) ReadMultiBufferWithMetadata() (*PacketPayload, error) { _, data, dest, err := r.HyConn.ReadPacket() if err != nil { return nil, err } b := buf.FromBytes(data) return &PacketPayload{Target: *dest, Buffer: buf.MultiBuffer{b}}, nil } type PacketConnectionReader struct { reader *PacketReader payload *PacketPayload } func (r *PacketConnectionReader) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if r.payload == nil || r.payload.Buffer.IsEmpty() { r.payload, err = r.reader.ReadMultiBufferWithMetadata() if err != nil { return } } addr = &net.UDPAddr{ IP: r.payload.Target.Address.IP(), Port: int(r.payload.Target.Port), } r.payload.Buffer, n = buf.SplitFirstBytes(r.payload.Buffer, p) return } ================================================ FILE: proxy/hysteria2/server.go ================================================ package hysteria2 import ( "context" "io" "time" hyProtocol "github.com/v2fly/hysteria/core/v2/international/protocol" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" udp_proto "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet" hyTransport "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) })) } // Server is an inbound connection handler that handles messages in protocol. type Server struct { policyManager policy.Manager packetEncoding packetaddr.PacketAddrType } // NewServer creates a new inbound handler. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { v := core.MustFromContext(ctx) server := &Server{ policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), packetEncoding: config.PacketEncoding, } return server, nil } // Network implements proxy.Inbound.Network(). func (s *Server) Network() []net.Network { return []net.Network{net.Network_TCP, net.Network_UNIX} } // Process implements proxy.Inbound.Process(). func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error { sid := session.ExportIDToError(ctx) iConn := conn if statConn, ok := conn.(*internet.StatCouterConnection); ok { iConn = statConn.Connection // will not count the UDP traffic. } hyConn, IsHy2Transport := iConn.(*hyTransport.HyConn) if IsHy2Transport && hyConn.IsUDPExtension { network = net.Network_UDP } if !IsHy2Transport && network == net.Network_UDP { return newError(hyTransport.CanNotUseUDPExtension) } sessionPolicy := s.policyManager.ForLevel(0) if err := conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() } bufferedReader := &buf.BufferedReader{ Reader: buf.NewReader(conn), } clientReader := &ConnReader{Reader: bufferedReader} if err := conn.SetReadDeadline(time.Time{}); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() } if network == net.Network_UDP { // handle udp request return s.handleUDPPayload(ctx, &PacketReader{Reader: clientReader, HyConn: hyConn}, &PacketWriter{Writer: conn, HyConn: hyConn}, dispatcher) } var reqAddr string var err error reqAddr, err = hyProtocol.ReadTCPRequest(conn) if err != nil { return newError("failed to parse header").Base(err) } err = hyProtocol.WriteTCPResponse(conn, true, "") if err != nil { return newError("failed to send response").Base(err) } address, stringPort, err := net.SplitHostPort(reqAddr) if err != nil { return err } port, err := net.PortFromString(stringPort) if err != nil { return err } destination := net.Destination{Network: network, Address: net.ParseAddress(address), Port: port} inbound := session.InboundFromContext(ctx) if inbound == nil { panic("no inbound metadata") } sessionPolicy = s.policyManager.ForLevel(0) ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: conn.RemoteAddr(), To: destination, Status: log.AccessAccepted, Reason: "", }) newError("received request for ", destination).WriteToLog(sid) return s.handleConnection(ctx, sessionPolicy, destination, clientReader, buf.NewWriter(conn), dispatcher) } func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Session, destination net.Destination, clientReader buf.Reader, clientWriter buf.Writer, dispatcher routing.Dispatcher, ) error { ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) link, err := dispatcher.Dispatch(ctx, destination) if err != nil { return newError("failed to dispatch request to ", destination).Base(err) } requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if err := buf.Copy(clientReader, link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer request").Base(err) } return nil } responseDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(link.Reader, clientWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to write response").Base(err) } return nil } requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDonePost, responseDone); err != nil { common.Must(common.Interrupt(link.Reader)) common.Must(common.Interrupt(link.Writer)) return newError("connection ends").Base(err) } return nil } func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error { udpDispatcherConstructor := udp.NewSplitDispatcher switch s.packetEncoding { case packetaddr.PacketAddrType_None: case packetaddr.PacketAddrType_Packet: packetAddrDispatcherFactory := udp.NewPacketAddrDispatcherCreator(ctx) udpDispatcherConstructor = packetAddrDispatcherFactory.NewPacketAddrDispatcher } udpServer := udpDispatcherConstructor(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) { if err := clientWriter.WriteMultiBufferWithMetadata(buf.MultiBuffer{packet.Payload}, packet.Source); err != nil { newError("failed to write response").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } }) inbound := session.InboundFromContext(ctx) for { select { case <-ctx.Done(): return nil default: p, err := clientReader.ReadMultiBufferWithMetadata() if err != nil { if errors.Cause(err) != io.EOF { return newError("unexpected EOF").Base(err) } return nil } currentPacketCtx := ctx currentPacketCtx = log.ContextWithAccessMessage(currentPacketCtx, &log.AccessMessage{ From: inbound.Source, To: p.Target, Status: log.AccessAccepted, Reason: "", }) newError("tunnelling request to ", p.Target).WriteToLog(session.ExportIDToError(ctx)) for _, b := range p.Buffer { udpServer.Dispatch(currentPacketCtx, p.Target, b) } } } } ================================================ FILE: proxy/loopback/config.go ================================================ package loopback //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: proxy/loopback/config.pb.go ================================================ package loopback import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` InboundTag string `protobuf:"bytes,1,opt,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_loopback_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_loopback_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_loopback_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetInboundTag() string { if x != nil { return x.InboundTag } return "" } var File_proxy_loopback_config_proto protoreflect.FileDescriptor const file_proxy_loopback_config_proto_rawDesc = "" + "\n" + "\x1bproxy/loopback/config.proto\x12\x19v2ray.core.proxy.loopback\x1a common/protoext/extensions.proto\"C\n" + "\x06Config\x12\x1f\n" + "\vinbound_tag\x18\x01 \x01(\tR\n" + "inboundTag:\x18\x82\xb5\x18\x14\n" + "\boutbound\x12\bloopbackBl\n" + "\x1dcom.v2ray.core.proxy.loopbackP\x01Z-github.com/v2fly/v2ray-core/v5/proxy/loopback\xaa\x02\x19V2Ray.Core.Proxy.Loopbackb\x06proto3" var ( file_proxy_loopback_config_proto_rawDescOnce sync.Once file_proxy_loopback_config_proto_rawDescData []byte ) func file_proxy_loopback_config_proto_rawDescGZIP() []byte { file_proxy_loopback_config_proto_rawDescOnce.Do(func() { file_proxy_loopback_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_loopback_config_proto_rawDesc), len(file_proxy_loopback_config_proto_rawDesc))) }) return file_proxy_loopback_config_proto_rawDescData } var file_proxy_loopback_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_loopback_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.proxy.loopback.Config } var file_proxy_loopback_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_proxy_loopback_config_proto_init() } func file_proxy_loopback_config_proto_init() { if File_proxy_loopback_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_loopback_config_proto_rawDesc), len(file_proxy_loopback_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_loopback_config_proto_goTypes, DependencyIndexes: file_proxy_loopback_config_proto_depIdxs, MessageInfos: file_proxy_loopback_config_proto_msgTypes, }.Build() File_proxy_loopback_config_proto = out.File file_proxy_loopback_config_proto_goTypes = nil file_proxy_loopback_config_proto_depIdxs = nil } ================================================ FILE: proxy/loopback/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.loopback; option csharp_namespace = "V2Ray.Core.Proxy.Loopback"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/loopback"; option java_package = "com.v2ray.core.proxy.loopback"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "loopback"; string inbound_tag = 1; } ================================================ FILE: proxy/loopback/errors.generated.go ================================================ package loopback import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/loopback/loopback.go ================================================ //go:build !confonly // +build !confonly package loopback import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type Loopback struct { config *Config dispatcherInstance routing.Dispatcher } func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified.") } destination := outbound.Target newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx)) input := link.Reader output := link.Writer var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { dialDest := destination content := new(session.Content) content.SkipDNSResolve = true ctx = session.ContextWithContent(ctx, content) inbound := session.InboundFromContext(ctx) inbound.Tag = l.config.InboundTag ctx = session.ContextWithInbound(ctx, inbound) rawConn, err := l.dispatcherInstance.Dispatch(ctx, dialDest) if err != nil { return err } var readerOpt net.ConnectionOption if dialDest.Network == net.Network_TCP { readerOpt = net.ConnectionOutputMulti(rawConn.Reader) } else { readerOpt = net.ConnectionOutputMultiUDP(rawConn.Reader) } conn = net.NewConnection(net.ConnectionInputMulti(rawConn.Writer), readerOpt) return nil }) if err != nil { return newError("failed to open connection to ", destination).Base(err) } defer conn.Close() requestDone := func() error { var writer buf.Writer if destination.Network == net.Network_TCP { writer = buf.NewWriter(conn) } else { writer = &buf.SequentialWriter{Writer: conn} } if err := buf.Copy(input, writer); err != nil { return newError("failed to process request").Base(err) } return nil } responseDone := func() error { var reader buf.Reader if destination.Network == net.Network_TCP { reader = buf.NewReader(conn) } else { reader = buf.NewPacketReader(conn) } if err := buf.Copy(reader, output); err != nil { return newError("failed to process response").Base(err) } return nil } if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil { return newError("connection ends").Base(err) } return nil } func (l *Loopback) init(config *Config, dispatcherInstance routing.Dispatcher) error { l.dispatcherInstance = dispatcherInstance l.config = config return nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { l := new(Loopback) err := core.RequireFeatures(ctx, func(dispatcherInstance routing.Dispatcher) error { return l.init(config.(*Config), dispatcherInstance) }) return l, err })) } ================================================ FILE: proxy/proxy.go ================================================ // Package proxy contains all proxies used by V2Ray. // // To implement an inbound or outbound proxy, one needs to do the following: // 1. Implement the interface(s) below. // 2. Register a config creator through common.RegisterConfig. package proxy import ( "context" "time" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" ) // A timeout for reading the first payload from the client, used in 0-RTT optimizations. const FirstPayloadTimeout = 100 * time.Millisecond // An Inbound processes inbound connections. type Inbound interface { // Network returns a list of networks that this inbound supports. Connections with not-supported networks will not be passed into Process(). Network() []net.Network // Process processes a connection of given network. If necessary, the Inbound can dispatch the connection to an Outbound. Process(context.Context, net.Network, internet.Connection, routing.Dispatcher) error } // An Outbound process outbound connections. type Outbound interface { // Process processes the given connection. The given dialer may be used to dial a system outbound connection. Process(context.Context, *transport.Link, internet.Dialer) error } // UserManager is the interface for Inbounds and Outbounds that can manage their users. type UserManager interface { // AddUser adds a new user. AddUser(context.Context, *protocol.MemoryUser) error // RemoveUser removes a user by email. RemoveUser(context.Context, string) error } type GetInbound interface { GetInbound() Inbound } type GetOutbound interface { GetOutbound() Outbound } ================================================ FILE: proxy/shadowsocks/client.go ================================================ package shadowsocks import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) // Client is a inbound handler for Shadowsocks protocol type Client struct { serverPicker protocol.ServerPicker policyManager policy.Manager } // NewClient create a new Shadowsocks client. func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { serverList := protocol.NewServerList() for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { return nil, newError("failed to parse server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { return nil, newError("0 server") } v := core.MustFromContext(ctx) client := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return client, nil } // Process implements OutboundHandler.Process(). func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified") } destination := outbound.Target network := destination.Network var server *protocol.ServerSpec var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { server = c.serverPicker.PickServer() dest := server.Destination() dest.Network = network rawConn, err := dialer.Dial(ctx, dest) if err != nil { return err } conn = rawConn return nil }) if err != nil { return newError("failed to find an available destination").AtWarning().Base(err) } newError("tunneling request to ", destination, " via ", network, ":", server.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx)) defer conn.Close() request := &protocol.RequestHeader{ Version: Version, Address: destination.Address, Port: destination.Port, } if destination.Network == net.Network_TCP { request.Command = protocol.RequestCommandTCP } else { request.Command = protocol.RequestCommandUDP } user := server.PickUser() _, ok := user.Account.(*MemoryAccount) if !ok { return newError("user account is not valid") } request.User = user sessionPolicy := c.policyManager.ForLevel(user.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil { requestDone := func() error { protocolWriter := &UDPWriter{ Writer: conn, Request: request, } return udp.CopyPacketConn(protocolWriter, packetConn, udp.UpdateActivity(timer)) } responseDone := func() error { protocolReader := &UDPReader{ Reader: conn, User: user, } return udp.CopyPacketConn(packetConn, protocolReader, udp.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } if request.Command == protocol.RequestCommandTCP { requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) bodyWriter, err := WriteTCPRequest(request, bufferedWriter) if err != nil { return newError("failed to write request").Base(err) } if err = buf.CopyOnceTimeout(link.Reader, bodyWriter, proxy.FirstPayloadTimeout); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { return newError("failed to write A request payload").Base(err).AtWarning() } if err := bufferedWriter.SetBuffered(false); err != nil { return err } return buf.Copy(link.Reader, bodyWriter, buf.UpdateActivity(timer)) } responseDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) responseReader, err := ReadTCPResponse(user, conn) if err != nil { return err } return buf.Copy(responseReader, link.Writer, buf.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } if request.Command == protocol.RequestCommandUDP { writer := &buf.SequentialWriter{Writer: &UDPWriter{ Writer: conn, Request: request, }} requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all UDP request").Base(err) } return nil } responseDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) reader := &UDPReader{ Reader: conn, User: user, } if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all UDP response").Base(err) } return nil } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } return nil } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewClient(ctx, config.(*ClientConfig)) })) } ================================================ FILE: proxy/shadowsocks/config.go ================================================ package shadowsocks import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/md5" "crypto/sha1" "io" "strings" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/hkdf" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/antireplay" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/crypto" "github.com/v2fly/v2ray-core/v5/common/protocol" ) // MemoryAccount is an account type converted from Account. type MemoryAccount struct { Cipher Cipher Key []byte replayFilter antireplay.GeneralizedReplayFilter ReducedIVEntropy bool } // Equals implements protocol.Account.Equals(). func (a *MemoryAccount) Equals(another protocol.Account) bool { if account, ok := another.(*MemoryAccount); ok { return bytes.Equal(a.Key, account.Key) } return false } func (a *MemoryAccount) CheckIV(iv []byte) error { if a.replayFilter == nil { return nil } if a.replayFilter.Check(iv) { return nil } return newError("IV is not unique") } func createAesGcm(key []byte) cipher.AEAD { block, err := aes.NewCipher(key) common.Must(err) gcm, err := cipher.NewGCM(block) common.Must(err) return gcm } func createChaCha20Poly1305(key []byte) cipher.AEAD { ChaChaPoly1305, err := chacha20poly1305.New(key) common.Must(err) return ChaChaPoly1305 } func (a *Account) getCipher() (Cipher, error) { switch a.CipherType { case CipherType_AES_128_GCM: return &AEADCipher{ KeyBytes: 16, IVBytes: 16, AEADAuthCreator: createAesGcm, }, nil case CipherType_AES_256_GCM: return &AEADCipher{ KeyBytes: 32, IVBytes: 32, AEADAuthCreator: createAesGcm, }, nil case CipherType_CHACHA20_POLY1305: return &AEADCipher{ KeyBytes: 32, IVBytes: 32, AEADAuthCreator: createChaCha20Poly1305, }, nil case CipherType_NONE: return NoneCipher{}, nil default: return nil, newError("Unsupported cipher.") } } // AsAccount implements protocol.AsAccount. func (a *Account) AsAccount() (protocol.Account, error) { Cipher, err := a.getCipher() if err != nil { return nil, newError("failed to get cipher").Base(err) } return &MemoryAccount{ Cipher: Cipher, Key: passwordToCipherKey([]byte(a.Password), Cipher.KeySize()), replayFilter: func() antireplay.GeneralizedReplayFilter { if a.IvCheck { return antireplay.NewBloomRing() } return nil }(), ReducedIVEntropy: a.ExperimentReducedIvHeadEntropy, }, nil } // Cipher is an interface for all Shadowsocks ciphers. type Cipher interface { KeySize() int32 IVSize() int32 NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) IsAEAD() bool EncodePacket(key []byte, b *buf.Buffer) error DecodePacket(key []byte, b *buf.Buffer) error } type AEADCipher struct { KeyBytes int32 IVBytes int32 AEADAuthCreator func(key []byte) cipher.AEAD } func (*AEADCipher) IsAEAD() bool { return true } func (c *AEADCipher) KeySize() int32 { return c.KeyBytes } func (c *AEADCipher) IVSize() int32 { return c.IVBytes } func (c *AEADCipher) createAuthenticator(key []byte, iv []byte) *crypto.AEADAuthenticator { nonce := crypto.GenerateInitialAEADNonce() subkey := make([]byte, c.KeyBytes) hkdfSHA1(key, iv, subkey) return &crypto.AEADAuthenticator{ AEAD: c.AEADAuthCreator(subkey), NonceGenerator: nonce, } } func (c *AEADCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) { auth := c.createAuthenticator(key, iv) return crypto.NewAuthenticationWriter(auth, &crypto.AEADChunkSizeParser{ Auth: auth, }, writer, protocol.TransferTypeStream, nil), nil } func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) { auth := c.createAuthenticator(key, iv) return crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{ Auth: auth, }, reader, protocol.TransferTypeStream, nil), nil } func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error { ivLen := c.IVSize() payloadLen := b.Len() auth := c.createAuthenticator(key, b.BytesTo(ivLen)) b.Extend(int32(auth.Overhead())) _, err := auth.Seal(b.BytesTo(ivLen), b.BytesRange(ivLen, payloadLen)) return err } func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error { if b.Len() <= c.IVSize() { return newError("insufficient data: ", b.Len()) } ivLen := c.IVSize() payloadLen := b.Len() auth := c.createAuthenticator(key, b.BytesTo(ivLen)) bbb, err := auth.Open(b.BytesTo(ivLen), b.BytesRange(ivLen, payloadLen)) if err != nil { return err } b.Resize(ivLen, int32(len(bbb))) return nil } type NoneCipher struct{} func (NoneCipher) KeySize() int32 { return 0 } func (NoneCipher) IVSize() int32 { return 0 } func (NoneCipher) IsAEAD() bool { return false } func (NoneCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) { return buf.NewReader(reader), nil } func (NoneCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) { return buf.NewWriter(writer), nil } func (NoneCipher) EncodePacket(key []byte, b *buf.Buffer) error { return nil } func (NoneCipher) DecodePacket(key []byte, b *buf.Buffer) error { return nil } func CipherFromString(c string) CipherType { switch strings.ToLower(c) { case "aes-128-gcm", "aes_128_gcm", "aead_aes_128_gcm": return CipherType_AES_128_GCM case "aes-256-gcm", "aes_256_gcm", "aead_aes_256_gcm": return CipherType_AES_256_GCM case "chacha20-poly1305", "chacha20_poly1305", "aead_chacha20_poly1305", "chacha20-ietf-poly1305": return CipherType_CHACHA20_POLY1305 case "none", "plain": return CipherType_NONE default: return CipherType_UNKNOWN } } func passwordToCipherKey(password []byte, keySize int32) []byte { key := make([]byte, 0, keySize) md5Sum := md5.Sum(password) key = append(key, md5Sum[:]...) for int32(len(key)) < keySize { md5Hash := md5.New() common.Must2(md5Hash.Write(md5Sum[:])) common.Must2(md5Hash.Write(password)) md5Hash.Sum(md5Sum[:0]) key = append(key, md5Sum[:]...) } return key } func hkdfSHA1(secret, salt, outKey []byte) { r := hkdf.New(sha1.New, secret, salt, []byte("ss-subkey")) common.Must2(io.ReadFull(r, outKey)) } ================================================ FILE: proxy/shadowsocks/config.pb.go ================================================ package shadowsocks import ( net "github.com/v2fly/v2ray-core/v5/common/net" packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" protocol "github.com/v2fly/v2ray-core/v5/common/protocol" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type CipherType int32 const ( CipherType_UNKNOWN CipherType = 0 CipherType_AES_128_GCM CipherType = 1 CipherType_AES_256_GCM CipherType = 2 CipherType_CHACHA20_POLY1305 CipherType = 3 CipherType_NONE CipherType = 4 ) // Enum value maps for CipherType. var ( CipherType_name = map[int32]string{ 0: "UNKNOWN", 1: "AES_128_GCM", 2: "AES_256_GCM", 3: "CHACHA20_POLY1305", 4: "NONE", } CipherType_value = map[string]int32{ "UNKNOWN": 0, "AES_128_GCM": 1, "AES_256_GCM": 2, "CHACHA20_POLY1305": 3, "NONE": 4, } ) func (x CipherType) Enum() *CipherType { p := new(CipherType) *p = x return p } func (x CipherType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (CipherType) Descriptor() protoreflect.EnumDescriptor { return file_proxy_shadowsocks_config_proto_enumTypes[0].Descriptor() } func (CipherType) Type() protoreflect.EnumType { return &file_proxy_shadowsocks_config_proto_enumTypes[0] } func (x CipherType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use CipherType.Descriptor instead. func (CipherType) EnumDescriptor() ([]byte, []int) { return file_proxy_shadowsocks_config_proto_rawDescGZIP(), []int{0} } type Account struct { state protoimpl.MessageState `protogen:"open.v1"` Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` CipherType CipherType `protobuf:"varint,2,opt,name=cipher_type,json=cipherType,proto3,enum=v2ray.core.proxy.shadowsocks.CipherType" json:"cipher_type,omitempty"` IvCheck bool `protobuf:"varint,3,opt,name=iv_check,json=ivCheck,proto3" json:"iv_check,omitempty"` ExperimentReducedIvHeadEntropy bool `protobuf:"varint,90001,opt,name=experiment_reduced_iv_head_entropy,json=experimentReducedIvHeadEntropy,proto3" json:"experiment_reduced_iv_head_entropy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Account) Reset() { *x = Account{} mi := &file_proxy_shadowsocks_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Account) String() string { return protoimpl.X.MessageStringOf(x) } func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Account.ProtoReflect.Descriptor instead. func (*Account) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks_config_proto_rawDescGZIP(), []int{0} } func (x *Account) GetPassword() string { if x != nil { return x.Password } return "" } func (x *Account) GetCipherType() CipherType { if x != nil { return x.CipherType } return CipherType_UNKNOWN } func (x *Account) GetIvCheck() bool { if x != nil { return x.IvCheck } return false } func (x *Account) GetExperimentReducedIvHeadEntropy() bool { if x != nil { return x.ExperimentReducedIvHeadEntropy } return false } type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // UdpEnabled specified whether or not to enable UDP for Shadowsocks. // Deprecated. Use 'network' field. // // Deprecated: Marked as deprecated in proxy/shadowsocks/config.proto. UdpEnabled bool `protobuf:"varint,1,opt,name=udp_enabled,json=udpEnabled,proto3" json:"udp_enabled,omitempty"` User *protocol.User `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` Network []net.Network `protobuf:"varint,3,rep,packed,name=network,proto3,enum=v2ray.core.common.net.Network" json:"network,omitempty"` PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,4,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_shadowsocks_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks_config_proto_rawDescGZIP(), []int{1} } // Deprecated: Marked as deprecated in proxy/shadowsocks/config.proto. func (x *ServerConfig) GetUdpEnabled() bool { if x != nil { return x.UdpEnabled } return false } func (x *ServerConfig) GetUser() *protocol.User { if x != nil { return x.User } return nil } func (x *ServerConfig) GetNetwork() []net.Network { if x != nil { return x.Network } return nil } func (x *ServerConfig) GetPacketEncoding() packetaddr.PacketAddrType { if x != nil { return x.PacketEncoding } return packetaddr.PacketAddrType(0) } type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_shadowsocks_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks_config_proto_rawDescGZIP(), []int{2} } func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint { if x != nil { return x.Server } return nil } var File_proxy_shadowsocks_config_proto protoreflect.FileDescriptor const file_proxy_shadowsocks_config_proto_rawDesc = "" + "\n" + "\x1eproxy/shadowsocks/config.proto\x12\x1cv2ray.core.proxy.shadowsocks\x1a\x18common/net/network.proto\x1a\x1acommon/protocol/user.proto\x1a!common/protocol/server_spec.proto\x1a\"common/net/packetaddr/config.proto\"\xd9\x01\n" + "\aAccount\x12\x1a\n" + "\bpassword\x18\x01 \x01(\tR\bpassword\x12I\n" + "\vcipher_type\x18\x02 \x01(\x0e2(.v2ray.core.proxy.shadowsocks.CipherTypeR\n" + "cipherType\x12\x19\n" + "\biv_check\x18\x03 \x01(\bR\aivCheck\x12L\n" + "\"experiment_reduced_iv_head_entropy\x18\x91\xbf\x05 \x01(\bR\x1eexperimentReducedIvHeadEntropy\"\xf7\x01\n" + "\fServerConfig\x12#\n" + "\vudp_enabled\x18\x01 \x01(\bB\x02\x18\x01R\n" + "udpEnabled\x124\n" + "\x04user\x18\x02 \x01(\v2 .v2ray.core.common.protocol.UserR\x04user\x128\n" + "\anetwork\x18\x03 \x03(\x0e2\x1e.v2ray.core.common.net.NetworkR\anetwork\x12R\n" + "\x0fpacket_encoding\x18\x04 \x01(\x0e2).v2ray.core.net.packetaddr.PacketAddrTypeR\x0epacketEncoding\"R\n" + "\fClientConfig\x12B\n" + "\x06server\x18\x01 \x03(\v2*.v2ray.core.common.protocol.ServerEndpointR\x06server*\\\n" + "\n" + "CipherType\x12\v\n" + "\aUNKNOWN\x10\x00\x12\x0f\n" + "\vAES_128_GCM\x10\x01\x12\x0f\n" + "\vAES_256_GCM\x10\x02\x12\x15\n" + "\x11CHACHA20_POLY1305\x10\x03\x12\b\n" + "\x04NONE\x10\x04Bu\n" + " com.v2ray.core.proxy.shadowsocksP\x01Z0github.com/v2fly/v2ray-core/v5/proxy/shadowsocks\xaa\x02\x1cV2Ray.Core.Proxy.Shadowsocksb\x06proto3" var ( file_proxy_shadowsocks_config_proto_rawDescOnce sync.Once file_proxy_shadowsocks_config_proto_rawDescData []byte ) func file_proxy_shadowsocks_config_proto_rawDescGZIP() []byte { file_proxy_shadowsocks_config_proto_rawDescOnce.Do(func() { file_proxy_shadowsocks_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_shadowsocks_config_proto_rawDesc), len(file_proxy_shadowsocks_config_proto_rawDesc))) }) return file_proxy_shadowsocks_config_proto_rawDescData } var file_proxy_shadowsocks_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_proxy_shadowsocks_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_proxy_shadowsocks_config_proto_goTypes = []any{ (CipherType)(0), // 0: v2ray.core.proxy.shadowsocks.CipherType (*Account)(nil), // 1: v2ray.core.proxy.shadowsocks.Account (*ServerConfig)(nil), // 2: v2ray.core.proxy.shadowsocks.ServerConfig (*ClientConfig)(nil), // 3: v2ray.core.proxy.shadowsocks.ClientConfig (*protocol.User)(nil), // 4: v2ray.core.common.protocol.User (net.Network)(0), // 5: v2ray.core.common.net.Network (packetaddr.PacketAddrType)(0), // 6: v2ray.core.net.packetaddr.PacketAddrType (*protocol.ServerEndpoint)(nil), // 7: v2ray.core.common.protocol.ServerEndpoint } var file_proxy_shadowsocks_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.proxy.shadowsocks.Account.cipher_type:type_name -> v2ray.core.proxy.shadowsocks.CipherType 4, // 1: v2ray.core.proxy.shadowsocks.ServerConfig.user:type_name -> v2ray.core.common.protocol.User 5, // 2: v2ray.core.proxy.shadowsocks.ServerConfig.network:type_name -> v2ray.core.common.net.Network 6, // 3: v2ray.core.proxy.shadowsocks.ServerConfig.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType 7, // 4: v2ray.core.proxy.shadowsocks.ClientConfig.server:type_name -> v2ray.core.common.protocol.ServerEndpoint 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_proxy_shadowsocks_config_proto_init() } func file_proxy_shadowsocks_config_proto_init() { if File_proxy_shadowsocks_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_shadowsocks_config_proto_rawDesc), len(file_proxy_shadowsocks_config_proto_rawDesc)), NumEnums: 1, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_shadowsocks_config_proto_goTypes, DependencyIndexes: file_proxy_shadowsocks_config_proto_depIdxs, EnumInfos: file_proxy_shadowsocks_config_proto_enumTypes, MessageInfos: file_proxy_shadowsocks_config_proto_msgTypes, }.Build() File_proxy_shadowsocks_config_proto = out.File file_proxy_shadowsocks_config_proto_goTypes = nil file_proxy_shadowsocks_config_proto_depIdxs = nil } ================================================ FILE: proxy/shadowsocks/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.shadowsocks; option csharp_namespace = "V2Ray.Core.Proxy.Shadowsocks"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks"; option java_package = "com.v2ray.core.proxy.shadowsocks"; option java_multiple_files = true; import "common/net/network.proto"; import "common/protocol/user.proto"; import "common/protocol/server_spec.proto"; import "common/net/packetaddr/config.proto"; message Account { string password = 1; CipherType cipher_type = 2; bool iv_check = 3; bool experiment_reduced_iv_head_entropy = 90001; } enum CipherType { UNKNOWN = 0; AES_128_GCM = 1; AES_256_GCM = 2; CHACHA20_POLY1305 = 3; NONE = 4; } message ServerConfig { // UdpEnabled specified whether or not to enable UDP for Shadowsocks. // Deprecated. Use 'network' field. bool udp_enabled = 1 [deprecated = true]; v2ray.core.common.protocol.User user = 2; repeated v2ray.core.common.net.Network network = 3; v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 4; } message ClientConfig { repeated v2ray.core.common.protocol.ServerEndpoint server = 1; } ================================================ FILE: proxy/shadowsocks/config_test.go ================================================ package shadowsocks_test import ( "crypto/rand" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" ) func TestAEADCipherUDP(t *testing.T) { rawAccount := &shadowsocks.Account{ CipherType: shadowsocks.CipherType_AES_128_GCM, Password: "test", } account, err := rawAccount.AsAccount() common.Must(err) cipher := account.(*shadowsocks.MemoryAccount).Cipher key := make([]byte, cipher.KeySize()) common.Must2(rand.Read(key)) payload := make([]byte, 1024) common.Must2(rand.Read(payload)) b1 := buf.New() common.Must2(b1.ReadFullFrom(rand.Reader, cipher.IVSize())) common.Must2(b1.Write(payload)) common.Must(cipher.EncodePacket(key, b1)) common.Must(cipher.DecodePacket(key, b1)) if diff := cmp.Diff(b1.Bytes(), payload); diff != "" { t.Error(diff) } } ================================================ FILE: proxy/shadowsocks/errors.generated.go ================================================ package shadowsocks import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/shadowsocks/protocol.go ================================================ package shadowsocks import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "hash/crc32" "io" mrand "math/rand" gonet "net" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/drain" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) const ( Version = 1 ) var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6), protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain), protocol.WithAddressTypeParser(func(b byte) byte { return b & 0x0F }), ) // ReadTCPSession reads a Shadowsocks TCP session from the given reader, returns its header and remaining parts. func ReadTCPSession(user *protocol.MemoryUser, reader io.Reader) (*protocol.RequestHeader, buf.Reader, error) { account := user.Account.(*MemoryAccount) hashkdf := hmac.New(sha256.New, []byte("SSBSKDF")) hashkdf.Write(account.Key) behaviorSeed := crc32.ChecksumIEEE(hashkdf.Sum(nil)) drainer, err := drain.NewBehaviorSeedLimitedDrainer(int64(behaviorSeed), 16+38, 3266, 64) if err != nil { return nil, nil, newError("failed to initialize drainer").Base(err) } buffer := buf.New() defer buffer.Release() ivLen := account.Cipher.IVSize() var iv []byte if ivLen > 0 { if _, err := buffer.ReadFullFrom(reader, ivLen); err != nil { drainer.AcknowledgeReceive(int(buffer.Len())) return nil, nil, drain.WithError(drainer, reader, newError("failed to read IV").Base(err)) } iv = append([]byte(nil), buffer.BytesTo(ivLen)...) } r, err := account.Cipher.NewDecryptionReader(account.Key, iv, reader) if err != nil { drainer.AcknowledgeReceive(int(buffer.Len())) return nil, nil, drain.WithError(drainer, reader, newError("failed to initialize decoding stream").Base(err).AtError()) } br := &buf.BufferedReader{Reader: r} request := &protocol.RequestHeader{ Version: Version, User: user, Command: protocol.RequestCommandTCP, } drainer.AcknowledgeReceive(int(buffer.Len())) buffer.Clear() addr, port, err := addrParser.ReadAddressPort(buffer, br) if err != nil { drainer.AcknowledgeReceive(int(buffer.Len())) return nil, nil, drain.WithError(drainer, reader, newError("failed to read address").Base(err)) } request.Address = addr request.Port = port if request.Address == nil { drainer.AcknowledgeReceive(int(buffer.Len())) return nil, nil, drain.WithError(drainer, reader, newError("invalid remote address.")) } if ivError := account.CheckIV(iv); ivError != nil { drainer.AcknowledgeReceive(int(buffer.Len())) return nil, nil, drain.WithError(drainer, reader, newError("failed iv check").Base(ivError)) } return request, br, nil } // WriteTCPRequest writes Shadowsocks request into the given writer, and returns a writer for body. func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) { user := request.User account := user.Account.(*MemoryAccount) var iv []byte if account.Cipher.IVSize() > 0 { iv = make([]byte, account.Cipher.IVSize()) common.Must2(rand.Read(iv)) if account.ReducedIVEntropy { remapToPrintable(iv[:6]) } if ivError := account.CheckIV(iv); ivError != nil { return nil, newError("failed to mark outgoing iv").Base(ivError) } if err := buf.WriteAllBytes(writer, iv); err != nil { return nil, newError("failed to write IV") } } w, err := account.Cipher.NewEncryptionWriter(account.Key, iv, writer) if err != nil { return nil, newError("failed to create encoding stream").Base(err).AtError() } header := buf.New() if err := addrParser.WriteAddressPort(header, request.Address, request.Port); err != nil { return nil, newError("failed to write address").Base(err) } if err := w.WriteMultiBuffer(buf.MultiBuffer{header}); err != nil { return nil, newError("failed to write header").Base(err) } return w, nil } func ReadTCPResponse(user *protocol.MemoryUser, reader io.Reader) (buf.Reader, error) { account := user.Account.(*MemoryAccount) hashkdf := hmac.New(sha256.New, []byte("SSBSKDF")) hashkdf.Write(account.Key) behaviorSeed := crc32.ChecksumIEEE(hashkdf.Sum(nil)) drainer, err := drain.NewBehaviorSeedLimitedDrainer(int64(behaviorSeed), 16+38, 3266, 64) if err != nil { return nil, newError("failed to initialize drainer").Base(err) } var iv []byte if account.Cipher.IVSize() > 0 { iv = make([]byte, account.Cipher.IVSize()) if n, err := io.ReadFull(reader, iv); err != nil { return nil, newError("failed to read IV").Base(err) } else { // nolint: revive drainer.AcknowledgeReceive(n) } } if ivError := account.CheckIV(iv); ivError != nil { return nil, drain.WithError(drainer, reader, newError("failed iv check").Base(ivError)) } return account.Cipher.NewDecryptionReader(account.Key, iv, reader) } func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) { user := request.User account := user.Account.(*MemoryAccount) var iv []byte if account.Cipher.IVSize() > 0 { iv = make([]byte, account.Cipher.IVSize()) common.Must2(rand.Read(iv)) if ivError := account.CheckIV(iv); ivError != nil { return nil, newError("failed to mark outgoing iv").Base(ivError) } if err := buf.WriteAllBytes(writer, iv); err != nil { return nil, newError("failed to write IV.").Base(err) } } return account.Cipher.NewEncryptionWriter(account.Key, iv, writer) } func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buffer, error) { user := request.User account := user.Account.(*MemoryAccount) ivLen := account.Cipher.IVSize() // Calculate required buffer size: IV + address length + payload + AEAD overhead (16) var addrPortLen int32 switch request.Address.Family() { case net.AddressFamilyDomain: if protocol.IsDomainTooLong(request.Address.Domain()) { return nil, newError("Super long domain is not supported: ", request.Address.Domain()) } addrPortLen = 1 + 1 + int32(len(request.Address.Domain())) + 2 case net.AddressFamilyIPv4: addrPortLen = 1 + 4 + 2 case net.AddressFamilyIPv6: addrPortLen = 1 + 16 + 2 default: panic("Unknown address type.") } neededSize := ivLen + addrPortLen + int32(len(payload)) + 16 var buffer *buf.Buffer if neededSize > buf.Size { buffer = buf.NewWithSize(neededSize) } else { buffer = buf.New() } if ivLen > 0 { common.Must2(buffer.ReadFullFrom(rand.Reader, ivLen)) } if err := addrParser.WriteAddressPort(buffer, request.Address, request.Port); err != nil { buffer.Release() return nil, newError("failed to write address").Base(err) } buffer.Write(payload) if err := account.Cipher.EncodePacket(account.Key, buffer); err != nil { buffer.Release() return nil, newError("failed to encrypt UDP payload").Base(err) } return buffer, nil } func DecodeUDPPacket(user *protocol.MemoryUser, payload *buf.Buffer) (*protocol.RequestHeader, *buf.Buffer, error) { account := user.Account.(*MemoryAccount) var iv []byte if !account.Cipher.IsAEAD() && account.Cipher.IVSize() > 0 { // Keep track of IV as it gets removed from payload in DecodePacket. iv = make([]byte, account.Cipher.IVSize()) copy(iv, payload.BytesTo(account.Cipher.IVSize())) } if err := account.Cipher.DecodePacket(account.Key, payload); err != nil { return nil, nil, newError("failed to decrypt UDP payload").Base(err) } request := &protocol.RequestHeader{ Version: Version, User: user, Command: protocol.RequestCommandUDP, } payload.SetByte(0, payload.Byte(0)&0x0F) addr, port, err := addrParser.ReadAddressPort(nil, payload) if err != nil { return nil, nil, newError("failed to parse address").Base(err) } request.Address = addr request.Port = port return request, payload, nil } type UDPReader struct { Reader io.Reader User *protocol.MemoryUser } func (v *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) { buffer := buf.New() _, err := buffer.ReadFrom(v.Reader) if err != nil { buffer.Release() return nil, err } _, payload, err := DecodeUDPPacket(v.User, buffer) if err != nil { buffer.Release() return nil, err } return buf.MultiBuffer{payload}, nil } func (v *UDPReader) ReadFrom(p []byte) (n int, addr gonet.Addr, err error) { buffer := buf.New() _, err = buffer.ReadFrom(v.Reader) if err != nil { buffer.Release() return 0, nil, err } vaddr, payload, err := DecodeUDPPacket(v.User, buffer) if err != nil { buffer.Release() return 0, nil, err } n = copy(p, payload.Bytes()) payload.Release() return n, &gonet.UDPAddr{IP: vaddr.Address.IP(), Port: int(vaddr.Port)}, nil } type UDPWriter struct { Writer io.Writer Request *protocol.RequestHeader } // Write implements io.Writer. func (w *UDPWriter) Write(payload []byte) (int, error) { packet, err := EncodeUDPPacket(w.Request, payload) if err != nil { return 0, err } _, err = w.Writer.Write(packet.Bytes()) packet.Release() return len(payload), err } func (w *UDPWriter) WriteTo(payload []byte, addr gonet.Addr) (n int, err error) { request := *w.Request udpAddr := addr.(*gonet.UDPAddr) request.Command = protocol.RequestCommandUDP request.Address = net.IPAddress(udpAddr.IP) request.Port = net.Port(udpAddr.Port) packet, err := EncodeUDPPacket(&request, payload) if err != nil { return 0, err } _, err = w.Writer.Write(packet.Bytes()) packet.Release() return len(payload), err } func remapToPrintable(input []byte) { const charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~\\\"" seed := mrand.New(mrand.NewSource(int64(crc32.ChecksumIEEE(input)))) for i := range input { input[i] = charSet[seed.Intn(len(charSet))] } } ================================================ FILE: proxy/shadowsocks/protocol_test.go ================================================ package shadowsocks_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" . "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" ) func toAccount(a *Account) protocol.Account { account, err := a.AsAccount() common.Must(err) return account } func equalRequestHeader(x, y *protocol.RequestHeader) bool { return cmp.Equal(x, y, cmp.Comparer(func(x, y protocol.RequestHeader) bool { return x == y })) } func TestUDPEncoding(t *testing.T) { request := &protocol.RequestHeader{ Version: Version, Command: protocol.RequestCommandUDP, Address: net.LocalHostIP, Port: 1234, User: &protocol.MemoryUser{ Email: "love@v2fly.org", Account: toAccount(&Account{ Password: "password", CipherType: CipherType_AES_128_GCM, }), }, } data := buf.New() common.Must2(data.WriteString("test string")) encodedData, err := EncodeUDPPacket(request, data.Bytes()) common.Must(err) decodedRequest, decodedData, err := DecodeUDPPacket(request.User, encodedData) common.Must(err) if r := cmp.Diff(decodedData.Bytes(), data.Bytes()); r != "" { t.Error("data: ", r) } if equalRequestHeader(decodedRequest, request) == false { t.Error("different request") } } func TestTCPRequest(t *testing.T) { cases := []struct { request *protocol.RequestHeader payload []byte }{ { request: &protocol.RequestHeader{ Version: Version, Command: protocol.RequestCommandTCP, Address: net.LocalHostIP, Port: 1234, User: &protocol.MemoryUser{ Email: "love@v2fly.org", Account: toAccount(&Account{ Password: "tcp-password", CipherType: CipherType_AES_128_GCM, }), }, }, payload: []byte("test string"), }, { request: &protocol.RequestHeader{ Version: Version, Command: protocol.RequestCommandTCP, Address: net.LocalHostIPv6, Port: 1234, User: &protocol.MemoryUser{ Email: "love@v2fly.org", Account: toAccount(&Account{ Password: "password", CipherType: CipherType_AES_256_GCM, }), }, }, payload: []byte("test string"), }, { request: &protocol.RequestHeader{ Version: Version, Command: protocol.RequestCommandTCP, Address: net.DomainAddress("v2fly.org"), Port: 1234, User: &protocol.MemoryUser{ Email: "love@v2fly.org", Account: toAccount(&Account{ Password: "password", CipherType: CipherType_CHACHA20_POLY1305, }), }, }, payload: []byte("test string"), }, } runTest := func(request *protocol.RequestHeader, payload []byte) { data := buf.New() common.Must2(data.Write(payload)) cache := buf.New() defer cache.Release() writer, err := WriteTCPRequest(request, cache) common.Must(err) common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data})) decodedRequest, reader, err := ReadTCPSession(request.User, cache) common.Must(err) if equalRequestHeader(decodedRequest, request) == false { t.Error("different request") } decodedData, err := reader.ReadMultiBuffer() common.Must(err) if r := cmp.Diff(decodedData[0].Bytes(), payload); r != "" { t.Error("data: ", r) } } for _, test := range cases { runTest(test.request, test.payload) } } func TestUDPReaderWriter(t *testing.T) { user := &protocol.MemoryUser{ Account: toAccount(&Account{ Password: "test-password", CipherType: CipherType_CHACHA20_POLY1305, }), } cache := buf.New() defer cache.Release() writer := &buf.SequentialWriter{Writer: &UDPWriter{ Writer: cache, Request: &protocol.RequestHeader{ Version: Version, Address: net.DomainAddress("v2fly.org"), Port: 123, User: user, }, }} reader := &UDPReader{ Reader: cache, User: user, } { b := buf.New() common.Must2(b.WriteString("test payload")) common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{b})) payload, err := reader.ReadMultiBuffer() common.Must(err) if payload[0].String() != "test payload" { t.Error("unexpected output: ", payload[0].String()) } } { b := buf.New() common.Must2(b.WriteString("test payload 2")) common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{b})) payload, err := reader.ReadMultiBuffer() common.Must(err) if payload[0].String() != "test payload 2" { t.Error("unexpected output: ", payload[0].String()) } } } ================================================ FILE: proxy/shadowsocks/server.go ================================================ package shadowsocks import ( "context" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" udp_proto "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) type Server struct { config *ServerConfig user *protocol.MemoryUser policyManager policy.Manager } // NewServer create a new Shadowsocks server. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { if config.GetUser() == nil { return nil, newError("user is not specified") } mUser, err := config.User.ToMemoryUser() if err != nil { return nil, newError("failed to parse user account").Base(err) } v := core.MustFromContext(ctx) s := &Server{ config: config, user: mUser, policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return s, nil } func (s *Server) Network() []net.Network { list := s.config.Network if len(list) == 0 { list = append(list, net.Network_TCP) } if s.config.UdpEnabled { list = append(list, net.Network_UDP) } return list } func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error { switch network { case net.Network_TCP: return s.handleConnection(ctx, conn, dispatcher) case net.Network_UDP: return s.handlerUDPPayload(ctx, conn, dispatcher) default: return newError("unknown network: ", network) } } func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error { udpDispatcherConstructor := udp.NewSplitDispatcher switch s.config.PacketEncoding { case packetaddr.PacketAddrType_None: break case packetaddr.PacketAddrType_Packet: packetAddrDispatcherFactory := udp.NewPacketAddrDispatcherCreator(ctx) udpDispatcherConstructor = packetAddrDispatcherFactory.NewPacketAddrDispatcher } udpServer := udpDispatcherConstructor(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) { request := protocol.RequestHeaderFromContext(ctx) if request == nil { request = &protocol.RequestHeader{ Port: packet.Source.Port, Address: packet.Source.Address, User: s.user, } } payload := packet.Payload data, err := EncodeUDPPacket(request, payload.Bytes()) payload.Release() if err != nil { newError("failed to encode UDP packet").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) return } defer data.Release() conn.Write(data.Bytes()) }) inbound := session.InboundFromContext(ctx) if inbound == nil { panic("no inbound metadata") } inbound.User = s.user reader := buf.NewPacketReader(conn) for { mpayload, err := reader.ReadMultiBuffer() if err != nil { break } for _, payload := range mpayload { request, data, err := DecodeUDPPacket(s.user, payload) if err != nil { if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.IsValid() { newError("dropping invalid UDP packet from: ", inbound.Source).Base(err).WriteToLog(session.ExportIDToError(ctx)) log.Record(&log.AccessMessage{ From: inbound.Source, To: "", Status: log.AccessRejected, Reason: err, }) } payload.Release() continue } currentPacketCtx := ctx dest := request.Destination() if inbound.Source.IsValid() { currentPacketCtx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: inbound.Source, To: dest, Status: log.AccessAccepted, Reason: "", Email: request.User.Email, }) } newError("tunnelling request to ", dest).WriteToLog(session.ExportIDToError(currentPacketCtx)) currentPacketCtx = protocol.ContextWithRequestHeader(currentPacketCtx, request) udpServer.Dispatch(currentPacketCtx, dest, data) } } return nil } func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error { sessionPolicy := s.policyManager.ForLevel(s.user.Level) conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)) bufferedReader := buf.BufferedReader{Reader: buf.NewReader(conn)} request, bodyReader, err := ReadTCPSession(s.user, &bufferedReader) if err != nil { log.Record(&log.AccessMessage{ From: conn.RemoteAddr(), To: "", Status: log.AccessRejected, Reason: err, }) return newError("failed to create request from: ", conn.RemoteAddr()).Base(err) } conn.SetReadDeadline(time.Time{}) inbound := session.InboundFromContext(ctx) if inbound == nil { panic("no inbound metadata") } inbound.User = s.user dest := request.Destination() ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: conn.RemoteAddr(), To: dest, Status: log.AccessAccepted, Reason: "", Email: request.User.Email, }) newError("tunnelling request to ", dest).WriteToLog(session.ExportIDToError(ctx)) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) link, err := dispatcher.Dispatch(ctx, dest) if err != nil { return err } responseDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) responseWriter, err := WriteTCPResponse(request, bufferedWriter) if err != nil { return newError("failed to write response").Base(err) } { payload, err := link.Reader.ReadMultiBuffer() if err != nil { return err } if err := responseWriter.WriteMultiBuffer(payload); err != nil { return err } } if err := bufferedWriter.SetBuffered(false); err != nil { return err } if err := buf.Copy(link.Reader, responseWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP response").Base(err) } return nil } requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if err := buf.Copy(bodyReader, link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP request").Base(err) } return nil } requestDoneAndCloseWriter := task.OnSuccess(requestDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDoneAndCloseWriter, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) return newError("connection ends").Base(err) } return nil } func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) })) } ================================================ FILE: proxy/shadowsocks/shadowsocks.go ================================================ // Package shadowsocks provides compatible functionality to Shadowsocks. // // Shadowsocks client and server are implemented as outbound and inbound respectively in V2Ray's term. // // R.I.P Shadowsocks package shadowsocks //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: proxy/shadowsocks/simplified/config.go ================================================ package simplified import ( "context" "encoding/json" "github.com/golang/protobuf/jsonpb" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" ) func (c *CipherTypeWrapper) UnmarshalJSONPB(unmarshaler *jsonpb.Unmarshaler, bytes []byte) error { var method string if err := json.Unmarshal(bytes, &method); err != nil { return err } if c.Value = shadowsocks.CipherFromString(method); c.Value == shadowsocks.CipherType_UNKNOWN { return newError("unknown cipher method: ", method) } return nil } func (c *CipherTypeWrapper) MarshalJSONPB(marshaler *jsonpb.Marshaler) ([]byte, error) { method := c.Value.String() return json.Marshal(method) } func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*ServerConfig) fullServer := &shadowsocks.ServerConfig{ User: &protocol.User{ Account: serial.ToTypedMessage(&shadowsocks.Account{ Password: simplifiedServer.Password, CipherType: simplifiedServer.Method.Value, }), }, Network: simplifiedServer.Networks.GetNetwork(), PacketEncoding: simplifiedServer.PacketEncoding, } return common.CreateObject(ctx, fullServer) })) common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedClient := config.(*ClientConfig) fullClient := &shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: simplifiedClient.Address, Port: simplifiedClient.Port, User: []*protocol.User{ { Account: serial.ToTypedMessage(&shadowsocks.Account{ Password: simplifiedClient.Password, CipherType: simplifiedClient.Method.Value, ExperimentReducedIvHeadEntropy: simplifiedClient.ExperimentReducedIvHeadEntropy, }), }, }, }, }, } return common.CreateObject(ctx, fullClient) })) } ================================================ FILE: proxy/shadowsocks/simplified/config.pb.go ================================================ package simplified import ( net "github.com/v2fly/v2ray-core/v5/common/net" packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" _ "github.com/v2fly/v2ray-core/v5/common/protoext" shadowsocks "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Method *CipherTypeWrapper `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` Networks *net.NetworkList `protobuf:"bytes,3,opt,name=networks,proto3" json:"networks,omitempty"` PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,4,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_shadowsocks_simplified_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_simplified_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks_simplified_config_proto_rawDescGZIP(), []int{0} } func (x *ServerConfig) GetMethod() *CipherTypeWrapper { if x != nil { return x.Method } return nil } func (x *ServerConfig) GetPassword() string { if x != nil { return x.Password } return "" } func (x *ServerConfig) GetNetworks() *net.NetworkList { if x != nil { return x.Networks } return nil } func (x *ServerConfig) GetPacketEncoding() packetaddr.PacketAddrType { if x != nil { return x.PacketEncoding } return packetaddr.PacketAddrType(0) } type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Method *CipherTypeWrapper `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` Password string `protobuf:"bytes,4,opt,name=password,proto3" json:"password,omitempty"` ExperimentReducedIvHeadEntropy bool `protobuf:"varint,90001,opt,name=experiment_reduced_iv_head_entropy,json=experimentReducedIvHeadEntropy,proto3" json:"experiment_reduced_iv_head_entropy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_shadowsocks_simplified_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_simplified_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks_simplified_config_proto_rawDescGZIP(), []int{1} } func (x *ClientConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *ClientConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *ClientConfig) GetMethod() *CipherTypeWrapper { if x != nil { return x.Method } return nil } func (x *ClientConfig) GetPassword() string { if x != nil { return x.Password } return "" } func (x *ClientConfig) GetExperimentReducedIvHeadEntropy() bool { if x != nil { return x.ExperimentReducedIvHeadEntropy } return false } type CipherTypeWrapper struct { state protoimpl.MessageState `protogen:"open.v1"` Value shadowsocks.CipherType `protobuf:"varint,1,opt,name=value,proto3,enum=v2ray.core.proxy.shadowsocks.CipherType" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CipherTypeWrapper) Reset() { *x = CipherTypeWrapper{} mi := &file_proxy_shadowsocks_simplified_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CipherTypeWrapper) String() string { return protoimpl.X.MessageStringOf(x) } func (*CipherTypeWrapper) ProtoMessage() {} func (x *CipherTypeWrapper) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks_simplified_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CipherTypeWrapper.ProtoReflect.Descriptor instead. func (*CipherTypeWrapper) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks_simplified_config_proto_rawDescGZIP(), []int{2} } func (x *CipherTypeWrapper) GetValue() shadowsocks.CipherType { if x != nil { return x.Value } return shadowsocks.CipherType(0) } var File_proxy_shadowsocks_simplified_config_proto protoreflect.FileDescriptor const file_proxy_shadowsocks_simplified_config_proto_rawDesc = "" + "\n" + ")proxy/shadowsocks/simplified/config.proto\x12'v2ray.core.proxy.shadowsocks.simplified\x1a common/protoext/extensions.proto\x1a\x18common/net/address.proto\x1a\x18common/net/network.proto\x1a\"common/net/packetaddr/config.proto\x1a\x1eproxy/shadowsocks/config.proto\"\xae\x02\n" + "\fServerConfig\x12R\n" + "\x06method\x18\x01 \x01(\v2:.v2ray.core.proxy.shadowsocks.simplified.CipherTypeWrapperR\x06method\x12\x1a\n" + "\bpassword\x18\x02 \x01(\tR\bpassword\x12>\n" + "\bnetworks\x18\x03 \x01(\v2\".v2ray.core.common.net.NetworkListR\bnetworks\x12R\n" + "\x0fpacket_encoding\x18\x04 \x01(\x0e2).v2ray.core.net.packetaddr.PacketAddrTypeR\x0epacketEncoding:\x1a\x82\xb5\x18\x16\n" + "\ainbound\x12\vshadowsocks\"\xbe\x02\n" + "\fClientConfig\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x12R\n" + "\x06method\x18\x03 \x01(\v2:.v2ray.core.proxy.shadowsocks.simplified.CipherTypeWrapperR\x06method\x12\x1a\n" + "\bpassword\x18\x04 \x01(\tR\bpassword\x12L\n" + "\"experiment_reduced_iv_head_entropy\x18\x91\xbf\x05 \x01(\bR\x1eexperimentReducedIvHeadEntropy:\x1f\x82\xb5\x18\x1b\n" + "\boutbound\x12\vshadowsocks\x90\xff)\x01\"S\n" + "\x11CipherTypeWrapper\x12>\n" + "\x05value\x18\x01 \x01(\x0e2(.v2ray.core.proxy.shadowsocks.CipherTypeR\x05valueB\x96\x01\n" + "+com.v2ray.core.proxy.shadowsocks.simplifiedP\x01Z;github.com/v2fly/v2ray-core/v5/proxy/shadowsocks/simplified\xaa\x02'V2Ray.Core.Proxy.Shadowsocks.Simplifiedb\x06proto3" var ( file_proxy_shadowsocks_simplified_config_proto_rawDescOnce sync.Once file_proxy_shadowsocks_simplified_config_proto_rawDescData []byte ) func file_proxy_shadowsocks_simplified_config_proto_rawDescGZIP() []byte { file_proxy_shadowsocks_simplified_config_proto_rawDescOnce.Do(func() { file_proxy_shadowsocks_simplified_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_shadowsocks_simplified_config_proto_rawDesc), len(file_proxy_shadowsocks_simplified_config_proto_rawDesc))) }) return file_proxy_shadowsocks_simplified_config_proto_rawDescData } var file_proxy_shadowsocks_simplified_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_proxy_shadowsocks_simplified_config_proto_goTypes = []any{ (*ServerConfig)(nil), // 0: v2ray.core.proxy.shadowsocks.simplified.ServerConfig (*ClientConfig)(nil), // 1: v2ray.core.proxy.shadowsocks.simplified.ClientConfig (*CipherTypeWrapper)(nil), // 2: v2ray.core.proxy.shadowsocks.simplified.CipherTypeWrapper (*net.NetworkList)(nil), // 3: v2ray.core.common.net.NetworkList (packetaddr.PacketAddrType)(0), // 4: v2ray.core.net.packetaddr.PacketAddrType (*net.IPOrDomain)(nil), // 5: v2ray.core.common.net.IPOrDomain (shadowsocks.CipherType)(0), // 6: v2ray.core.proxy.shadowsocks.CipherType } var file_proxy_shadowsocks_simplified_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.shadowsocks.simplified.ServerConfig.method:type_name -> v2ray.core.proxy.shadowsocks.simplified.CipherTypeWrapper 3, // 1: v2ray.core.proxy.shadowsocks.simplified.ServerConfig.networks:type_name -> v2ray.core.common.net.NetworkList 4, // 2: v2ray.core.proxy.shadowsocks.simplified.ServerConfig.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType 5, // 3: v2ray.core.proxy.shadowsocks.simplified.ClientConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 2, // 4: v2ray.core.proxy.shadowsocks.simplified.ClientConfig.method:type_name -> v2ray.core.proxy.shadowsocks.simplified.CipherTypeWrapper 6, // 5: v2ray.core.proxy.shadowsocks.simplified.CipherTypeWrapper.value:type_name -> v2ray.core.proxy.shadowsocks.CipherType 6, // [6:6] is the sub-list for method output_type 6, // [6:6] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name } func init() { file_proxy_shadowsocks_simplified_config_proto_init() } func file_proxy_shadowsocks_simplified_config_proto_init() { if File_proxy_shadowsocks_simplified_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_shadowsocks_simplified_config_proto_rawDesc), len(file_proxy_shadowsocks_simplified_config_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_shadowsocks_simplified_config_proto_goTypes, DependencyIndexes: file_proxy_shadowsocks_simplified_config_proto_depIdxs, MessageInfos: file_proxy_shadowsocks_simplified_config_proto_msgTypes, }.Build() File_proxy_shadowsocks_simplified_config_proto = out.File file_proxy_shadowsocks_simplified_config_proto_goTypes = nil file_proxy_shadowsocks_simplified_config_proto_depIdxs = nil } ================================================ FILE: proxy/shadowsocks/simplified/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.shadowsocks.simplified; option csharp_namespace = "V2Ray.Core.Proxy.Shadowsocks.Simplified"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks/simplified"; option java_package = "com.v2ray.core.proxy.shadowsocks.simplified"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "common/net/address.proto"; import "common/net/network.proto"; import "common/net/packetaddr/config.proto"; import "proxy/shadowsocks/config.proto"; message ServerConfig{ option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "shadowsocks"; CipherTypeWrapper method = 1; string password = 2; v2ray.core.common.net.NetworkList networks = 3; v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 4; } message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "shadowsocks"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; CipherTypeWrapper method = 3; string password = 4; bool experiment_reduced_iv_head_entropy = 90001; } message CipherTypeWrapper { v2ray.core.proxy.shadowsocks.CipherType value = 1; } ================================================ FILE: proxy/shadowsocks/simplified/errors.generated.go ================================================ package simplified import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/shadowsocks2022/client.go ================================================ package shadowsocks2022 import ( "context" gonet "net" "sync" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) type Client struct { config *ClientConfig ctx context.Context } const UDPConnectionState = "UDPConnectionState" type ClientUDPConnState struct { session *ClientUDPSession initOnce *sync.Once } func (c *ClientUDPConnState) GetOrCreateSession(create func() (*ClientUDPSession, error)) (*ClientUDPSession, error) { var errOuter error c.initOnce.Do(func() { sessionState, err := create() if err != nil { errOuter = newError("failed to create UDP session").Base(err) return } c.session = sessionState }) if errOuter != nil { return nil, newError("failed to initialize UDP State").Base(errOuter) } return c.session, nil } func NewClientUDPConnState() (*ClientUDPConnState, error) { return &ClientUDPConnState{initOnce: &sync.Once{}}, nil } func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified") } destination := outbound.Target network := destination.Network keyDerivation := newBLAKE3KeyDerivation() var method Method switch c.config.Method { case "2022-blake3-aes-128-gcm": method = newAES128GCMMethod() case "2022-blake3-aes-256-gcm": method = newAES256GCMMethod() default: return newError("unknown method: ", c.config.Method) } effectivePsk := c.config.Psk ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, time.Minute) if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil { udpSession, err := c.getUDPSession(c.ctx, network, dialer, method, keyDerivation) if err != nil { return newError("failed to get UDP udpSession").Base(err) } requestDone := func() error { return udp.CopyPacketConn(udpSession, packetConn, udp.UpdateActivity(timer)) } responseDone := func() error { return udp.CopyPacketConn(packetConn, udpSession, udp.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } if network == net.Network_TCP { var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { dest := net.TCPDestination(c.config.Address.AsAddress(), net.Port(c.config.Port)) dest.Network = network rawConn, err := dialer.Dial(ctx, dest) if err != nil { return err } conn = rawConn return nil }) if err != nil { return newError("failed to find an available destination").AtWarning().Base(err) } newError("tunneling request to ", destination, " via ", network, ":", net.TCPDestination(c.config.Address.AsAddress(), net.Port(c.config.Port)).NetAddr()).WriteToLog(session.ExportIDToError(ctx)) defer conn.Close() request := &TCPRequest{ keyDerivation: keyDerivation, method: method, } TCPRequestBuffer := buf.New() defer TCPRequestBuffer.Release() err = request.EncodeTCPRequestHeader(effectivePsk, c.config.Ipsk, destination.Address, int(destination.Port), nil, TCPRequestBuffer) if err != nil { return newError("failed to encode TCP request header").Base(err) } _, err = conn.Write(TCPRequestBuffer.Bytes()) if err != nil { return newError("failed to write TCP request header").Base(err) } requestDone := func() error { encodedWriter := request.CreateClientC2SWriter(conn) return buf.Copy(link.Reader, encodedWriter, buf.UpdateActivity(timer)) } responseDone := func() error { err = request.DecodeTCPResponseHeader(effectivePsk, conn) if err != nil { return newError("failed to decode TCP response header").Base(err) } if err = request.CheckC2SConnectionConstraint(); err != nil { return newError("C2S connection constraint violation").Base(err) } initialPayload := buf.NewWithSize(65535) encodedReader, err := request.CreateClientS2CReader(conn, initialPayload) if err != nil { return newError("failed to create client S2C reader").Base(err) } err = link.Writer.WriteMultiBuffer(buf.MultiBuffer{initialPayload}) if err != nil { return newError("failed to write initial payload").Base(err) } return buf.Copy(encodedReader, link.Writer, buf.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } else { udpSession, err := c.getUDPSession(c.ctx, network, dialer, method, keyDerivation) if err != nil { return newError("failed to get UDP udpSession").Base(err) } monoDestUDPConn := udp.NewMonoDestUDPConn(udpSession, &gonet.UDPAddr{IP: destination.Address.IP(), Port: int(destination.Port)}) requestDone := func() error { return buf.Copy(link.Reader, monoDestUDPConn, buf.UpdateActivity(timer)) } responseDone := func() error { return buf.Copy(monoDestUDPConn, link.Writer, buf.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } } func (c *Client) getUDPSession(ctx context.Context, network net.Network, dialer internet.Dialer, method Method, keyDerivation *BLAKE3KeyDerivation) (internet.AbstractPacketConn, error) { storage := envctx.EnvironmentFromContext(ctx).(environment.ProxyEnvironment).TransientStorage() clientUDPStateIfce, err := storage.Get(ctx, UDPConnectionState) if err != nil { return nil, newError("failed to get UDP connection state").Base(err) } clientUDPState, ok := clientUDPStateIfce.(*ClientUDPConnState) if !ok { return nil, newError("failed to cast UDP connection state") } sessionState, err := clientUDPState.GetOrCreateSession(func() (*ClientUDPSession, error) { var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { dest := net.TCPDestination(c.config.Address.AsAddress(), net.Port(c.config.Port)) dest.Network = network rawConn, err := dialer.Dial(ctx, dest) if err != nil { return err } conn = rawConn return nil }) if err != nil { return nil, newError("failed to find an available destination").AtWarning().Base(err) } newError("creating udp session to ", network, ":", c.config.Address).WriteToLog(session.ExportIDToError(ctx)) packetProcessor, err := method.GetUDPClientProcessor(c.config.Ipsk, c.config.Psk, keyDerivation) if err != nil { return nil, newError("failed to create UDP client packet processor").Base(err) } return NewClientUDPSession(ctx, conn, packetProcessor), nil }) if err != nil { return nil, newError("failed to create UDP session").Base(err) } sessionConn, err := sessionState.NewSessionConn() if err != nil { return nil, newError("failed to create UDP session connection").Base(err) } return sessionConn, nil } func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { storage := envctx.EnvironmentFromContext(ctx).(environment.ProxyEnvironment).TransientStorage() udpState, err := NewClientUDPConnState() if err != nil { return nil, newError("failed to create UDP connection state").Base(err) } storage.Put(ctx, UDPConnectionState, udpState) return &Client{ config: config, ctx: ctx, }, nil } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { clientConfig, ok := config.(*ClientConfig) if !ok { return nil, newError("not a ClientConfig") } return NewClient(ctx, clientConfig) })) } ================================================ FILE: proxy/shadowsocks2022/client_session.go ================================================ package shadowsocks2022 import ( "context" "crypto/rand" "io" gonet "net" "sync" "time" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/pion/transport/v2/replaydetector" ) func NewClientUDPSession(ctx context.Context, conn io.ReadWriteCloser, packetProcessor UDPClientPacketProcessor) *ClientUDPSession { session := &ClientUDPSession{ locker: &sync.RWMutex{}, conn: conn, packetProcessor: packetProcessor, sessionMap: make(map[string]*ClientUDPSessionConn), sessionMapAlias: make(map[string]string), } session.ctx, session.finish = context.WithCancel(ctx) go session.KeepReading() return session } type ClientUDPSession struct { locker *sync.RWMutex conn io.ReadWriteCloser packetProcessor UDPClientPacketProcessor sessionMap map[string]*ClientUDPSessionConn sessionMapAlias map[string]string ctx context.Context finish func() } func (c *ClientUDPSession) GetCachedState(sessionID string) UDPClientPacketProcessorCachedState { c.locker.RLock() defer c.locker.RUnlock() state, ok := c.sessionMap[sessionID] if !ok { return nil } return state.cachedProcessorState } func (c *ClientUDPSession) GetCachedServerState(serverSessionID string) UDPClientPacketProcessorCachedState { c.locker.RLock() defer c.locker.RUnlock() clientSessionID := c.getCachedStateAlias(serverSessionID) if clientSessionID == "" { return nil } state, ok := c.sessionMap[clientSessionID] if !ok { return nil } if serverState, ok := state.trackedServerSessionID[serverSessionID]; !ok { return nil } else { return serverState.cachedRecvProcessorState } } func (c *ClientUDPSession) getCachedStateAlias(serverSessionID string) string { state, ok := c.sessionMapAlias[serverSessionID] if !ok { return "" } return state } func (c *ClientUDPSession) PutCachedState(sessionID string, cache UDPClientPacketProcessorCachedState) { c.locker.RLock() defer c.locker.RUnlock() state, ok := c.sessionMap[sessionID] if !ok { return } state.cachedProcessorState = cache } func (c *ClientUDPSession) PutCachedServerState(serverSessionID string, cache UDPClientPacketProcessorCachedState) { c.locker.RLock() defer c.locker.RUnlock() clientSessionID := c.getCachedStateAlias(serverSessionID) if clientSessionID == "" { return } state, ok := c.sessionMap[clientSessionID] if !ok { return } if serverState, ok := state.trackedServerSessionID[serverSessionID]; ok { serverState.cachedRecvProcessorState = cache return } } func (c *ClientUDPSession) Close() error { c.finish() return c.conn.Close() } func (c *ClientUDPSession) WriteUDPRequest(request *UDPRequest) error { buffer := buf.New() defer buffer.Release() err := c.packetProcessor.EncodeUDPRequest(request, buffer, c) if request.Payload != nil { request.Payload.Release() } if err != nil { return newError("unable to encode udp request").Base(err) } _, err = c.conn.Write(buffer.Bytes()) if err != nil { return newError("unable to write to conn").Base(err) } return nil } func (c *ClientUDPSession) KeepReading() { for c.ctx.Err() == nil { udpResp := &UDPResponse{} buffer := make([]byte, 1600) n, err := c.conn.Read(buffer) if err != nil { newError("unable to read from conn").Base(err).WriteToLog() return } if n != 0 { err := c.packetProcessor.DecodeUDPResp(buffer[:n], udpResp, c) if err != nil { newError("unable to decode udp response").Base(err).WriteToLog() continue } { timeDifference := int64(udpResp.TimeStamp) - time.Now().Unix() if timeDifference < -30 || timeDifference > 30 { newError("udp packet timestamp difference too large, packet discarded, time diff = ", timeDifference).WriteToLog() continue } } c.locker.RLock() session, ok := c.sessionMap[string(udpResp.ClientSessionID[:])] c.locker.RUnlock() if ok { select { case session.readChan <- udpResp: default: } } else { newError("misbehaving server: unknown client session ID").Base(err).WriteToLog() } } } } func (c *ClientUDPSession) NewSessionConn() (internet.AbstractPacketConn, error) { sessionID := make([]byte, 8) _, err := rand.Read(sessionID) if err != nil { return nil, newError("unable to generate session id").Base(err) } connctx, connfinish := context.WithCancel(c.ctx) sessionConn := &ClientUDPSessionConn{ sessionID: string(sessionID), readChan: make(chan *UDPResponse, 128), parent: c, ctx: connctx, finish: connfinish, nextWritePacketID: 0, trackedServerSessionID: make(map[string]*ClientUDPSessionServerTracker), } c.locker.Lock() c.sessionMap[sessionConn.sessionID] = sessionConn c.locker.Unlock() return sessionConn, nil } type ClientUDPSessionServerTracker struct { cachedRecvProcessorState UDPClientPacketProcessorCachedState rxReplayDetector replaydetector.ReplayDetector lastSeen time.Time } type ClientUDPSessionConn struct { sessionID string readChan chan *UDPResponse parent *ClientUDPSession nextWritePacketID uint64 trackedServerSessionID map[string]*ClientUDPSessionServerTracker cachedProcessorState UDPClientPacketProcessorCachedState ctx context.Context finish func() } func (c *ClientUDPSessionConn) Close() error { c.parent.locker.Lock() delete(c.parent.sessionMap, c.sessionID) for k := range c.trackedServerSessionID { delete(c.parent.sessionMapAlias, k) } c.parent.locker.Unlock() c.finish() return nil } func (c *ClientUDPSessionConn) WriteTo(p []byte, addr gonet.Addr) (n int, err error) { thisPacketID := c.nextWritePacketID c.nextWritePacketID += 1 req := &UDPRequest{ SessionID: [8]byte{}, PacketID: thisPacketID, TimeStamp: uint64(time.Now().Unix()), Address: net.IPAddress(addr.(*gonet.UDPAddr).IP), Port: addr.(*net.UDPAddr).Port, Payload: nil, } copy(req.SessionID[:], c.sessionID) req.Payload = buf.New() req.Payload.Write(p) err = c.parent.WriteUDPRequest(req) if err != nil { return 0, newError("unable to write to parent session").Base(err) } return len(p), nil } func (c *ClientUDPSessionConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { for { select { case <-c.ctx.Done(): return 0, nil, io.EOF case resp := <-c.readChan: n = copy(p, resp.Payload.Bytes()) resp.Payload.Release() var trackedState *ClientUDPSessionServerTracker if trackedStateReceived, ok := c.trackedServerSessionID[string(resp.SessionID[:])]; !ok { for key, value := range c.trackedServerSessionID { if time.Since(value.lastSeen) > 65*time.Second { delete(c.trackedServerSessionID, key) } } state := &ClientUDPSessionServerTracker{ rxReplayDetector: replaydetector.New(1024, ^uint64(0)), } c.trackedServerSessionID[string(resp.SessionID[:])] = state c.parent.locker.Lock() c.parent.sessionMapAlias[string(resp.SessionID[:])] = string(resp.ClientSessionID[:]) c.parent.locker.Unlock() trackedState = state } else { trackedState = trackedStateReceived } if accept, ok := trackedState.rxReplayDetector.Check(resp.PacketID); ok { accept() } else { newError("misbehaving server: replayed packet").Base(err).WriteToLog() continue } trackedState.lastSeen = time.Now() addr = &net.UDPAddr{IP: resp.Address.IP(), Port: resp.Port} } return n, addr, nil } } ================================================ FILE: proxy/shadowsocks2022/config.pb.go ================================================ package shadowsocks2022 import ( net "github.com/v2fly/v2ray-core/v5/common/net" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` Psk []byte `protobuf:"bytes,2,opt,name=psk,proto3" json:"psk,omitempty"` Ipsk [][]byte `protobuf:"bytes,4,rep,name=ipsk,proto3" json:"ipsk,omitempty"` Address *net.IPOrDomain `protobuf:"bytes,5,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,6,opt,name=port,proto3" json:"port,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_shadowsocks2022_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_shadowsocks2022_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_shadowsocks2022_config_proto_rawDescGZIP(), []int{0} } func (x *ClientConfig) GetMethod() string { if x != nil { return x.Method } return "" } func (x *ClientConfig) GetPsk() []byte { if x != nil { return x.Psk } return nil } func (x *ClientConfig) GetIpsk() [][]byte { if x != nil { return x.Ipsk } return nil } func (x *ClientConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *ClientConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } var File_proxy_shadowsocks2022_config_proto protoreflect.FileDescriptor const file_proxy_shadowsocks2022_config_proto_rawDesc = "" + "\n" + "\"proxy/shadowsocks2022/config.proto\x12 v2ray.core.proxy.shadowsocks2022\x1a\x18common/net/address.proto\x1a common/protoext/extensions.proto\"\xc2\x01\n" + "\fClientConfig\x12\x16\n" + "\x06method\x18\x01 \x01(\tR\x06method\x12\x10\n" + "\x03psk\x18\x02 \x01(\fR\x03psk\x12\x12\n" + "\x04ipsk\x18\x04 \x03(\fR\x04ipsk\x12;\n" + "\aaddress\x18\x05 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x06 \x01(\rR\x04port:#\x82\xb5\x18\x1f\n" + "\boutbound\x12\x0fshadowsocks2022\x90\xff)\x01B\x81\x01\n" + "$com.v2ray.core.proxy.shadowsocks2022P\x01Z4github.com/v2fly/v2ray-core/v5/proxy/shadowsocks2022\xaa\x02 V2Ray.Core.Proxy.Shadowsocks2022b\x06proto3" var ( file_proxy_shadowsocks2022_config_proto_rawDescOnce sync.Once file_proxy_shadowsocks2022_config_proto_rawDescData []byte ) func file_proxy_shadowsocks2022_config_proto_rawDescGZIP() []byte { file_proxy_shadowsocks2022_config_proto_rawDescOnce.Do(func() { file_proxy_shadowsocks2022_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_shadowsocks2022_config_proto_rawDesc), len(file_proxy_shadowsocks2022_config_proto_rawDesc))) }) return file_proxy_shadowsocks2022_config_proto_rawDescData } var file_proxy_shadowsocks2022_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_shadowsocks2022_config_proto_goTypes = []any{ (*ClientConfig)(nil), // 0: v2ray.core.proxy.shadowsocks2022.ClientConfig (*net.IPOrDomain)(nil), // 1: v2ray.core.common.net.IPOrDomain } var file_proxy_shadowsocks2022_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.proxy.shadowsocks2022.ClientConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_proxy_shadowsocks2022_config_proto_init() } func file_proxy_shadowsocks2022_config_proto_init() { if File_proxy_shadowsocks2022_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_shadowsocks2022_config_proto_rawDesc), len(file_proxy_shadowsocks2022_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_shadowsocks2022_config_proto_goTypes, DependencyIndexes: file_proxy_shadowsocks2022_config_proto_depIdxs, MessageInfos: file_proxy_shadowsocks2022_config_proto_msgTypes, }.Build() File_proxy_shadowsocks2022_config_proto = out.File file_proxy_shadowsocks2022_config_proto_goTypes = nil file_proxy_shadowsocks2022_config_proto_depIdxs = nil } ================================================ FILE: proxy/shadowsocks2022/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.shadowsocks2022; option csharp_namespace = "V2Ray.Core.Proxy.Shadowsocks2022"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks2022"; option java_package = "com.v2ray.core.proxy.shadowsocks2022"; option java_multiple_files = true; import "common/net/address.proto"; import "common/protoext/extensions.proto"; message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "shadowsocks2022"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; string method = 1; bytes psk = 2; repeated bytes ipsk = 4; v2ray.core.common.net.IPOrDomain address = 5; uint32 port = 6; } ================================================ FILE: proxy/shadowsocks2022/eih_aes.go ================================================ package shadowsocks2022 import ( "crypto/subtle" "io" "github.com/v2fly/struc" "github.com/v2fly/v2ray-core/v5/common/buf" "lukechampine.com/blake3" ) func newAESEIH(size int) *aesEIH { return &aesEIH{length: size} } func newAESEIHWithData(size int, eih [][aesEIHSize]byte) *aesEIH { return &aesEIH{length: size, eih: eih} } const aesEIHSize = 16 type aesEIH struct { eih [][aesEIHSize]byte length int } func (a *aesEIH) Pack(p []byte, opt *struc.Options) (int, error) { var totalCopy int for i := 0; i < a.length; i++ { n := copy(p[aesEIHSize*i:aesEIHSize*(i+1)], a.eih[i][:]) if n != 16 { return 0, newError("failed to pack aesEIH") } totalCopy += n } return totalCopy, nil } func (a *aesEIH) Unpack(r io.Reader, length int, opt *struc.Options) error { a.eih = make([][aesEIHSize]byte, a.length) for i := 0; i < a.length; i++ { n, err := r.Read(a.eih[i][:]) if err != nil { return newError("failed to unpack aesEIH").Base(err) } if n != aesEIHSize { return newError("failed to unpack aesEIH") } } return nil } func (a *aesEIH) Size(opt *struc.Options) int { return a.length * aesEIHSize } func (a *aesEIH) String() string { return "" } const aesEIHPskHashSize = 16 type aesEIHGenerator struct { ipsk [][]byte ipskHash [][aesEIHPskHashSize]byte psk []byte pskHash [aesEIHPskHashSize]byte length int } func newAESEIHGeneratorContainer(size int, effectivePsk []byte, ipsk [][]byte) *aesEIHGenerator { var ipskHash [][aesEIHPskHashSize]byte for _, v := range ipsk { hash := blake3.Sum512(v) ipskHash = append(ipskHash, [aesEIHPskHashSize]byte(hash[:16])) } pskHashFull := blake3.Sum512(effectivePsk) pskHash := [aesEIHPskHashSize]byte(pskHashFull[:16]) return &aesEIHGenerator{length: size, ipsk: ipsk, ipskHash: ipskHash, psk: effectivePsk, pskHash: pskHash} } func (a *aesEIHGenerator) GenerateEIH(derivation KeyDerivation, method Method, salt []byte) (ExtensibleIdentityHeaders, error) { return a.generateEIHWithMask(derivation, method, salt, nil) } func (a *aesEIHGenerator) GenerateEIHUDP(derivation KeyDerivation, method Method, mask []byte) (ExtensibleIdentityHeaders, error) { return a.generateEIHWithMask(derivation, method, nil, mask) } func (a *aesEIHGenerator) generateEIHWithMask(derivation KeyDerivation, method Method, salt, mask []byte) (ExtensibleIdentityHeaders, error) { eih := make([][16]byte, a.length) current := a.length - 1 currentPskHash := a.pskHash for { identityKeyBuf := buf.New() identityKey := identityKeyBuf.Extend(int32(method.GetSessionSubKeyAndSaltLength())) if mask == nil { err := derivation.GetIdentitySubKey(a.ipsk[current], salt, identityKey) if err != nil { return nil, newError("failed to get identity sub key").Base(err) } } else { copy(identityKey, a.ipsk[current]) } eih[current] = [16]byte{} if mask != nil { subtle.XORBytes(currentPskHash[:], mask, currentPskHash[:]) } err := method.GenerateEIH(identityKey, currentPskHash[:], eih[current][:]) if err != nil { return nil, newError("failed to generate EIH").Base(err) } current-- if current < 0 { break } currentPskHash = a.ipskHash[current] identityKeyBuf.Release() } return newAESEIHWithData(a.length, eih), nil } ================================================ FILE: proxy/shadowsocks2022/encoding.go ================================================ package shadowsocks2022 import ( "bytes" "crypto/cipher" cryptoRand "crypto/rand" "encoding/binary" "io" "time" "github.com/v2fly/struc" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/crypto" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) type TCPRequest struct { keyDerivation KeyDerivation method Method c2sSalt RequestSalt c2sNonce crypto.BytesGenerator c2sAEAD cipher.AEAD s2cSalt RequestSalt s2cNonce crypto.BytesGenerator s2cAEAD cipher.AEAD s2cSaltAssert RequestSalt s2cInitialPayloadSize int } func (t *TCPRequest) EncodeTCPRequestHeader(effectivePsk []byte, eih [][]byte, address DestinationAddress, destPort int, initialPayload []byte, out *buf.Buffer, ) error { requestSalt := newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength()) { err := requestSalt.FillAllFrom(cryptoRand.Reader) if err != nil { return newError("failed to fill salt").Base(err) } } t.c2sSalt = requestSalt sessionKey := make([]byte, t.method.GetSessionSubKeyAndSaltLength()) { err := t.keyDerivation.GetSessionSubKey(effectivePsk, requestSalt.Bytes(), sessionKey) if err != nil { return newError("failed to get session sub key").Base(err) } } aead, err := t.method.GetStreamAEAD(sessionKey) if err != nil { return newError("failed to get stream AEAD").Base(err) } t.c2sAEAD = aead paddingLength := TCPMinPaddingLength if initialPayload == nil { initialPayload = []byte{} paddingLength += 1 + dice.RollWith(TCPMaxPaddingLength, cryptoRand.Reader) } variableLengthHeader := &TCPRequestHeader3VariableLength{ DestinationAddress: address, Contents: struct { PaddingLength uint16 `struc:"sizeof=Padding"` Padding []byte }(struct { PaddingLength uint16 Padding []byte }{ PaddingLength: uint16(paddingLength), Padding: make([]byte, paddingLength), }), } variableLengthHeaderBuffer := buf.New() defer variableLengthHeaderBuffer.Release() { err := addrParser.WriteAddressPort(variableLengthHeaderBuffer, address, net.Port(destPort)) if err != nil { return newError("failed to write address port").Base(err) } } { err := struc.Pack(variableLengthHeaderBuffer, &variableLengthHeader.Contents) if err != nil { return newError("failed to pack variable length header").Base(err) } } { _, err := variableLengthHeaderBuffer.Write(initialPayload) if err != nil { return newError("failed to write initial payload").Base(err) } } fixedLengthHeader := &TCPRequestHeader2FixedLength{ Type: TCPHeaderTypeClientToServerStream, Timestamp: uint64(time.Now().Unix()), HeaderLength: uint16(variableLengthHeaderBuffer.Len()), } fixedLengthHeaderBuffer := buf.New() defer fixedLengthHeaderBuffer.Release() { err := struc.Pack(fixedLengthHeaderBuffer, fixedLengthHeader) if err != nil { return newError("failed to pack fixed length header").Base(err) } } eihHeader := ExtensibleIdentityHeaders(newAESEIH(0)) if len(eih) != 0 { eihGenerator := newAESEIHGeneratorContainer(len(eih), effectivePsk, eih) eihHeaderGenerated, err := eihGenerator.GenerateEIH(t.keyDerivation, t.method, requestSalt.Bytes()) if err != nil { return newError("failed to construct EIH").Base(err) } eihHeader = eihHeaderGenerated } preSessionKeyHeader := &TCPRequestHeader1PreSessionKey{ Salt: requestSalt, EIH: eihHeader, } preSessionKeyHeaderBuffer := buf.New() defer preSessionKeyHeaderBuffer.Release() { err := struc.Pack(preSessionKeyHeaderBuffer, preSessionKeyHeader) if err != nil { return newError("failed to pack pre session key header").Base(err) } } requestNonce := crypto.GenerateInitialAEADNonce() t.c2sNonce = requestNonce { n, err := out.Write(preSessionKeyHeaderBuffer.BytesFrom(0)) if err != nil { return newError("failed to write pre session key header").Base(err) } if int32(n) != preSessionKeyHeaderBuffer.Len() { return newError("failed to write pre session key header") } } { fixedLengthEncrypted := out.Extend(fixedLengthHeaderBuffer.Len() + int32(aead.Overhead())) aead.Seal(fixedLengthEncrypted[:0], requestNonce(), fixedLengthHeaderBuffer.Bytes(), nil) } { variableLengthEncrypted := out.Extend(variableLengthHeaderBuffer.Len() + int32(aead.Overhead())) aead.Seal(variableLengthEncrypted[:0], requestNonce(), variableLengthHeaderBuffer.Bytes(), nil) } return nil } func (t *TCPRequest) DecodeTCPResponseHeader(effectivePsk []byte, in io.Reader) error { var preSessionKeyHeader TCPResponseHeader1PreSessionKey preSessionKeyHeader.Salt = newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength()) { err := struc.Unpack(in, &preSessionKeyHeader) if err != nil { return newError("failed to unpack pre session key header").Base(err) } } s2cSalt := preSessionKeyHeader.Salt.Bytes() t.s2cSalt = preSessionKeyHeader.Salt sessionKey := make([]byte, t.method.GetSessionSubKeyAndSaltLength()) { err := t.keyDerivation.GetSessionSubKey(effectivePsk, s2cSalt, sessionKey) if err != nil { return newError("failed to get session sub key").Base(err) } } aead, err := t.method.GetStreamAEAD(sessionKey) if err != nil { return newError("failed to get stream AEAD").Base(err) } t.s2cAEAD = aead fixedLengthHeaderEncryptedBuffer := buf.New() defer fixedLengthHeaderEncryptedBuffer.Release() { _, err := fixedLengthHeaderEncryptedBuffer.ReadFullFrom(in, 11+int32(t.method.GetSessionSubKeyAndSaltLength())+int32(aead.Overhead())) if err != nil { return newError("failed to read fixed length header encrypted").Base(err) } } s2cNonce := crypto.GenerateInitialAEADNonce() t.s2cNonce = s2cNonce fixedLengthHeaderDecryptedBuffer := buf.New() defer fixedLengthHeaderDecryptedBuffer.Release() { decryptionBuffer := fixedLengthHeaderDecryptedBuffer.Extend(11 + int32(t.method.GetSessionSubKeyAndSaltLength())) _, err = aead.Open(decryptionBuffer[:0], s2cNonce(), fixedLengthHeaderEncryptedBuffer.Bytes(), nil) if err != nil { return newError("failed to decrypt fixed length header").Base(err) } } var fixedLengthHeader TCPResponseHeader2FixedLength fixedLengthHeader.RequestSalt = newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength()) { err := struc.Unpack(bytes.NewReader(fixedLengthHeaderDecryptedBuffer.Bytes()), &fixedLengthHeader) if err != nil { return newError("failed to unpack fixed length header").Base(err) } } if fixedLengthHeader.Type != TCPHeaderTypeServerToClientStream { return newError("unexpected TCP header type") } timeDifference := int64(fixedLengthHeader.Timestamp) - time.Now().Unix() if timeDifference < -30 || timeDifference > 30 { return newError("timestamp is too far away, timeDifference = ", timeDifference) } t.s2cSaltAssert = fixedLengthHeader.RequestSalt t.s2cInitialPayloadSize = int(fixedLengthHeader.InitialPayloadLength) return nil } func (t *TCPRequest) CheckC2SConnectionConstraint() error { if !bytes.Equal(t.c2sSalt.Bytes(), t.s2cSaltAssert.Bytes()) { return newError("c2s salt not equal to s2c salt assert") } return nil } func (t *TCPRequest) CreateClientS2CReader(in io.Reader, initialPayload *buf.Buffer) (buf.Reader, error) { AEADAuthenticator := &crypto.AEADAuthenticator{ AEAD: t.s2cAEAD, NonceGenerator: t.s2cNonce, AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } initialPayloadEncrypted := buf.NewWithSize(65535) defer initialPayloadEncrypted.Release() initialPayloadEncryptedBytes := initialPayloadEncrypted.Extend(int32(t.s2cAEAD.Overhead()) + int32(t.s2cInitialPayloadSize)) _, err := io.ReadFull(in, initialPayloadEncryptedBytes) if err != nil { return nil, newError("failed to read initial payload").Base(err) } initialPayloadBytes := initialPayload.Extend(int32(t.s2cInitialPayloadSize)) _, err = t.s2cAEAD.Open(initialPayloadBytes[:0], t.s2cNonce(), initialPayloadEncryptedBytes, nil) if err != nil { return nil, newError("failed to decrypt initial payload").Base(err) } return crypto.NewAuthenticationReader(AEADAuthenticator, &AEADChunkSizeParser{ Auth: AEADAuthenticator, }, in, protocol.TransferTypeStream, nil), nil } func (t *TCPRequest) CreateClientC2SWriter(writer io.Writer) buf.Writer { AEADAuthenticator := &crypto.AEADAuthenticator{ AEAD: t.c2sAEAD, NonceGenerator: t.c2sNonce, AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser := &crypto.AEADChunkSizeParser{ Auth: AEADAuthenticator, } return crypto.NewAuthenticationWriter(AEADAuthenticator, sizeParser, writer, protocol.TransferTypeStream, nil) } type AEADChunkSizeParser struct { Auth *crypto.AEADAuthenticator } func (p *AEADChunkSizeParser) HasConstantOffset() uint16 { return uint16(p.Auth.Overhead()) } func (p *AEADChunkSizeParser) SizeBytes() int32 { return 2 + int32(p.Auth.Overhead()) } func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte { binary.BigEndian.PutUint16(b, size-uint16(p.Auth.Overhead())) b, err := p.Auth.Seal(b[:0], b[:2]) common.Must(err) return b } func (p *AEADChunkSizeParser) Decode(b []byte) (uint16, error) { b, err := p.Auth.Open(b[:0], b) if err != nil { return 0, err } return binary.BigEndian.Uint16(b), nil } ================================================ FILE: proxy/shadowsocks2022/errors.generated.go ================================================ package shadowsocks2022 import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/shadowsocks2022/kdf_blake3.go ================================================ package shadowsocks2022 import ( "lukechampine.com/blake3" "github.com/v2fly/v2ray-core/v5/common/buf" ) func newBLAKE3KeyDerivation() *BLAKE3KeyDerivation { return &BLAKE3KeyDerivation{} } type BLAKE3KeyDerivation struct{} func (b BLAKE3KeyDerivation) GetSessionSubKey(effectivePsk, salt []byte, outKey []byte) error { keyingMaterialBuffer := buf.New() keyingMaterialBuffer.Write(effectivePsk) keyingMaterialBuffer.Write(salt) blake3.DeriveKey(outKey, "shadowsocks 2022 session subkey", keyingMaterialBuffer.Bytes()) keyingMaterialBuffer.Release() return nil } func (b BLAKE3KeyDerivation) GetIdentitySubKey(effectivePsk, salt []byte, outKey []byte) error { keyingMaterialBuffer := buf.New() keyingMaterialBuffer.Write(effectivePsk) keyingMaterialBuffer.Write(salt) blake3.DeriveKey(outKey, "shadowsocks 2022 identity subkey", keyingMaterialBuffer.Bytes()) keyingMaterialBuffer.Release() return nil } ================================================ FILE: proxy/shadowsocks2022/method_aes128gcm.go ================================================ package shadowsocks2022 import ( "crypto/aes" "crypto/cipher" ) func newAES128GCMMethod() *AES128GCMMethod { return &AES128GCMMethod{} } type AES128GCMMethod struct{} func (a AES128GCMMethod) GetSessionSubKeyAndSaltLength() int { return 16 } func (a AES128GCMMethod) GetStreamAEAD(sessionSubKey []byte) (cipher.AEAD, error) { aesCipher, err := aes.NewCipher(sessionSubKey) if err != nil { return nil, newError("failed to create AES cipher").Base(err) } aead, err := cipher.NewGCM(aesCipher) if err != nil { return nil, newError("failed to create AES-GCM AEAD").Base(err) } return aead, nil } func (a AES128GCMMethod) GenerateEIH(currentIdentitySubKey []byte, nextPskHash []byte, out []byte) error { aesCipher, err := aes.NewCipher(currentIdentitySubKey) if err != nil { return newError("failed to create AES cipher").Base(err) } aesCipher.Encrypt(out, nextPskHash) return nil } func (a AES128GCMMethod) GetUDPClientProcessor(ipsk [][]byte, psk []byte, derivation KeyDerivation) (UDPClientPacketProcessor, error) { reqSeparateHeaderPsk := psk if ipsk != nil { reqSeparateHeaderPsk = ipsk[0] } reqSeparateHeaderCipher, err := aes.NewCipher(reqSeparateHeaderPsk) if err != nil { return nil, newError("failed to create AES cipher").Base(err) } respSeparateHeaderCipher, err := aes.NewCipher(psk) if err != nil { return nil, newError("failed to create AES cipher").Base(err) } getPacketAEAD := func(sessionID []byte) cipher.AEAD { sessionKey := make([]byte, a.GetSessionSubKeyAndSaltLength()) derivation.GetSessionSubKey(psk, sessionID, sessionKey) block, err := aes.NewCipher(sessionKey) if err != nil { panic(err) } aead, err := cipher.NewGCM(block) if err != nil { panic(err) } return aead } if len(ipsk) == 0 { return NewAESUDPClientPacketProcessor(reqSeparateHeaderCipher, respSeparateHeaderCipher, getPacketAEAD, nil), nil } eihGenerator := newAESEIHGeneratorContainer(len(ipsk), psk, ipsk) getEIH := func(mask []byte) ExtensibleIdentityHeaders { eih, err := eihGenerator.GenerateEIHUDP(derivation, a, mask) if err != nil { newError("failed to generate EIH").Base(err).WriteToLog() } return eih } return NewAESUDPClientPacketProcessor(reqSeparateHeaderCipher, respSeparateHeaderCipher, getPacketAEAD, getEIH), nil } ================================================ FILE: proxy/shadowsocks2022/method_aes256gcm.go ================================================ package shadowsocks2022 import ( "crypto/aes" "crypto/cipher" ) func newAES256GCMMethod() *AES256GCMMethod { return &AES256GCMMethod{} } type AES256GCMMethod struct{} func (a AES256GCMMethod) GetSessionSubKeyAndSaltLength() int { return 32 } func (a AES256GCMMethod) GetStreamAEAD(sessionSubKey []byte) (cipher.AEAD, error) { aesCipher, err := aes.NewCipher(sessionSubKey) if err != nil { return nil, newError("failed to create AES cipher").Base(err) } aead, err := cipher.NewGCM(aesCipher) if err != nil { return nil, newError("failed to create AES-GCM AEAD").Base(err) } return aead, nil } func (a AES256GCMMethod) GenerateEIH(currentIdentitySubKey []byte, nextPskHash []byte, out []byte) error { aesCipher, err := aes.NewCipher(currentIdentitySubKey) if err != nil { return newError("failed to create AES cipher").Base(err) } aesCipher.Encrypt(out, nextPskHash) return nil } func (a AES256GCMMethod) GetUDPClientProcessor(ipsk [][]byte, psk []byte, derivation KeyDerivation) (UDPClientPacketProcessor, error) { reqSeparateHeaderPsk := psk if ipsk != nil { reqSeparateHeaderPsk = ipsk[0] } reqSeparateHeaderCipher, err := aes.NewCipher(reqSeparateHeaderPsk) if err != nil { return nil, newError("failed to create AES cipher").Base(err) } respSeparateHeaderCipher, err := aes.NewCipher(psk) if err != nil { return nil, newError("failed to create AES cipher").Base(err) } getPacketAEAD := func(sessionID []byte) cipher.AEAD { sessionKey := make([]byte, a.GetSessionSubKeyAndSaltLength()) derivation.GetSessionSubKey(psk, sessionID, sessionKey) block, err := aes.NewCipher(sessionKey) if err != nil { panic(err) } aead, err := cipher.NewGCM(block) if err != nil { panic(err) } return aead } if len(ipsk) == 0 { return NewAESUDPClientPacketProcessor(reqSeparateHeaderCipher, respSeparateHeaderCipher, getPacketAEAD, nil), nil } eihGenerator := newAESEIHGeneratorContainer(len(ipsk), psk, ipsk) getEIH := func(mask []byte) ExtensibleIdentityHeaders { eih, err := eihGenerator.GenerateEIHUDP(derivation, a, mask) if err != nil { newError("failed to generate EIH").Base(err).WriteToLog() } return eih } return NewAESUDPClientPacketProcessor(reqSeparateHeaderCipher, respSeparateHeaderCipher, getPacketAEAD, getEIH), nil } ================================================ FILE: proxy/shadowsocks2022/requestsalt.go ================================================ package shadowsocks2022 import ( "encoding/hex" "io" "github.com/v2fly/struc" ) func newRequestSaltWithLength(length int) RequestSalt { return &requestSaltWithLength{length: length} } type requestSaltWithLength struct { length int content []byte } func (r *requestSaltWithLength) isRequestSalt() {} func (r *requestSaltWithLength) Pack(p []byte, opt *struc.Options) (int, error) { n := copy(p, r.content) if n != r.length { return 0, newError("failed to pack request salt with length") } return n, nil } func (r *requestSaltWithLength) Unpack(reader io.Reader, length int, opt *struc.Options) error { r.content = make([]byte, r.length) n, err := io.ReadFull(reader, r.content) if err != nil { return newError("failed to unpack request salt with length").Base(err) } if n != r.length { return newError("failed to unpack request salt with length") } return nil } func (r *requestSaltWithLength) Size(opt *struc.Options) int { return r.length } func (r *requestSaltWithLength) String() string { return hex.Dump(r.content) } func (r *requestSaltWithLength) Bytes() []byte { return r.content } func (r *requestSaltWithLength) FillAllFrom(reader io.Reader) error { r.content = make([]byte, r.length) _, err := io.ReadFull(reader, r.content) if err != nil { return newError("failed to fill salt from reader").Base(err) } return nil } ================================================ FILE: proxy/shadowsocks2022/ss2022.go ================================================ package shadowsocks2022 import ( "crypto/cipher" "io" "github.com/v2fly/struc" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type KeyDerivation interface { GetSessionSubKey(effectivePsk, Salt []byte, OutKey []byte) error GetIdentitySubKey(effectivePsk, Salt []byte, OutKey []byte) error } type Method interface { GetSessionSubKeyAndSaltLength() int GetStreamAEAD(SessionSubKey []byte) (cipher.AEAD, error) GenerateEIH(CurrentIdentitySubKey []byte, nextPskHash []byte, out []byte) error GetUDPClientProcessor(ipsk [][]byte, psk []byte, derivation KeyDerivation) (UDPClientPacketProcessor, error) } type ExtensibleIdentityHeaders interface { struc.Custom } type DestinationAddress interface { net.Address } type RequestSalt interface { struc.Custom isRequestSalt() Bytes() []byte FillAllFrom(reader io.Reader) error } type TCPRequestHeader1PreSessionKey struct { Salt RequestSalt EIH ExtensibleIdentityHeaders } type TCPRequestHeader2FixedLength struct { Type byte Timestamp uint64 HeaderLength uint16 } type TCPRequestHeader3VariableLength struct { DestinationAddress DestinationAddress Contents struct { PaddingLength uint16 `struc:"sizeof=Padding"` Padding []byte } } type TCPRequestHeader struct { PreSessionKeyHeader TCPRequestHeader1PreSessionKey FixedLengthHeader TCPRequestHeader2FixedLength Header TCPRequestHeader3VariableLength } type TCPResponseHeader1PreSessionKey struct { Salt RequestSalt } type TCPResponseHeader2FixedLength struct { Type byte Timestamp uint64 RequestSalt RequestSalt InitialPayloadLength uint16 } type TCPResponseHeader struct { PreSessionKeyHeader TCPResponseHeader1PreSessionKey Header TCPResponseHeader2FixedLength } const ( TCPHeaderTypeClientToServerStream = byte(0x00) TCPHeaderTypeServerToClientStream = byte(0x01) TCPMinPaddingLength = 0 TCPMaxPaddingLength = 900 ) var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6), protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain), ) type UDPRequest struct { SessionID [8]byte PacketID uint64 TimeStamp uint64 Address DestinationAddress Port int Payload *buf.Buffer } type UDPResponse struct { UDPRequest ClientSessionID [8]byte } const ( UDPHeaderTypeClientToServerStream = byte(0x00) UDPHeaderTypeServerToClientStream = byte(0x01) ) type UDPClientPacketProcessorCachedStateContainer interface { GetCachedState(sessionID string) UDPClientPacketProcessorCachedState PutCachedState(sessionID string, cache UDPClientPacketProcessorCachedState) GetCachedServerState(serverSessionID string) UDPClientPacketProcessorCachedState PutCachedServerState(serverSessionID string, cache UDPClientPacketProcessorCachedState) } type UDPClientPacketProcessorCachedState interface{} // UDPClientPacketProcessor // Caller retain and receive all ownership of the buffer type UDPClientPacketProcessor interface { EncodeUDPRequest(request *UDPRequest, out *buf.Buffer, cache UDPClientPacketProcessorCachedStateContainer) error DecodeUDPResp(input []byte, resp *UDPResponse, cache UDPClientPacketProcessorCachedStateContainer) error } ================================================ FILE: proxy/shadowsocks2022/udp_aes.go ================================================ package shadowsocks2022 import ( "bytes" "crypto/cipher" "io" "github.com/v2fly/struc" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" ) type AESUDPClientPacketProcessor struct { requestSeparateHeaderBlockCipher cipher.Block responseSeparateHeaderBlockCipher cipher.Block mainPacketAEAD func([]byte) cipher.AEAD EIHGenerator func([]byte) ExtensibleIdentityHeaders } func NewAESUDPClientPacketProcessor(requestSeparateHeaderBlockCipher, responseSeparateHeaderBlockCipher cipher.Block, mainPacketAEAD func([]byte) cipher.AEAD, eih func([]byte) ExtensibleIdentityHeaders) *AESUDPClientPacketProcessor { return &AESUDPClientPacketProcessor{ requestSeparateHeaderBlockCipher: requestSeparateHeaderBlockCipher, responseSeparateHeaderBlockCipher: responseSeparateHeaderBlockCipher, mainPacketAEAD: mainPacketAEAD, EIHGenerator: eih, } } type separateHeader struct { SessionID [8]byte PacketID uint64 } type header struct { Type byte TimeStamp uint64 PaddingLength uint16 `struc:"sizeof=Padding"` Padding []byte } type respHeader struct { Type byte TimeStamp uint64 ClientSessionID [8]byte PaddingLength uint16 `struc:"sizeof=Padding"` Padding []byte } type cachedUDPState struct { sessionAEAD cipher.AEAD sessionRecvAEAD cipher.AEAD } func (p *AESUDPClientPacketProcessor) EncodeUDPRequest(request *UDPRequest, out *buf.Buffer, cache UDPClientPacketProcessorCachedStateContainer, ) error { separateHeaderStruct := separateHeader{PacketID: request.PacketID, SessionID: request.SessionID} separateHeaderBuffer := buf.New() defer separateHeaderBuffer.Release() { err := struc.Pack(separateHeaderBuffer, &separateHeaderStruct) if err != nil { return newError("failed to pack separateHeader").Base(err) } } separateHeaderBufferBytes := separateHeaderBuffer.Bytes() { encryptedDest := out.Extend(16) p.requestSeparateHeaderBlockCipher.Encrypt(encryptedDest, separateHeaderBufferBytes) } if p.EIHGenerator != nil { eih := p.EIHGenerator(separateHeaderBufferBytes[0:16]) eihHeader := struct { EIH ExtensibleIdentityHeaders }{ EIH: eih, } err := struc.Pack(out, &eihHeader) if err != nil { return newError("failed to pack eih").Base(err) } } headerStruct := header{ Type: UDPHeaderTypeClientToServerStream, TimeStamp: request.TimeStamp, PaddingLength: 0, Padding: nil, } requestBodyBuffer := buf.New() { err := struc.Pack(requestBodyBuffer, &headerStruct) if err != nil { return newError("failed to header").Base(err) } } { err := addrParser.WriteAddressPort(requestBodyBuffer, request.Address, net.Port(request.Port)) if err != nil { return newError("failed to write address port").Base(err) } } { _, err := io.Copy(requestBodyBuffer, bytes.NewReader(request.Payload.Bytes())) if err != nil { return newError("failed to copy payload").Base(err) } } { cacheKey := string(separateHeaderBufferBytes[0:8]) receivedCacheInterface := cache.GetCachedState(cacheKey) cachedState := &cachedUDPState{} if receivedCacheInterface != nil { cachedState = receivedCacheInterface.(*cachedUDPState) } if cachedState.sessionAEAD == nil { cachedState.sessionAEAD = p.mainPacketAEAD(separateHeaderBufferBytes[0:8]) cache.PutCachedState(cacheKey, cachedState) } mainPacketAEADMaterialized := cachedState.sessionAEAD encryptedDest := out.Extend(int32(mainPacketAEADMaterialized.Overhead()) + requestBodyBuffer.Len()) mainPacketAEADMaterialized.Seal(encryptedDest[:0], separateHeaderBuffer.Bytes()[4:16], requestBodyBuffer.Bytes(), nil) } return nil } func (p *AESUDPClientPacketProcessor) DecodeUDPResp(input []byte, resp *UDPResponse, cache UDPClientPacketProcessorCachedStateContainer, ) error { separateHeaderBuffer := buf.New() defer separateHeaderBuffer.Release() { encryptedDest := separateHeaderBuffer.Extend(16) p.responseSeparateHeaderBlockCipher.Decrypt(encryptedDest, input) } separateHeaderStruct := separateHeader{} { err := struc.Unpack(separateHeaderBuffer, &separateHeaderStruct) if err != nil { return newError("failed to unpack separateHeader").Base(err) } } resp.PacketID = separateHeaderStruct.PacketID resp.SessionID = separateHeaderStruct.SessionID { cacheKey := string(separateHeaderBuffer.Bytes()[0:8]) receivedCacheInterface := cache.GetCachedServerState(cacheKey) cachedState := &cachedUDPState{} if receivedCacheInterface != nil { cachedState = receivedCacheInterface.(*cachedUDPState) } if cachedState.sessionRecvAEAD == nil { cachedState.sessionRecvAEAD = p.mainPacketAEAD(separateHeaderBuffer.Bytes()[0:8]) cache.PutCachedServerState(cacheKey, cachedState) } mainPacketAEADMaterialized := cachedState.sessionRecvAEAD decryptedDestBuffer := buf.New() decryptedDest := decryptedDestBuffer.Extend(int32(len(input)) - 16 - int32(mainPacketAEADMaterialized.Overhead())) _, err := mainPacketAEADMaterialized.Open(decryptedDest[:0], separateHeaderBuffer.Bytes()[4:16], input[16:], nil) if err != nil { return newError("failed to open main packet").Base(err) } decryptedDestReader := bytes.NewReader(decryptedDest) headerStruct := respHeader{} { err := struc.Unpack(decryptedDestReader, &headerStruct) if err != nil { return newError("failed to unpack header").Base(err) } } resp.TimeStamp = headerStruct.TimeStamp addressReaderBuf := buf.New() defer addressReaderBuf.Release() var port net.Port resp.Address, port, err = addrParser.ReadAddressPort(addressReaderBuf, decryptedDestReader) if err != nil { return newError("failed to read address port").Base(err) } resp.Port = int(port) readedLength := decryptedDestReader.Size() - int64(decryptedDestReader.Len()) decryptedDestBuffer.Advance(int32(readedLength)) resp.Payload = decryptedDestBuffer resp.ClientSessionID = headerStruct.ClientSessionID return nil } } ================================================ FILE: proxy/socks/client.go ================================================ package socks import ( "context" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) // Client is a Socks5 client. type Client struct { serverPicker protocol.ServerPicker policyManager policy.Manager version Version dns dns.Client delayAuthWrite bool } // NewClient create a new Socks5 client based on the given config. func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { serverList := protocol.NewServerList() for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { return nil, newError("failed to get server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { return nil, newError("0 target server") } v := core.MustFromContext(ctx) c := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), version: config.Version, delayAuthWrite: config.DelayAuthWrite, } if config.Version == Version_SOCKS4 { c.dns = v.GetFeature(dns.ClientType()).(dns.Client) } return c, nil } // Process implements proxy.Outbound.Process. func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified.") } // Destination of the inner request. destination := outbound.Target // Outbound server. var server *protocol.ServerSpec // Outbound server's destination. var dest net.Destination // Connection to the outbound server. var conn internet.Connection if err := retry.ExponentialBackoff(5, 100).On(func() error { server = c.serverPicker.PickServer() dest = server.Destination() rawConn, err := dialer.Dial(ctx, dest) if err != nil { return err } conn = rawConn return nil }); err != nil { return newError("failed to find an available destination").Base(err) } defer func() { if err := conn.Close(); err != nil { newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } }() p := c.policyManager.ForLevel(0) request := &protocol.RequestHeader{ Version: socks5Version, Command: protocol.RequestCommandTCP, Address: destination.Address, Port: destination.Port, } switch c.version { case Version_SOCKS4: if request.Address.Family().IsDomain() { ips, err := dns.LookupIPWithOption(c.dns, request.Address.Domain(), dns.IPOption{IPv4Enable: true, IPv6Enable: false, FakeEnable: false}) if err != nil { return err } else if len(ips) == 0 { return dns.ErrEmptyResponse } request.Address = net.IPAddress(ips[0]) } fallthrough case Version_SOCKS4A: request.Version = socks4Version if destination.Network == net.Network_UDP { return newError("udp is not supported in socks4") } else if destination.Address.Family().IsIPv6() { return newError("ipv6 is not supported in socks4") } } if destination.Network == net.Network_UDP { request.Command = protocol.RequestCommandUDP } user := server.PickUser() if user != nil { request.User = user p = c.policyManager.ForLevel(user.Level) } if err := conn.SetDeadline(time.Now().Add(p.Timeouts.Handshake)); err != nil { newError("failed to set deadline for handshake").Base(err).WriteToLog(session.ExportIDToError(ctx)) } var udpRequest *protocol.RequestHeader var err error if request.Version == socks4Version { err = ClientHandshake4(request, conn, conn) if err != nil { return newError("failed to establish connection to server").AtWarning().Base(err) } } else { udpRequest, err = ClientHandshake(request, conn, conn, c.delayAuthWrite) if err != nil { return newError("failed to establish connection to server").AtWarning().Base(err) } } if udpRequest != nil { if udpRequest.Address == net.AnyIP || udpRequest.Address == net.AnyIPv6 { udpRequest.Address = dest.Address } } if err := conn.SetDeadline(time.Time{}); err != nil { newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx)) } ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle) if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil { udpConn, err := dialer.Dial(ctx, udpRequest.Destination()) if err != nil { return newError("failed to create UDP connection").Base(err) } defer udpConn.Close() requestDone := func() error { protocolWriter := NewUDPWriter(request, udpConn) return udp.CopyPacketConn(protocolWriter, packetConn, udp.UpdateActivity(timer)) } responseDone := func() error { protocolReader := &UDPReader{ reader: udpConn, } return udp.CopyPacketConn(packetConn, protocolReader, udp.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } var requestFunc func() error var responseFunc func() error if request.Command == protocol.RequestCommandTCP { requestFunc = func() error { defer timer.SetTimeout(p.Timeouts.DownlinkOnly) return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) } responseFunc = func() error { defer timer.SetTimeout(p.Timeouts.UplinkOnly) return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) } } else if request.Command == protocol.RequestCommandUDP { udpConn, err := dialer.Dial(ctx, udpRequest.Destination()) if err != nil { return newError("failed to create UDP connection").Base(err) } defer udpConn.Close() requestFunc = func() error { defer timer.SetTimeout(p.Timeouts.DownlinkOnly) return buf.Copy(link.Reader, &buf.SequentialWriter{Writer: NewUDPWriter(request, udpConn)}, buf.UpdateActivity(timer)) } responseFunc = func() error { defer timer.SetTimeout(p.Timeouts.UplinkOnly) reader := &UDPReader{reader: udpConn} return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)) } } responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { return newError("connection ends").Base(err) } return nil } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewClient(ctx, config.(*ClientConfig)) })) } ================================================ FILE: proxy/socks/config.go ================================================ package socks import "github.com/v2fly/v2ray-core/v5/common/protocol" func (a *Account) Equals(another protocol.Account) bool { if account, ok := another.(*Account); ok { return a.Username == account.Username } return false } func (a *Account) AsAccount() (protocol.Account, error) { return a, nil } func (c *ServerConfig) HasAccount(username, password string) bool { if c.Accounts == nil { return false } storedPassed, found := c.Accounts[username] if !found { return false } return storedPassed == password } ================================================ FILE: proxy/socks/config.pb.go ================================================ package socks import ( net "github.com/v2fly/v2ray-core/v5/common/net" packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" protocol "github.com/v2fly/v2ray-core/v5/common/protocol" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // AuthType is the authentication type of Socks proxy. type AuthType int32 const ( // NO_AUTH is for anonymous authentication. AuthType_NO_AUTH AuthType = 0 // PASSWORD is for username/password authentication. AuthType_PASSWORD AuthType = 1 ) // Enum value maps for AuthType. var ( AuthType_name = map[int32]string{ 0: "NO_AUTH", 1: "PASSWORD", } AuthType_value = map[string]int32{ "NO_AUTH": 0, "PASSWORD": 1, } ) func (x AuthType) Enum() *AuthType { p := new(AuthType) *p = x return p } func (x AuthType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (AuthType) Descriptor() protoreflect.EnumDescriptor { return file_proxy_socks_config_proto_enumTypes[0].Descriptor() } func (AuthType) Type() protoreflect.EnumType { return &file_proxy_socks_config_proto_enumTypes[0] } func (x AuthType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use AuthType.Descriptor instead. func (AuthType) EnumDescriptor() ([]byte, []int) { return file_proxy_socks_config_proto_rawDescGZIP(), []int{0} } type Version int32 const ( Version_SOCKS5 Version = 0 Version_SOCKS4 Version = 1 Version_SOCKS4A Version = 2 ) // Enum value maps for Version. var ( Version_name = map[int32]string{ 0: "SOCKS5", 1: "SOCKS4", 2: "SOCKS4A", } Version_value = map[string]int32{ "SOCKS5": 0, "SOCKS4": 1, "SOCKS4A": 2, } ) func (x Version) Enum() *Version { p := new(Version) *p = x return p } func (x Version) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Version) Descriptor() protoreflect.EnumDescriptor { return file_proxy_socks_config_proto_enumTypes[1].Descriptor() } func (Version) Type() protoreflect.EnumType { return &file_proxy_socks_config_proto_enumTypes[1] } func (x Version) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Version.Descriptor instead. func (Version) EnumDescriptor() ([]byte, []int) { return file_proxy_socks_config_proto_rawDescGZIP(), []int{1} } // Account represents a Socks account. type Account struct { state protoimpl.MessageState `protogen:"open.v1"` Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Account) Reset() { *x = Account{} mi := &file_proxy_socks_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Account) String() string { return protoimpl.X.MessageStringOf(x) } func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_socks_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Account.ProtoReflect.Descriptor instead. func (*Account) Descriptor() ([]byte, []int) { return file_proxy_socks_config_proto_rawDescGZIP(), []int{0} } func (x *Account) GetUsername() string { if x != nil { return x.Username } return "" } func (x *Account) GetPassword() string { if x != nil { return x.Password } return "" } // ServerConfig is the protobuf config for Socks server. type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` AuthType AuthType `protobuf:"varint,1,opt,name=auth_type,json=authType,proto3,enum=v2ray.core.proxy.socks.AuthType" json:"auth_type,omitempty"` Accounts map[string]string `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Address *net.IPOrDomain `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` UdpEnabled bool `protobuf:"varint,4,opt,name=udp_enabled,json=udpEnabled,proto3" json:"udp_enabled,omitempty"` // Deprecated: Marked as deprecated in proxy/socks/config.proto. Timeout uint32 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` UserLevel uint32 `protobuf:"varint,6,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,7,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"` DeferLastReply bool `protobuf:"varint,8,opt,name=defer_last_reply,json=deferLastReply,proto3" json:"defer_last_reply,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_socks_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_socks_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_socks_config_proto_rawDescGZIP(), []int{1} } func (x *ServerConfig) GetAuthType() AuthType { if x != nil { return x.AuthType } return AuthType_NO_AUTH } func (x *ServerConfig) GetAccounts() map[string]string { if x != nil { return x.Accounts } return nil } func (x *ServerConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *ServerConfig) GetUdpEnabled() bool { if x != nil { return x.UdpEnabled } return false } // Deprecated: Marked as deprecated in proxy/socks/config.proto. func (x *ServerConfig) GetTimeout() uint32 { if x != nil { return x.Timeout } return 0 } func (x *ServerConfig) GetUserLevel() uint32 { if x != nil { return x.UserLevel } return 0 } func (x *ServerConfig) GetPacketEncoding() packetaddr.PacketAddrType { if x != nil { return x.PacketEncoding } return packetaddr.PacketAddrType(0) } func (x *ServerConfig) GetDeferLastReply() bool { if x != nil { return x.DeferLastReply } return false } // ClientConfig is the protobuf config for Socks client. type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Sever is a list of Socks server addresses. Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` Version Version `protobuf:"varint,2,opt,name=version,proto3,enum=v2ray.core.proxy.socks.Version" json:"version,omitempty"` DelayAuthWrite bool `protobuf:"varint,3,opt,name=delay_auth_write,json=delayAuthWrite,proto3" json:"delay_auth_write,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_socks_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_socks_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_socks_config_proto_rawDescGZIP(), []int{2} } func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint { if x != nil { return x.Server } return nil } func (x *ClientConfig) GetVersion() Version { if x != nil { return x.Version } return Version_SOCKS5 } func (x *ClientConfig) GetDelayAuthWrite() bool { if x != nil { return x.DelayAuthWrite } return false } var File_proxy_socks_config_proto protoreflect.FileDescriptor const file_proxy_socks_config_proto_rawDesc = "" + "\n" + "\x18proxy/socks/config.proto\x12\x16v2ray.core.proxy.socks\x1a\x18common/net/address.proto\x1a\"common/net/packetaddr/config.proto\x1a!common/protocol/server_spec.proto\"A\n" + "\aAccount\x12\x1a\n" + "\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" + "\bpassword\x18\x02 \x01(\tR\bpassword\"\xf3\x03\n" + "\fServerConfig\x12=\n" + "\tauth_type\x18\x01 \x01(\x0e2 .v2ray.core.proxy.socks.AuthTypeR\bauthType\x12N\n" + "\baccounts\x18\x02 \x03(\v22.v2ray.core.proxy.socks.ServerConfig.AccountsEntryR\baccounts\x12;\n" + "\aaddress\x18\x03 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x1f\n" + "\vudp_enabled\x18\x04 \x01(\bR\n" + "udpEnabled\x12\x1c\n" + "\atimeout\x18\x05 \x01(\rB\x02\x18\x01R\atimeout\x12\x1d\n" + "\n" + "user_level\x18\x06 \x01(\rR\tuserLevel\x12R\n" + "\x0fpacket_encoding\x18\a \x01(\x0e2).v2ray.core.net.packetaddr.PacketAddrTypeR\x0epacketEncoding\x12(\n" + "\x10defer_last_reply\x18\b \x01(\bR\x0edeferLastReply\x1a;\n" + "\rAccountsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xb7\x01\n" + "\fClientConfig\x12B\n" + "\x06server\x18\x01 \x03(\v2*.v2ray.core.common.protocol.ServerEndpointR\x06server\x129\n" + "\aversion\x18\x02 \x01(\x0e2\x1f.v2ray.core.proxy.socks.VersionR\aversion\x12(\n" + "\x10delay_auth_write\x18\x03 \x01(\bR\x0edelayAuthWrite*%\n" + "\bAuthType\x12\v\n" + "\aNO_AUTH\x10\x00\x12\f\n" + "\bPASSWORD\x10\x01*.\n" + "\aVersion\x12\n" + "\n" + "\x06SOCKS5\x10\x00\x12\n" + "\n" + "\x06SOCKS4\x10\x01\x12\v\n" + "\aSOCKS4A\x10\x02Bc\n" + "\x1acom.v2ray.core.proxy.socksP\x01Z*github.com/v2fly/v2ray-core/v5/proxy/socks\xaa\x02\x16V2Ray.Core.Proxy.Socksb\x06proto3" var ( file_proxy_socks_config_proto_rawDescOnce sync.Once file_proxy_socks_config_proto_rawDescData []byte ) func file_proxy_socks_config_proto_rawDescGZIP() []byte { file_proxy_socks_config_proto_rawDescOnce.Do(func() { file_proxy_socks_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_socks_config_proto_rawDesc), len(file_proxy_socks_config_proto_rawDesc))) }) return file_proxy_socks_config_proto_rawDescData } var file_proxy_socks_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_proxy_socks_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_proxy_socks_config_proto_goTypes = []any{ (AuthType)(0), // 0: v2ray.core.proxy.socks.AuthType (Version)(0), // 1: v2ray.core.proxy.socks.Version (*Account)(nil), // 2: v2ray.core.proxy.socks.Account (*ServerConfig)(nil), // 3: v2ray.core.proxy.socks.ServerConfig (*ClientConfig)(nil), // 4: v2ray.core.proxy.socks.ClientConfig nil, // 5: v2ray.core.proxy.socks.ServerConfig.AccountsEntry (*net.IPOrDomain)(nil), // 6: v2ray.core.common.net.IPOrDomain (packetaddr.PacketAddrType)(0), // 7: v2ray.core.net.packetaddr.PacketAddrType (*protocol.ServerEndpoint)(nil), // 8: v2ray.core.common.protocol.ServerEndpoint } var file_proxy_socks_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.proxy.socks.ServerConfig.auth_type:type_name -> v2ray.core.proxy.socks.AuthType 5, // 1: v2ray.core.proxy.socks.ServerConfig.accounts:type_name -> v2ray.core.proxy.socks.ServerConfig.AccountsEntry 6, // 2: v2ray.core.proxy.socks.ServerConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 7, // 3: v2ray.core.proxy.socks.ServerConfig.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType 8, // 4: v2ray.core.proxy.socks.ClientConfig.server:type_name -> v2ray.core.common.protocol.ServerEndpoint 1, // 5: v2ray.core.proxy.socks.ClientConfig.version:type_name -> v2ray.core.proxy.socks.Version 6, // [6:6] is the sub-list for method output_type 6, // [6:6] is the sub-list for method input_type 6, // [6:6] is the sub-list for extension type_name 6, // [6:6] is the sub-list for extension extendee 0, // [0:6] is the sub-list for field type_name } func init() { file_proxy_socks_config_proto_init() } func file_proxy_socks_config_proto_init() { if File_proxy_socks_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_socks_config_proto_rawDesc), len(file_proxy_socks_config_proto_rawDesc)), NumEnums: 2, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_socks_config_proto_goTypes, DependencyIndexes: file_proxy_socks_config_proto_depIdxs, EnumInfos: file_proxy_socks_config_proto_enumTypes, MessageInfos: file_proxy_socks_config_proto_msgTypes, }.Build() File_proxy_socks_config_proto = out.File file_proxy_socks_config_proto_goTypes = nil file_proxy_socks_config_proto_depIdxs = nil } ================================================ FILE: proxy/socks/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.socks; option csharp_namespace = "V2Ray.Core.Proxy.Socks"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/socks"; option java_package = "com.v2ray.core.proxy.socks"; option java_multiple_files = true; import "common/net/address.proto"; import "common/net/packetaddr/config.proto"; import "common/protocol/server_spec.proto"; // Account represents a Socks account. message Account { string username = 1; string password = 2; } // AuthType is the authentication type of Socks proxy. enum AuthType { // NO_AUTH is for anonymous authentication. NO_AUTH = 0; // PASSWORD is for username/password authentication. PASSWORD = 1; } enum Version { SOCKS5 = 0; SOCKS4 = 1; SOCKS4A = 2; } // ServerConfig is the protobuf config for Socks server. message ServerConfig { AuthType auth_type = 1; map accounts = 2; v2ray.core.common.net.IPOrDomain address = 3; bool udp_enabled = 4; uint32 timeout = 5 [deprecated = true]; uint32 user_level = 6; v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 7; bool defer_last_reply = 8; } // ClientConfig is the protobuf config for Socks client. message ClientConfig { // Sever is a list of Socks server addresses. repeated v2ray.core.common.protocol.ServerEndpoint server = 1; Version version = 2; bool delay_auth_write = 3; } ================================================ FILE: proxy/socks/errors.generated.go ================================================ package socks import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/socks/protocol.go ================================================ package socks import ( "encoding/binary" "io" gonet "net" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) const ( socks5Version = 0x05 socks4Version = 0x04 cmdTCPConnect = 0x01 cmdTCPBind = 0x02 cmdUDPAssociate = 0x03 cmdTorResolve = 0xF0 cmdTorResolvePTR = 0xF1 socks4RequestGranted = 90 socks4RequestRejected = 91 authNotRequired = 0x00 // authGssAPI = 0x01 authPassword = 0x02 authNoMatchingMethod = 0xFF statusSuccess = 0x00 statusConnRefused = 0x05 statusCmdNotSupport = 0x07 ) var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6), protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain), ) type ServerSession struct { config *ServerConfig address net.Address port net.Port clientAddress net.Address flushLastReply func(bool) error } func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { if s.config.AuthType == AuthType_PASSWORD { writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0)) return nil, newError("socks 4 is not allowed when auth is required.") } var port net.Port var address net.Address { buffer := buf.StackNew() if _, err := buffer.ReadFullFrom(reader, 6); err != nil { buffer.Release() return nil, newError("insufficient header").Base(err) } port = net.PortFromBytes(buffer.BytesRange(0, 2)) address = net.IPAddress(buffer.BytesRange(2, 6)) buffer.Release() } if _, err := ReadUntilNull(reader); /* user id */ err != nil { return nil, err } if address.IP()[0] == 0x00 { domain, err := ReadUntilNull(reader) if err != nil { return nil, newError("failed to read domain for socks 4a").Base(err) } address = net.DomainAddress(domain) } switch cmd { case cmdTCPConnect: request := &protocol.RequestHeader{ Command: protocol.RequestCommandTCP, Address: address, Port: port, Version: socks4Version, } if err := s.setupLastReply(func(ok bool) error { if ok { return writeSocks4Response(writer, socks4RequestGranted, net.AnyIP, net.Port(0)) } else { return writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0)) } }); err != nil { return nil, err } return request, nil default: writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0)) return nil, newError("unsupported command: ", cmd) } } func (s *ServerSession) auth5(nMethod byte, reader io.Reader, writer io.Writer) (username string, err error) { buffer := buf.StackNew() defer buffer.Release() if _, err = buffer.ReadFullFrom(reader, int32(nMethod)); err != nil { return "", newError("failed to read auth methods").Base(err) } var expectedAuth byte = authNotRequired if s.config.AuthType == AuthType_PASSWORD { expectedAuth = authPassword } if !hasAuthMethod(expectedAuth, buffer.BytesRange(0, int32(nMethod))) { writeSocks5AuthenticationResponse(writer, socks5Version, authNoMatchingMethod) return "", newError("no matching auth method") } if err := writeSocks5AuthenticationResponse(writer, socks5Version, expectedAuth); err != nil { return "", newError("failed to write auth response").Base(err) } if expectedAuth == authPassword { username, password, err := ReadUsernamePassword(reader) if err != nil { return "", newError("failed to read username and password for authentication").Base(err) } if !s.config.HasAccount(username, password) { writeSocks5AuthenticationResponse(writer, 0x01, 0xFF) return "", newError("invalid username or password") } if err := writeSocks5AuthenticationResponse(writer, 0x01, 0x00); err != nil { return "", newError("failed to write auth response").Base(err) } return username, nil } return "", nil } func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { var ( username string err error ) if username, err = s.auth5(nMethod, reader, writer); err != nil { return nil, err } var cmd byte { buffer := buf.StackNew() if _, err := buffer.ReadFullFrom(reader, 3); err != nil { buffer.Release() return nil, newError("failed to read request").Base(err) } cmd = buffer.Byte(1) buffer.Release() } request := new(protocol.RequestHeader) if username != "" { request.User = &protocol.MemoryUser{Email: username} } switch cmd { case cmdTCPConnect, cmdTorResolve, cmdTorResolvePTR: // We don't have a solution for Tor case now. Simply treat it as connect command. request.Command = protocol.RequestCommandTCP case cmdUDPAssociate: if !s.config.UdpEnabled { writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) return nil, newError("UDP is not enabled.") } request.Command = protocol.RequestCommandUDP case cmdTCPBind: writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) return nil, newError("TCP bind is not supported.") default: writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) return nil, newError("unknown command ", cmd) } request.Version = socks5Version addr, port, err := addrParser.ReadAddressPort(nil, reader) if err != nil { return nil, newError("failed to read address").Base(err) } request.Address = addr request.Port = port responseAddress := s.address responsePort := s.port //nolint:gocritic // Use if else chain for clarity if request.Command == protocol.RequestCommandUDP { if s.config.Address != nil { // Use configured IP as remote address in the response to UdpAssociate responseAddress = s.config.Address.AsAddress() } else if s.clientAddress == net.LocalHostIP || s.clientAddress == net.LocalHostIPv6 { // For localhost clients use loopback IP responseAddress = s.clientAddress } else { // For non-localhost clients use inbound listening address responseAddress = s.address } } if err := s.setupLastReply(func(ok bool) error { if ok { return writeSocks5Response(writer, statusSuccess, responseAddress, responsePort) } else { return writeSocks5Response(writer, statusConnRefused, net.AnyIP, net.Port(0)) } }); err != nil { return nil, err } return request, nil } // Sets the callback and calls or postpones it based on the boolean field func (s *ServerSession) setupLastReply(callback func(bool) error) error { noOpCallback := func(bool) error { return nil } // set the field even if we call it now because it will be called again s.flushLastReply = func(ok bool) error { s.flushLastReply = noOpCallback return callback(ok) } if s.config.GetDeferLastReply() { return nil } return s.flushLastReply(true) } // Handshake performs a Socks4/4a/5 handshake. func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { buffer := buf.StackNew() if _, err := buffer.ReadFullFrom(reader, 2); err != nil { buffer.Release() return nil, newError("insufficient header").Base(err) } version := buffer.Byte(0) cmd := buffer.Byte(1) buffer.Release() switch version { case socks4Version: return s.handshake4(cmd, reader, writer) case socks5Version: return s.handshake5(cmd, reader, writer) default: return nil, newError("unknown Socks version: ", version) } } // ReadUsernamePassword reads Socks 5 username/password message from the given reader. // +----+------+----------+------+----------+ // |VER | ULEN | UNAME | PLEN | PASSWD | // +----+------+----------+------+----------+ // | 1 | 1 | 1 to 255 | 1 | 1 to 255 | // +----+------+----------+------+----------+ func ReadUsernamePassword(reader io.Reader) (string, string, error) { buffer := buf.StackNew() defer buffer.Release() if _, err := buffer.ReadFullFrom(reader, 2); err != nil { return "", "", err } nUsername := int32(buffer.Byte(1)) buffer.Clear() if _, err := buffer.ReadFullFrom(reader, nUsername); err != nil { return "", "", err } username := buffer.String() buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 1); err != nil { return "", "", err } nPassword := int32(buffer.Byte(0)) buffer.Clear() if _, err := buffer.ReadFullFrom(reader, nPassword); err != nil { return "", "", err } password := buffer.String() return username, password, nil } // ReadUntilNull reads content from given reader, until a null (0x00) byte. func ReadUntilNull(reader io.Reader) (string, error) { b := buf.StackNew() defer b.Release() for { _, err := b.ReadFullFrom(reader, 1) if err != nil { return "", err } if b.Byte(b.Len()-1) == 0x00 { b.Resize(0, b.Len()-1) return b.String(), nil } if b.IsFull() { return "", newError("buffer overrun") } } } func hasAuthMethod(expectedAuth byte, authCandidates []byte) bool { for _, a := range authCandidates { if a == expectedAuth { return true } } return false } func writeSocks5AuthenticationResponse(writer io.Writer, version byte, auth byte) error { return buf.WriteAllBytes(writer, []byte{version, auth}) } func writeSocks5Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error { buffer := buf.New() defer buffer.Release() common.Must2(buffer.Write([]byte{socks5Version, errCode, 0x00 /* reserved */})) if err := addrParser.WriteAddressPort(buffer, address, port); err != nil { return err } return buf.WriteAllBytes(writer, buffer.Bytes()) } func writeSocks4Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error { buffer := buf.StackNew() defer buffer.Release() common.Must(buffer.WriteByte(0x00)) common.Must(buffer.WriteByte(errCode)) portBytes := buffer.Extend(2) binary.BigEndian.PutUint16(portBytes, port.Value()) common.Must2(buffer.Write(address.IP())) return buf.WriteAllBytes(writer, buffer.Bytes()) } func DecodeUDPPacket(packet *buf.Buffer) (*protocol.RequestHeader, error) { if packet.Len() < 5 { return nil, newError("insufficient length of packet.") } request := &protocol.RequestHeader{ Version: socks5Version, Command: protocol.RequestCommandUDP, } // packet[0] and packet[1] are reserved if packet.Byte(2) != 0 /* fragments */ { return nil, newError("discarding fragmented payload.") } packet.Advance(3) addr, port, err := addrParser.ReadAddressPort(nil, packet) if err != nil { return nil, newError("failed to read UDP header").Base(err) } request.Address = addr request.Port = port return request, nil } func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer, error) { b := buf.New() common.Must2(b.Write([]byte{0, 0, 0 /* Fragment */})) if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil { b.Release() return nil, err } common.Must2(b.Write(data)) return b, nil } func EncodeUDPPacketFromAddress(address net.Destination, data []byte) (*buf.Buffer, error) { b := buf.New() common.Must2(b.Write([]byte{0, 0, 0 /* Fragment */})) if err := addrParser.WriteAddressPort(b, address.Address, address.Port); err != nil { b.Release() return nil, err } common.Must2(b.Write(data)) return b, nil } type UDPReader struct { reader io.Reader } func NewUDPReader(reader io.Reader) *UDPReader { return &UDPReader{reader: reader} } func (r *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) { b := buf.New() if _, err := b.ReadFrom(r.reader); err != nil { return nil, err } if _, err := DecodeUDPPacket(b); err != nil { return nil, err } return buf.MultiBuffer{b}, nil } func (r *UDPReader) ReadFrom(p []byte) (n int, addr gonet.Addr, err error) { buffer := buf.New() _, err = buffer.ReadFrom(r.reader) if err != nil { buffer.Release() return 0, nil, err } req, err := DecodeUDPPacket(buffer) if err != nil { buffer.Release() return 0, nil, err } n = copy(p, buffer.Bytes()) buffer.Release() return n, &gonet.UDPAddr{IP: req.Address.IP(), Port: int(req.Port)}, nil } type UDPWriter struct { request *protocol.RequestHeader writer io.Writer } func NewUDPWriter(request *protocol.RequestHeader, writer io.Writer) *UDPWriter { return &UDPWriter{ request: request, writer: writer, } } // Write implements io.Writer. func (w *UDPWriter) Write(b []byte) (int, error) { eb, err := EncodeUDPPacket(w.request, b) if err != nil { return 0, err } defer eb.Release() if _, err := w.writer.Write(eb.Bytes()); err != nil { return 0, err } return len(b), nil } func (w *UDPWriter) WriteTo(payload []byte, addr gonet.Addr) (n int, err error) { request := *w.request udpAddr := addr.(*gonet.UDPAddr) request.Command = protocol.RequestCommandUDP request.Address = net.IPAddress(udpAddr.IP) request.Port = net.Port(udpAddr.Port) packet, err := EncodeUDPPacket(&request, payload) if err != nil { return 0, err } _, err = w.writer.Write(packet.Bytes()) packet.Release() return len(payload), err } func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer io.Writer, delayAuthWrite bool) (*protocol.RequestHeader, error) { authByte := byte(authNotRequired) if request.User != nil { authByte = byte(authPassword) } b := buf.New() defer b.Release() common.Must2(b.Write([]byte{socks5Version, 0x01, authByte})) if !delayAuthWrite { if authByte == authPassword { account := request.User.Account.(*Account) common.Must(b.WriteByte(0x01)) common.Must(b.WriteByte(byte(len(account.Username)))) common.Must2(b.WriteString(account.Username)) common.Must(b.WriteByte(byte(len(account.Password)))) common.Must2(b.WriteString(account.Password)) } } if err := buf.WriteAllBytes(writer, b.Bytes()); err != nil { return nil, err } b.Clear() if _, err := b.ReadFullFrom(reader, 2); err != nil { return nil, err } if b.Byte(0) != socks5Version { return nil, newError("unexpected server version: ", b.Byte(0)).AtWarning() } if b.Byte(1) != authByte { return nil, newError("auth method not supported.").AtWarning() } if authByte == authPassword { b.Clear() if delayAuthWrite { account := request.User.Account.(*Account) common.Must(b.WriteByte(0x01)) common.Must(b.WriteByte(byte(len(account.Username)))) common.Must2(b.WriteString(account.Username)) common.Must(b.WriteByte(byte(len(account.Password)))) common.Must2(b.WriteString(account.Password)) if err := buf.WriteAllBytes(writer, b.Bytes()); err != nil { return nil, err } b.Clear() } if _, err := b.ReadFullFrom(reader, 2); err != nil { return nil, err } if b.Byte(1) != 0x00 { return nil, newError("server rejects account: ", b.Byte(1)) } } b.Clear() command := byte(cmdTCPConnect) if request.Command == protocol.RequestCommandUDP { command = byte(cmdUDPAssociate) } common.Must2(b.Write([]byte{socks5Version, command, 0x00 /* reserved */})) if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil { return nil, err } if err := buf.WriteAllBytes(writer, b.Bytes()); err != nil { return nil, err } b.Clear() if _, err := b.ReadFullFrom(reader, 3); err != nil { return nil, err } resp := b.Byte(1) if resp != 0x00 { return nil, newError("server rejects request: ", resp) } b.Clear() address, port, err := addrParser.ReadAddressPort(b, reader) if err != nil { return nil, err } if request.Command == protocol.RequestCommandUDP { udpRequest := &protocol.RequestHeader{ Version: socks5Version, Command: protocol.RequestCommandUDP, Address: address, Port: port, } return udpRequest, nil } return nil, nil } func ClientHandshake4(request *protocol.RequestHeader, reader io.Reader, writer io.Writer) error { b := buf.New() defer b.Release() common.Must2(b.Write([]byte{socks4Version, cmdTCPConnect})) portBytes := b.Extend(2) binary.BigEndian.PutUint16(portBytes, request.Port.Value()) switch request.Address.Family() { case net.AddressFamilyIPv4: common.Must2(b.Write(request.Address.IP())) case net.AddressFamilyDomain: common.Must2(b.Write([]byte{0x00, 0x00, 0x00, 0x01})) case net.AddressFamilyIPv6: return newError("ipv6 is not supported in socks4") default: panic("Unknown family type.") } if request.User != nil { account := request.User.Account.(*Account) common.Must2(b.WriteString(account.Username)) } common.Must(b.WriteByte(0x00)) if request.Address.Family() == net.AddressFamilyDomain { common.Must2(b.WriteString(request.Address.Domain())) common.Must(b.WriteByte(0x00)) } if err := buf.WriteAllBytes(writer, b.Bytes()); err != nil { return err } b.Clear() if _, err := b.ReadFullFrom(reader, 8); err != nil { return err } if b.Byte(0) != 0x00 { return newError("unexpected version of the reply code: ", b.Byte(0)) } if b.Byte(1) != socks4RequestGranted { return newError("server rejects request: ", b.Byte(1)) } return nil } ================================================ FILE: proxy/socks/protocol_test.go ================================================ package socks_test import ( "bytes" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" . "github.com/v2fly/v2ray-core/v5/proxy/socks" ) func TestUDPEncoding(t *testing.T) { b := buf.New() request := &protocol.RequestHeader{ Address: net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}), Port: 1024, } writer := &buf.SequentialWriter{Writer: NewUDPWriter(request, b)} content := []byte{'a'} payload := buf.New() payload.Write(content) common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{payload})) reader := NewUDPReader(b) decodedPayload, err := reader.ReadMultiBuffer() common.Must(err) if r := cmp.Diff(decodedPayload[0].Bytes(), content); r != "" { t.Error(r) } } func TestReadUsernamePassword(t *testing.T) { testCases := []struct { Input []byte Username string Password string Error bool }{ { Input: []byte{0x05, 0x01, 'a', 0x02, 'b', 'c'}, Username: "a", Password: "bc", }, { Input: []byte{0x05, 0x18, 'a', 0x02, 'b', 'c'}, Error: true, }, } for _, testCase := range testCases { reader := bytes.NewReader(testCase.Input) username, password, err := ReadUsernamePassword(reader) if testCase.Error { if err == nil { t.Error("for input: ", testCase.Input, " expect error, but actually nil") } } else { if err != nil { t.Error("for input: ", testCase.Input, " expect no error, but actually ", err.Error()) } if testCase.Username != username { t.Error("for input: ", testCase.Input, " expect username ", testCase.Username, " but actually ", username) } if testCase.Password != password { t.Error("for input: ", testCase.Input, " expect password ", testCase.Password, " but actually ", password) } } } } func TestReadUntilNull(t *testing.T) { testCases := []struct { Input []byte Output string Error bool }{ { Input: []byte{'a', 'b', 0x00}, Output: "ab", }, { Input: []byte{'a'}, Error: true, }, } for _, testCase := range testCases { reader := bytes.NewReader(testCase.Input) value, err := ReadUntilNull(reader) if testCase.Error { if err == nil { t.Error("for input: ", testCase.Input, " expect error, but actually nil") } } else { if err != nil { t.Error("for input: ", testCase.Input, " expect no error, but actually ", err.Error()) } if testCase.Output != value { t.Error("for input: ", testCase.Input, " expect output ", testCase.Output, " but actually ", value) } } } } func BenchmarkReadUsernamePassword(b *testing.B) { input := []byte{0x05, 0x01, 'a', 0x02, 'b', 'c'} buffer := buf.New() buffer.Write(input) b.ResetTimer() for i := 0; i < b.N; i++ { _, _, err := ReadUsernamePassword(buffer) common.Must(err) buffer.Clear() buffer.Extend(int32(len(input))) } } ================================================ FILE: proxy/socks/server.go ================================================ package socks import ( "context" "io" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" udp_proto "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) // Server is a SOCKS 5 proxy server type Server struct { config *ServerConfig policyManager policy.Manager } // NewServer creates a new Server object. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { v := core.MustFromContext(ctx) s := &Server{ config: config, policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return s, nil } func (s *Server) policy() policy.Session { config := s.config p := s.policyManager.ForLevel(config.UserLevel) if config.Timeout > 0 { features.PrintDeprecatedFeatureWarning("Socks timeout") } if config.Timeout > 0 && config.UserLevel == 0 { p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second } return p } // Network implements proxy.Inbound. func (s *Server) Network() []net.Network { list := []net.Network{net.Network_TCP} if s.config.UdpEnabled { list = append(list, net.Network_UDP) } return list } // Process implements proxy.Inbound. func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error { if inbound := session.InboundFromContext(ctx); inbound != nil { inbound.User = &protocol.MemoryUser{ Level: s.config.UserLevel, } } switch network { case net.Network_TCP: return s.processTCP(ctx, conn, dispatcher) case net.Network_UDP: return s.handleUDPPayload(ctx, conn, dispatcher) default: return newError("unknown network: ", network) } } func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error { plcy := s.policy() if err := conn.SetReadDeadline(time.Now().Add(plcy.Timeouts.Handshake)); err != nil { newError("failed to set deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } inbound := session.InboundFromContext(ctx) if inbound == nil || !inbound.Gateway.IsValid() { return newError("inbound gateway not specified") } svrSession := &ServerSession{ config: s.config, address: inbound.Gateway.Address, port: inbound.Gateway.Port, clientAddress: inbound.Source.Address, } reader := &buf.BufferedReader{Reader: buf.NewReader(conn)} request, err := svrSession.Handshake(reader, conn) if err != nil { if inbound != nil && inbound.Source.IsValid() { log.Record(&log.AccessMessage{ From: inbound.Source, To: "", Status: log.AccessRejected, Reason: err, }) } return newError("failed to read request").Base(err) } if request.User != nil { inbound.User.Email = request.User.Email } if err := conn.SetReadDeadline(time.Time{}); err != nil { newError("failed to clear deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } if request.Command == protocol.RequestCommandTCP { dest := request.Destination() newError("TCP Connect request to ", dest).WriteToLog(session.ExportIDToError(ctx)) if inbound != nil && inbound.Source.IsValid() { ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: inbound.Source, To: dest, Status: log.AccessAccepted, Reason: "", }) } dispatcher = &handshakeFinalizingDispatcher{Feature: dispatcher, delegate: dispatcher, serverSession: svrSession} return s.transport(ctx, reader, conn, dest, dispatcher) } err = svrSession.flushLastReply(true) if err != nil { return err } if request.Command == protocol.RequestCommandUDP { return s.handleUDP(conn) } return nil } // Wrapper to send final SOCKS reply only after Dispatch() returns type handshakeFinalizingDispatcher struct { features.Feature // do not inherit Dispatch() in case its signature changes delegate routing.Dispatcher serverSession *ServerSession } func (d *handshakeFinalizingDispatcher) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { link, err := d.delegate.Dispatch(ctx, dest) if err == nil { closeInDefer := true defer func() { if closeInDefer { common.Interrupt(link.Reader) common.Interrupt(link.Writer) } }() err = d.serverSession.flushLastReply(true) if err != nil { return nil, err } closeInDefer = false } else { d.serverSession.flushLastReply(false) } return link, err } func (*Server) handleUDP(c io.Reader) error { // The TCP connection closes after this method returns. We need to wait until // the client closes it. return common.Error2(io.Copy(buf.DiscardBytes, c)) } func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher routing.Dispatcher) error { ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle) plcy := s.policy() ctx = policy.ContextWithBufferPolicy(ctx, plcy.Buffer) link, err := dispatcher.Dispatch(ctx, dest) if err != nil { return err } requestDone := func() error { defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly) if err := buf.Copy(buf.NewReader(reader), link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP request").Base(err) } return nil } responseDone := func() error { defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) v2writer := buf.NewWriter(writer) if err := buf.Copy(link.Reader, v2writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP response").Base(err) } return nil } requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDonePost, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) return newError("connection ends").Base(err) } return nil } func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error { udpDispatcherConstructor := udp.NewSplitDispatcher switch s.config.PacketEncoding { case packetaddr.PacketAddrType_None: break case packetaddr.PacketAddrType_Packet: packetAddrDispatcherFactory := udp.NewPacketAddrDispatcherCreator(ctx) udpDispatcherConstructor = packetAddrDispatcherFactory.NewPacketAddrDispatcher } udpServer := udpDispatcherConstructor(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) { payload := packet.Payload newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx)) request := protocol.RequestHeaderFromContext(ctx) var packetSource net.Destination if request == nil { packetSource = packet.Source } else { packetSource = net.UDPDestination(request.Address, request.Port) } udpMessage, err := EncodeUDPPacketFromAddress(packetSource, payload.Bytes()) payload.Release() defer udpMessage.Release() if err != nil { newError("failed to write UDP response").AtWarning().Base(err).WriteToLog(session.ExportIDToError(ctx)) } conn.Write(udpMessage.Bytes()) }) if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.IsValid() { newError("client UDP connection from ", inbound.Source).WriteToLog(session.ExportIDToError(ctx)) } reader := buf.NewPacketReader(conn) for { mpayload, err := reader.ReadMultiBuffer() if err != nil { return err } for _, payload := range mpayload { request, err := DecodeUDPPacket(payload) if err != nil { newError("failed to parse UDP request").Base(err).WriteToLog(session.ExportIDToError(ctx)) payload.Release() continue } if payload.IsEmpty() { payload.Release() continue } currentPacketCtx := ctx newError("send packet to ", request.Destination(), " with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx)) if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.IsValid() { currentPacketCtx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: inbound.Source, To: request.Destination(), Status: log.AccessAccepted, Reason: "", }) } currentPacketCtx = protocol.ContextWithRequestHeader(currentPacketCtx, request) udpServer.Dispatch(currentPacketCtx, request.Destination(), payload) } } } func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) })) } ================================================ FILE: proxy/socks/simplified/config.go ================================================ package simplified import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/proxy/socks" ) func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*ServerConfig) fullServer := &socks.ServerConfig{ AuthType: socks.AuthType_NO_AUTH, Address: simplifiedServer.Address, UdpEnabled: simplifiedServer.UdpEnabled, PacketEncoding: simplifiedServer.PacketEncoding, DeferLastReply: simplifiedServer.DeferLastReply, } return common.CreateObject(ctx, fullServer) })) common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedClient := config.(*ClientConfig) fullClient := &socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: simplifiedClient.Address, Port: simplifiedClient.Port, }, }, } return common.CreateObject(ctx, fullClient) })) } ================================================ FILE: proxy/socks/simplified/config.pb.go ================================================ package simplified import ( net "github.com/v2fly/v2ray-core/v5/common/net" packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` UdpEnabled bool `protobuf:"varint,4,opt,name=udp_enabled,json=udpEnabled,proto3" json:"udp_enabled,omitempty"` PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,7,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"` DeferLastReply bool `protobuf:"varint,8,opt,name=defer_last_reply,json=deferLastReply,proto3" json:"defer_last_reply,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_socks_simplified_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_socks_simplified_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_socks_simplified_config_proto_rawDescGZIP(), []int{0} } func (x *ServerConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *ServerConfig) GetUdpEnabled() bool { if x != nil { return x.UdpEnabled } return false } func (x *ServerConfig) GetPacketEncoding() packetaddr.PacketAddrType { if x != nil { return x.PacketEncoding } return packetaddr.PacketAddrType(0) } func (x *ServerConfig) GetDeferLastReply() bool { if x != nil { return x.DeferLastReply } return false } type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_socks_simplified_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_socks_simplified_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_socks_simplified_config_proto_rawDescGZIP(), []int{1} } func (x *ClientConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *ClientConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } var File_proxy_socks_simplified_config_proto protoreflect.FileDescriptor const file_proxy_socks_simplified_config_proto_rawDesc = "" + "\n" + "#proxy/socks/simplified/config.proto\x12!v2ray.core.proxy.socks.simplified\x1a common/protoext/extensions.proto\x1a\x18common/net/address.proto\x1a\"common/net/packetaddr/config.proto\"\x80\x02\n" + "\fServerConfig\x12;\n" + "\aaddress\x18\x03 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x1f\n" + "\vudp_enabled\x18\x04 \x01(\bR\n" + "udpEnabled\x12R\n" + "\x0fpacket_encoding\x18\a \x01(\x0e2).v2ray.core.net.packetaddr.PacketAddrTypeR\x0epacketEncoding\x12(\n" + "\x10defer_last_reply\x18\b \x01(\bR\x0edeferLastReply:\x14\x82\xb5\x18\x10\n" + "\ainbound\x12\x05socks\"v\n" + "\fClientConfig\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port:\x15\x82\xb5\x18\x11\n" + "\boutbound\x12\x05socksB\x84\x01\n" + "%com.v2ray.core.proxy.socks.simplifiedP\x01Z5github.com/v2fly/v2ray-core/v5/proxy/socks/simplified\xaa\x02!V2Ray.Core.Proxy.Socks.Simplifiedb\x06proto3" var ( file_proxy_socks_simplified_config_proto_rawDescOnce sync.Once file_proxy_socks_simplified_config_proto_rawDescData []byte ) func file_proxy_socks_simplified_config_proto_rawDescGZIP() []byte { file_proxy_socks_simplified_config_proto_rawDescOnce.Do(func() { file_proxy_socks_simplified_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_socks_simplified_config_proto_rawDesc), len(file_proxy_socks_simplified_config_proto_rawDesc))) }) return file_proxy_socks_simplified_config_proto_rawDescData } var file_proxy_socks_simplified_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_socks_simplified_config_proto_goTypes = []any{ (*ServerConfig)(nil), // 0: v2ray.core.proxy.socks.simplified.ServerConfig (*ClientConfig)(nil), // 1: v2ray.core.proxy.socks.simplified.ClientConfig (*net.IPOrDomain)(nil), // 2: v2ray.core.common.net.IPOrDomain (packetaddr.PacketAddrType)(0), // 3: v2ray.core.net.packetaddr.PacketAddrType } var file_proxy_socks_simplified_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.socks.simplified.ServerConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 3, // 1: v2ray.core.proxy.socks.simplified.ServerConfig.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType 2, // 2: v2ray.core.proxy.socks.simplified.ClientConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_proxy_socks_simplified_config_proto_init() } func file_proxy_socks_simplified_config_proto_init() { if File_proxy_socks_simplified_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_socks_simplified_config_proto_rawDesc), len(file_proxy_socks_simplified_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_socks_simplified_config_proto_goTypes, DependencyIndexes: file_proxy_socks_simplified_config_proto_depIdxs, MessageInfos: file_proxy_socks_simplified_config_proto_msgTypes, }.Build() File_proxy_socks_simplified_config_proto = out.File file_proxy_socks_simplified_config_proto_goTypes = nil file_proxy_socks_simplified_config_proto_depIdxs = nil } ================================================ FILE: proxy/socks/simplified/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.socks.simplified; option csharp_namespace = "V2Ray.Core.Proxy.Socks.Simplified"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/socks/simplified"; option java_package = "com.v2ray.core.proxy.socks.simplified"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "common/net/address.proto"; import "common/net/packetaddr/config.proto"; message ServerConfig{ option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "socks"; v2ray.core.common.net.IPOrDomain address = 3; bool udp_enabled = 4; v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 7; bool defer_last_reply = 8; } message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "socks"; v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; } ================================================ FILE: proxy/socks/socks.go ================================================ // Package socks provides implements of Socks protocol 4, 4a and 5. package socks //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: proxy/trojan/client.go ================================================ package trojan import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) // Client is an inbound handler for trojan protocol type Client struct { serverPicker protocol.ServerPicker policyManager policy.Manager } // NewClient create a new trojan client. func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { serverList := protocol.NewServerList() for _, rec := range config.Server { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { return nil, newError("failed to parse server spec").Base(err) } serverList.AddServer(s) } if serverList.Size() == 0 { return nil, newError("0 server") } v := core.MustFromContext(ctx) client := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return client, nil } // Process implements OutboundHandler.Process(). func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified") } destination := outbound.Target network := destination.Network var server *protocol.ServerSpec var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { server = c.serverPicker.PickServer() rawConn, err := dialer.Dial(ctx, server.Destination()) if err != nil { return err } conn = rawConn return nil }) if err != nil { return newError("failed to find an available destination").AtWarning().Base(err) } newError("tunneling request to ", destination, " via ", server.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx)) defer conn.Close() user := server.PickUser() account, ok := user.Account.(*MemoryAccount) if !ok { return newError("user account is not valid") } sessionPolicy := c.policyManager.ForLevel(user.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil { postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) var buffer [2048]byte n, addr, err := packetConn.ReadFrom(buffer[:]) if err != nil { return newError("failed to read a packet").Base(err) } dest := net.DestinationFromAddr(addr) bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) connWriter := &ConnWriter{Writer: bufferWriter, Target: dest, Account: account} packetWriter := &PacketWriter{Writer: connWriter, Target: dest} // write some request payload to buffer if _, err := packetWriter.WriteTo(buffer[:n], addr); err != nil { return newError("failed to write a request payload").Base(err) } // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer if err = bufferWriter.SetBuffered(false); err != nil { return newError("failed to flush payload").Base(err).AtWarning() } return udp.CopyPacketConn(packetWriter, packetConn, udp.UpdateActivity(timer)) } getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) packetReader := &PacketReader{Reader: conn} packetConnectionReader := &PacketConnectionReader{reader: packetReader} return udp.CopyPacketConn(packetConn, packetConnectionReader, udp.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer)) if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) var bodyWriter buf.Writer bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) connWriter := &ConnWriter{Writer: bufferWriter, Target: destination, Account: account} if destination.Network == net.Network_UDP { bodyWriter = &PacketWriter{Writer: connWriter, Target: destination} } else { bodyWriter = connWriter } // write some request payload to buffer err = buf.CopyOnceTimeout(link.Reader, bodyWriter, proxy.FirstPayloadTimeout) switch err { case buf.ErrNotTimeoutReader, buf.ErrReadTimeout: if err := connWriter.WriteHeader(); err != nil { return newError("failed to write request header").Base(err).AtWarning() } case nil: default: return newError("failed to write a request payload").Base(err).AtWarning() } // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer if err = bufferWriter.SetBuffered(false); err != nil { return newError("failed to flush payload").Base(err).AtWarning() } if err = buf.Copy(link.Reader, bodyWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer request payload").Base(err).AtInfo() } return nil } getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) var reader buf.Reader if network == net.Network_UDP { reader = &PacketReader{ Reader: conn, } } else { reader = buf.NewReader(conn) } return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer)) if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } return nil } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewClient(ctx, config.(*ClientConfig)) })) } ================================================ FILE: proxy/trojan/config.go ================================================ package trojan import ( "crypto/sha256" "encoding/hex" "fmt" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol" ) // MemoryAccount is an account type converted from Account. type MemoryAccount struct { Password string Key []byte } // AsAccount implements protocol.AsAccount. func (a *Account) AsAccount() (protocol.Account, error) { password := a.GetPassword() key := hexSha224(password) return &MemoryAccount{ Password: password, Key: key, }, nil } // Equals implements protocol.Account.Equals(). func (a *MemoryAccount) Equals(another protocol.Account) bool { if account, ok := another.(*MemoryAccount); ok { return a.Password == account.Password } return false } func hexSha224(password string) []byte { buf := make([]byte, 56) hash := sha256.New224() common.Must2(hash.Write([]byte(password))) hex.Encode(buf, hash.Sum(nil)) return buf } func hexString(data []byte) string { str := "" for _, v := range data { str += fmt.Sprintf("%02x", v) } return str } ================================================ FILE: proxy/trojan/config.pb.go ================================================ package trojan import ( packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" protocol "github.com/v2fly/v2ray-core/v5/common/protocol" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Account struct { state protoimpl.MessageState `protogen:"open.v1"` Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Account) Reset() { *x = Account{} mi := &file_proxy_trojan_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Account) String() string { return protoimpl.X.MessageStringOf(x) } func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Account.ProtoReflect.Descriptor instead. func (*Account) Descriptor() ([]byte, []int) { return file_proxy_trojan_config_proto_rawDescGZIP(), []int{0} } func (x *Account) GetPassword() string { if x != nil { return x.Password } return "" } type Fallback struct { state protoimpl.MessageState `protogen:"open.v1"` Alpn string `protobuf:"bytes,1,opt,name=alpn,proto3" json:"alpn,omitempty"` Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` Dest string `protobuf:"bytes,4,opt,name=dest,proto3" json:"dest,omitempty"` Xver uint64 `protobuf:"varint,5,opt,name=xver,proto3" json:"xver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Fallback) Reset() { *x = Fallback{} mi := &file_proxy_trojan_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Fallback) String() string { return protoimpl.X.MessageStringOf(x) } func (*Fallback) ProtoMessage() {} func (x *Fallback) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Fallback.ProtoReflect.Descriptor instead. func (*Fallback) Descriptor() ([]byte, []int) { return file_proxy_trojan_config_proto_rawDescGZIP(), []int{1} } func (x *Fallback) GetAlpn() string { if x != nil { return x.Alpn } return "" } func (x *Fallback) GetPath() string { if x != nil { return x.Path } return "" } func (x *Fallback) GetType() string { if x != nil { return x.Type } return "" } func (x *Fallback) GetDest() string { if x != nil { return x.Dest } return "" } func (x *Fallback) GetXver() uint64 { if x != nil { return x.Xver } return 0 } type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Server []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=server,proto3" json:"server,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_trojan_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_trojan_config_proto_rawDescGZIP(), []int{2} } func (x *ClientConfig) GetServer() []*protocol.ServerEndpoint { if x != nil { return x.Server } return nil } type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` Fallbacks []*Fallback `protobuf:"bytes,3,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,4,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_trojan_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_trojan_config_proto_rawDescGZIP(), []int{3} } func (x *ServerConfig) GetUsers() []*protocol.User { if x != nil { return x.Users } return nil } func (x *ServerConfig) GetFallbacks() []*Fallback { if x != nil { return x.Fallbacks } return nil } func (x *ServerConfig) GetPacketEncoding() packetaddr.PacketAddrType { if x != nil { return x.PacketEncoding } return packetaddr.PacketAddrType(0) } var File_proxy_trojan_config_proto protoreflect.FileDescriptor const file_proxy_trojan_config_proto_rawDesc = "" + "\n" + "\x19proxy/trojan/config.proto\x12\x17v2ray.core.proxy.trojan\x1a\x1acommon/protocol/user.proto\x1a!common/protocol/server_spec.proto\x1a\"common/net/packetaddr/config.proto\"%\n" + "\aAccount\x12\x1a\n" + "\bpassword\x18\x01 \x01(\tR\bpassword\"n\n" + "\bFallback\x12\x12\n" + "\x04alpn\x18\x01 \x01(\tR\x04alpn\x12\x12\n" + "\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" + "\x04type\x18\x03 \x01(\tR\x04type\x12\x12\n" + "\x04dest\x18\x04 \x01(\tR\x04dest\x12\x12\n" + "\x04xver\x18\x05 \x01(\x04R\x04xver\"R\n" + "\fClientConfig\x12B\n" + "\x06server\x18\x01 \x03(\v2*.v2ray.core.common.protocol.ServerEndpointR\x06server\"\xdb\x01\n" + "\fServerConfig\x126\n" + "\x05users\x18\x01 \x03(\v2 .v2ray.core.common.protocol.UserR\x05users\x12?\n" + "\tfallbacks\x18\x03 \x03(\v2!.v2ray.core.proxy.trojan.FallbackR\tfallbacks\x12R\n" + "\x0fpacket_encoding\x18\x04 \x01(\x0e2).v2ray.core.net.packetaddr.PacketAddrTypeR\x0epacketEncodingBf\n" + "\x1bcom.v2ray.core.proxy.trojanP\x01Z+github.com/v2fly/v2ray-core/v5/proxy/trojan\xaa\x02\x17V2Ray.Core.Proxy.Trojanb\x06proto3" var ( file_proxy_trojan_config_proto_rawDescOnce sync.Once file_proxy_trojan_config_proto_rawDescData []byte ) func file_proxy_trojan_config_proto_rawDescGZIP() []byte { file_proxy_trojan_config_proto_rawDescOnce.Do(func() { file_proxy_trojan_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_trojan_config_proto_rawDesc), len(file_proxy_trojan_config_proto_rawDesc))) }) return file_proxy_trojan_config_proto_rawDescData } var file_proxy_trojan_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_proxy_trojan_config_proto_goTypes = []any{ (*Account)(nil), // 0: v2ray.core.proxy.trojan.Account (*Fallback)(nil), // 1: v2ray.core.proxy.trojan.Fallback (*ClientConfig)(nil), // 2: v2ray.core.proxy.trojan.ClientConfig (*ServerConfig)(nil), // 3: v2ray.core.proxy.trojan.ServerConfig (*protocol.ServerEndpoint)(nil), // 4: v2ray.core.common.protocol.ServerEndpoint (*protocol.User)(nil), // 5: v2ray.core.common.protocol.User (packetaddr.PacketAddrType)(0), // 6: v2ray.core.net.packetaddr.PacketAddrType } var file_proxy_trojan_config_proto_depIdxs = []int32{ 4, // 0: v2ray.core.proxy.trojan.ClientConfig.server:type_name -> v2ray.core.common.protocol.ServerEndpoint 5, // 1: v2ray.core.proxy.trojan.ServerConfig.users:type_name -> v2ray.core.common.protocol.User 1, // 2: v2ray.core.proxy.trojan.ServerConfig.fallbacks:type_name -> v2ray.core.proxy.trojan.Fallback 6, // 3: v2ray.core.proxy.trojan.ServerConfig.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_proxy_trojan_config_proto_init() } func file_proxy_trojan_config_proto_init() { if File_proxy_trojan_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_trojan_config_proto_rawDesc), len(file_proxy_trojan_config_proto_rawDesc)), NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_trojan_config_proto_goTypes, DependencyIndexes: file_proxy_trojan_config_proto_depIdxs, MessageInfos: file_proxy_trojan_config_proto_msgTypes, }.Build() File_proxy_trojan_config_proto = out.File file_proxy_trojan_config_proto_goTypes = nil file_proxy_trojan_config_proto_depIdxs = nil } ================================================ FILE: proxy/trojan/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.trojan; option csharp_namespace = "V2Ray.Core.Proxy.Trojan"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/trojan"; option java_package = "com.v2ray.core.proxy.trojan"; option java_multiple_files = true; import "common/protocol/user.proto"; import "common/protocol/server_spec.proto"; import "common/net/packetaddr/config.proto"; message Account { string password = 1; } message Fallback { string alpn = 1; string path = 2; string type = 3; string dest = 4; uint64 xver = 5; } message ClientConfig { repeated v2ray.core.common.protocol.ServerEndpoint server = 1; } message ServerConfig { repeated v2ray.core.common.protocol.User users = 1; repeated Fallback fallbacks = 3; v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 4; } ================================================ FILE: proxy/trojan/errors.generated.go ================================================ package trojan import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/trojan/protocol.go ================================================ package trojan import ( "encoding/binary" "io" gonet "net" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) var ( crlf = []byte{'\r', '\n'} addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6), protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain), ) ) const ( commandTCP byte = 1 commandUDP byte = 3 ) // ConnWriter is TCP Connection Writer Wrapper for trojan protocol type ConnWriter struct { io.Writer Target net.Destination Account *MemoryAccount headerSent bool } // Write implements io.Writer func (c *ConnWriter) Write(p []byte) (n int, err error) { if !c.headerSent { if err := c.writeHeader(); err != nil { return 0, newError("failed to write request header").Base(err) } } return c.Writer.Write(p) } // WriteMultiBuffer implements buf.Writer func (c *ConnWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { defer buf.ReleaseMulti(mb) for _, b := range mb { if !b.IsEmpty() { if _, err := c.Write(b.Bytes()); err != nil { return err } } } return nil } func (c *ConnWriter) WriteHeader() error { if !c.headerSent { if err := c.writeHeader(); err != nil { return err } } return nil } func (c *ConnWriter) writeHeader() error { buffer := buf.StackNew() defer buffer.Release() command := commandTCP if c.Target.Network == net.Network_UDP { command = commandUDP } if _, err := buffer.Write(c.Account.Key); err != nil { return err } if _, err := buffer.Write(crlf); err != nil { return err } if err := buffer.WriteByte(command); err != nil { return err } if err := addrParser.WriteAddressPort(&buffer, c.Target.Address, c.Target.Port); err != nil { return err } if _, err := buffer.Write(crlf); err != nil { return err } _, err := c.Writer.Write(buffer.Bytes()) if err == nil { c.headerSent = true } return err } // PacketWriter UDP Connection Writer Wrapper for trojan protocol type PacketWriter struct { io.Writer Target net.Destination } // WriteMultiBuffer implements buf.Writer func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { for _, b := range mb { if b.IsEmpty() { continue } if _, err := w.writePacket(b.Bytes(), w.Target); err != nil { buf.ReleaseMulti(mb) return err } } return nil } // WriteMultiBufferWithMetadata writes udp packet with destination specified func (w *PacketWriter) WriteMultiBufferWithMetadata(mb buf.MultiBuffer, dest net.Destination) error { for _, b := range mb { if b.IsEmpty() { continue } if _, err := w.writePacket(b.Bytes(), dest); err != nil { buf.ReleaseMulti(mb) return err } } return nil } func (w *PacketWriter) WriteTo(payload []byte, addr gonet.Addr) (int, error) { dest := net.DestinationFromAddr(addr) return w.writePacket(payload, dest) } func (w *PacketWriter) writePacket(payload []byte, dest net.Destination) (int, error) { // nolint: unparam var addrPortLen int32 switch dest.Address.Family() { case net.AddressFamilyDomain: if protocol.IsDomainTooLong(dest.Address.Domain()) { return 0, newError("Super long domain is not supported: ", dest.Address.Domain()) } addrPortLen = 1 + 1 + int32(len(dest.Address.Domain())) + 2 case net.AddressFamilyIPv4: addrPortLen = 1 + 4 + 2 case net.AddressFamilyIPv6: addrPortLen = 1 + 16 + 2 default: panic("Unknown address type.") } length := len(payload) lengthBuf := [2]byte{} binary.BigEndian.PutUint16(lengthBuf[:], uint16(length)) buffer := buf.NewWithSize(addrPortLen + 2 + 2 + int32(length)) defer buffer.Release() if err := addrParser.WriteAddressPort(buffer, dest.Address, dest.Port); err != nil { return 0, err } if _, err := buffer.Write(lengthBuf[:]); err != nil { return 0, err } if _, err := buffer.Write(crlf); err != nil { return 0, err } if _, err := buffer.Write(payload); err != nil { return 0, err } _, err := w.Write(buffer.Bytes()) if err != nil { return 0, err } return length, nil } // ConnReader is TCP Connection Reader Wrapper for trojan protocol type ConnReader struct { io.Reader Target net.Destination headerParsed bool } // ParseHeader parses the trojan protocol header func (c *ConnReader) ParseHeader() error { var crlf [2]byte var command [1]byte var hash [56]byte if _, err := io.ReadFull(c.Reader, hash[:]); err != nil { return newError("failed to read user hash").Base(err) } if _, err := io.ReadFull(c.Reader, crlf[:]); err != nil { return newError("failed to read crlf").Base(err) } if _, err := io.ReadFull(c.Reader, command[:]); err != nil { return newError("failed to read command").Base(err) } network := net.Network_TCP if command[0] == commandUDP { network = net.Network_UDP } addr, port, err := addrParser.ReadAddressPort(nil, c.Reader) if err != nil { return newError("failed to read address and port").Base(err) } c.Target = net.Destination{Network: network, Address: addr, Port: port} if _, err := io.ReadFull(c.Reader, crlf[:]); err != nil { return newError("failed to read crlf").Base(err) } c.headerParsed = true return nil } // Read implements io.Reader func (c *ConnReader) Read(p []byte) (int, error) { if !c.headerParsed { if err := c.ParseHeader(); err != nil { return 0, err } } return c.Reader.Read(p) } // ReadMultiBuffer implements buf.Reader func (c *ConnReader) ReadMultiBuffer() (buf.MultiBuffer, error) { b := buf.New() _, err := b.ReadFrom(c) return buf.MultiBuffer{b}, err } // PacketPayload combines udp payload and destination type PacketPayload struct { Target net.Destination Buffer buf.MultiBuffer } // PacketReader is UDP Connection Reader Wrapper for trojan protocol type PacketReader struct { io.Reader } // ReadMultiBuffer implements buf.Reader func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { p, err := r.ReadMultiBufferWithMetadata() if p != nil { return p.Buffer, err } return nil, err } // ReadMultiBufferWithMetadata reads udp packet with destination func (r *PacketReader) ReadMultiBufferWithMetadata() (*PacketPayload, error) { addr, port, err := addrParser.ReadAddressPort(nil, r) if err != nil { return nil, newError("failed to read address and port").Base(err) } var lengthBuf [2]byte if _, err := io.ReadFull(r, lengthBuf[:]); err != nil { return nil, newError("failed to read payload length").Base(err) } length := binary.BigEndian.Uint16(lengthBuf[:]) var crlf [2]byte if _, err := io.ReadFull(r, crlf[:]); err != nil { return nil, newError("failed to read crlf").Base(err) } dest := net.UDPDestination(addr, port) b := buf.NewWithSize(int32(length)) _, err = b.ReadFullFrom(r, int32(length)) if err != nil { return nil, newError("failed to read payload").Base(err) } return &PacketPayload{Target: dest, Buffer: buf.MultiBuffer{b}}, nil } type PacketConnectionReader struct { reader *PacketReader payload *PacketPayload } func (r *PacketConnectionReader) ReadFrom(p []byte) (n int, addr gonet.Addr, err error) { if r.payload == nil || r.payload.Buffer.IsEmpty() { r.payload, err = r.reader.ReadMultiBufferWithMetadata() if err != nil { return } } addr = &gonet.UDPAddr{ IP: r.payload.Target.Address.IP(), Port: int(r.payload.Target.Port), } r.payload.Buffer, n = buf.SplitFirstBytes(r.payload.Buffer, p) return } ================================================ FILE: proxy/trojan/protocol_test.go ================================================ package trojan_test import ( "crypto/rand" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" . "github.com/v2fly/v2ray-core/v5/proxy/trojan" ) func toAccount(a *Account) protocol.Account { account, err := a.AsAccount() common.Must(err) return account } func TestTCPRequest(t *testing.T) { user := &protocol.MemoryUser{ Email: "love@v2fly.org", Account: toAccount(&Account{ Password: "password", }), } payload := []byte("test string") data := buf.New() common.Must2(data.Write(payload)) buffer := buf.New() defer buffer.Release() destination := net.Destination{Network: net.Network_TCP, Address: net.LocalHostIP, Port: 1234} writer := &ConnWriter{Writer: buffer, Target: destination, Account: user.Account.(*MemoryAccount)} common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data})) reader := &ConnReader{Reader: buffer} common.Must(reader.ParseHeader()) if r := cmp.Diff(reader.Target, destination); r != "" { t.Error("destination: ", r) } decodedData, err := reader.ReadMultiBuffer() common.Must(err) if r := cmp.Diff(decodedData[0].Bytes(), payload); r != "" { t.Error("data: ", r) } } func TestUDPRequest(t *testing.T) { user := &protocol.MemoryUser{ Email: "love@v2fly.org", Account: toAccount(&Account{ Password: "password", }), } payload := []byte("test string") data := buf.New() common.Must2(data.Write(payload)) buffer := buf.New() defer buffer.Release() destination := net.Destination{Network: net.Network_UDP, Address: net.LocalHostIP, Port: 1234} writer := &PacketWriter{Writer: &ConnWriter{Writer: buffer, Target: destination, Account: user.Account.(*MemoryAccount)}, Target: destination} common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data})) connReader := &ConnReader{Reader: buffer} common.Must(connReader.ParseHeader()) packetReader := &PacketReader{Reader: connReader} p, err := packetReader.ReadMultiBufferWithMetadata() common.Must(err) if p.Buffer.IsEmpty() { t.Error("no request data") } if r := cmp.Diff(p.Target, destination); r != "" { t.Error("destination: ", r) } mb, decoded := buf.SplitFirst(p.Buffer) buf.ReleaseMulti(mb) if r := cmp.Diff(decoded.Bytes(), payload); r != "" { t.Error("data: ", r) } } func TestLargeUDPRequest(t *testing.T) { user := &protocol.MemoryUser{ Email: "love@v2fly.org", Account: toAccount(&Account{ Password: "password", }), } payload := make([]byte, 4096) common.Must2(rand.Read(payload)) data := buf.NewWithSize(int32(len(payload))) common.Must2(data.Write(payload)) buffer := buf.NewWithSize(2*data.Len() + 1) defer buffer.Release() destination := net.Destination{Network: net.Network_UDP, Address: net.LocalHostIP, Port: 1234} writer := &PacketWriter{Writer: &ConnWriter{Writer: buffer, Target: destination, Account: user.Account.(*MemoryAccount)}, Target: destination} common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data, data})) connReader := &ConnReader{Reader: buffer} common.Must(connReader.ParseHeader()) packetReader := &PacketReader{Reader: connReader} for i := 0; i < 2; i++ { p, err := packetReader.ReadMultiBufferWithMetadata() common.Must(err) if p.Buffer.IsEmpty() { t.Error("no request data") } if r := cmp.Diff(p.Target, destination); r != "" { t.Error("destination: ", r) } mb, decoded := buf.SplitFirst(p.Buffer) buf.ReleaseMulti(mb) if r := cmp.Diff(decoded.Bytes(), payload); r != "" { t.Error("data: ", r) } } } ================================================ FILE: proxy/trojan/server.go ================================================ package trojan import ( "context" "io" "strconv" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol" udp_proto "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) })) } // Server is an inbound connection handler that handles messages in trojan protocol. type Server struct { policyManager policy.Manager validator *Validator fallbacks map[string]map[string]*Fallback // or nil packetEncoding packetaddr.PacketAddrType } // NewServer creates a new trojan inbound handler. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { validator := new(Validator) for _, user := range config.Users { u, err := user.ToMemoryUser() if err != nil { return nil, newError("failed to get trojan user").Base(err).AtError() } if err := validator.Add(u); err != nil { return nil, newError("failed to add user").Base(err).AtError() } } v := core.MustFromContext(ctx) server := &Server{ policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), validator: validator, packetEncoding: config.PacketEncoding, } if config.Fallbacks != nil { server.fallbacks = make(map[string]map[string]*Fallback) for _, fb := range config.Fallbacks { if server.fallbacks[fb.Alpn] == nil { server.fallbacks[fb.Alpn] = make(map[string]*Fallback) } server.fallbacks[fb.Alpn][fb.Path] = fb } if server.fallbacks[""] != nil { for alpn, pfb := range server.fallbacks { if alpn != "" { // && alpn != "h2" { for path, fb := range server.fallbacks[""] { if pfb[path] == nil { pfb[path] = fb } } } } } } return server, nil } // AddUser implements proxy.UserManager.AddUser(). func (s *Server) AddUser(ctx context.Context, u *protocol.MemoryUser) error { return s.validator.Add(u) } // RemoveUser implements proxy.UserManager.RemoveUser(). func (s *Server) RemoveUser(ctx context.Context, e string) error { return s.validator.Del(e) } // Network implements proxy.Inbound.Network(). func (s *Server) Network() []net.Network { return []net.Network{net.Network_TCP, net.Network_UNIX} } // Process implements proxy.Inbound.Process(). func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error { sid := session.ExportIDToError(ctx) iConn := conn if statConn, ok := iConn.(*internet.StatCouterConnection); ok { iConn = statConn.Connection } sessionPolicy := s.policyManager.ForLevel(0) if err := conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() } first := buf.New() defer first.Release() firstLen, err := first.ReadFrom(conn) if err != nil { return newError("failed to read first request").Base(err) } newError("firstLen = ", firstLen).AtInfo().WriteToLog(sid) bufferedReader := &buf.BufferedReader{ Reader: buf.NewReader(conn), Buffer: buf.MultiBuffer{first}, } var user *protocol.MemoryUser apfb := s.fallbacks isfb := apfb != nil shouldFallback := false if firstLen < 58 || first.Byte(56) != '\r' { // invalid protocol err = newError("not trojan protocol") log.Record(&log.AccessMessage{ From: conn.RemoteAddr(), To: "", Status: log.AccessRejected, Reason: err, }) shouldFallback = true } else { user = s.validator.Get(hexString(first.BytesTo(56))) if user == nil { // invalid user, let's fallback err = newError("not a valid user") log.Record(&log.AccessMessage{ From: conn.RemoteAddr(), To: "", Status: log.AccessRejected, Reason: err, }) shouldFallback = true } } if isfb && shouldFallback { return s.fallback(ctx, sid, err, sessionPolicy, conn, iConn, apfb, first, firstLen, bufferedReader) } else if shouldFallback { return newError("invalid protocol or invalid user") } clientReader := &ConnReader{Reader: bufferedReader} if err := clientReader.ParseHeader(); err != nil { log.Record(&log.AccessMessage{ From: conn.RemoteAddr(), To: "", Status: log.AccessRejected, Reason: err, }) return newError("failed to create request from: ", conn.RemoteAddr()).Base(err) } destination := clientReader.Target if err := conn.SetReadDeadline(time.Time{}); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() } inbound := session.InboundFromContext(ctx) if inbound == nil { panic("no inbound metadata") } inbound.User = user sessionPolicy = s.policyManager.ForLevel(user.Level) if destination.Network == net.Network_UDP { // handle udp request return s.handleUDPPayload(ctx, &PacketReader{Reader: clientReader}, &PacketWriter{Writer: conn}, dispatcher) } ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: conn.RemoteAddr(), To: destination, Status: log.AccessAccepted, Reason: "", Email: user.Email, }) newError("received request for ", destination).WriteToLog(sid) return s.handleConnection(ctx, sessionPolicy, destination, clientReader, buf.NewWriter(conn), dispatcher) } func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error { udpDispatcherConstructor := udp.NewSplitDispatcher switch s.packetEncoding { case packetaddr.PacketAddrType_None: case packetaddr.PacketAddrType_Packet: packetAddrDispatcherFactory := udp.NewPacketAddrDispatcherCreator(ctx) udpDispatcherConstructor = packetAddrDispatcherFactory.NewPacketAddrDispatcher } udpServer := udpDispatcherConstructor(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) { if err := clientWriter.WriteMultiBufferWithMetadata(buf.MultiBuffer{packet.Payload}, packet.Source); err != nil { newError("failed to write response").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } }) inbound := session.InboundFromContext(ctx) user := inbound.User for { select { case <-ctx.Done(): return nil default: p, err := clientReader.ReadMultiBufferWithMetadata() if err != nil { if errors.Cause(err) != io.EOF { return newError("unexpected EOF").Base(err) } return nil } currentPacketCtx := ctx currentPacketCtx = log.ContextWithAccessMessage(currentPacketCtx, &log.AccessMessage{ From: inbound.Source, To: p.Target, Status: log.AccessAccepted, Reason: "", Email: user.Email, }) newError("tunnelling request to ", p.Target).WriteToLog(session.ExportIDToError(ctx)) for _, b := range p.Buffer { udpServer.Dispatch(currentPacketCtx, p.Target, b) } } } } func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Session, destination net.Destination, clientReader buf.Reader, clientWriter buf.Writer, dispatcher routing.Dispatcher, ) error { ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) link, err := dispatcher.Dispatch(ctx, destination) if err != nil { return newError("failed to dispatch request to ", destination).Base(err) } requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if err := buf.Copy(clientReader, link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer request").Base(err) } return nil } responseDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(link.Reader, clientWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to write response").Base(err) } return nil } requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDonePost, responseDone); err != nil { common.Must(common.Interrupt(link.Reader)) common.Must(common.Interrupt(link.Writer)) return newError("connection ends").Base(err) } return nil } func (s *Server) fallback(ctx context.Context, sid errors.ExportOption, err error, sessionPolicy policy.Session, connection internet.Connection, iConn internet.Connection, apfb map[string]map[string]*Fallback, first *buf.Buffer, firstLen int64, reader buf.Reader) error { if err := connection.SetReadDeadline(time.Time{}); err != nil { newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid) } newError("fallback starts").Base(err).AtInfo().WriteToLog(sid) alpn := "" if len(apfb) > 1 || apfb[""] == nil { if tlsConn, ok := iConn.(*tls.Conn); ok { alpn = tlsConn.ConnectionState().NegotiatedProtocol newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid) } if apfb[alpn] == nil { alpn = "" } } pfb := apfb[alpn] if pfb == nil { return newError(`failed to find the default "alpn" config`).AtWarning() } path := "" if len(pfb) > 1 || pfb[""] == nil { if firstLen >= 18 && first.Byte(4) != '*' { // not h2c firstBytes := first.Bytes() for i := 4; i <= 8; i++ { // 5 -> 9 if firstBytes[i] == '/' && firstBytes[i-1] == ' ' { search := len(firstBytes) if search > 64 { search = 64 // up to about 60 } for j := i + 1; j < search; j++ { k := firstBytes[j] if k == '\r' || k == '\n' { // avoid logging \r or \n break } if k == ' ' { path = string(firstBytes[i:j]) newError("realPath = " + path).AtInfo().WriteToLog(sid) if pfb[path] == nil { path = "" } break } } break } } } } fb := pfb[path] if fb == nil { return newError(`failed to find the default "path" config`).AtWarning() } ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) var conn net.Conn if err := retry.ExponentialBackoff(5, 100).On(func() error { var dialer net.Dialer conn, err = dialer.DialContext(ctx, fb.Type, fb.Dest) if err != nil { return err } return nil }); err != nil { return newError("failed to dial to " + fb.Dest).Base(err).AtWarning() } defer conn.Close() serverReader := buf.NewReader(conn) serverWriter := buf.NewWriter(conn) postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if fb.Xver != 0 { remoteAddr, remotePort, err := net.SplitHostPort(connection.RemoteAddr().String()) if err != nil { return err } localAddr, localPort, err := net.SplitHostPort(connection.LocalAddr().String()) if err != nil { return err } ipv4 := true for i := 0; i < len(remoteAddr); i++ { if remoteAddr[i] == ':' { ipv4 = false break } } pro := buf.New() defer pro.Release() switch fb.Xver { case 1: if ipv4 { common.Must2(pro.Write([]byte("PROXY TCP4 " + remoteAddr + " " + localAddr + " " + remotePort + " " + localPort + "\r\n"))) } else { common.Must2(pro.Write([]byte("PROXY TCP6 " + remoteAddr + " " + localAddr + " " + remotePort + " " + localPort + "\r\n"))) } case 2: common.Must2(pro.Write([]byte("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x21"))) // signature + v2 + PROXY if ipv4 { common.Must2(pro.Write([]byte("\x11\x00\x0C"))) // AF_INET + STREAM + 12 bytes common.Must2(pro.Write(net.ParseIP(remoteAddr).To4())) common.Must2(pro.Write(net.ParseIP(localAddr).To4())) } else { common.Must2(pro.Write([]byte("\x21\x00\x24"))) // AF_INET6 + STREAM + 36 bytes common.Must2(pro.Write(net.ParseIP(remoteAddr).To16())) common.Must2(pro.Write(net.ParseIP(localAddr).To16())) } p1, _ := strconv.ParseUint(remotePort, 10, 16) p2, _ := strconv.ParseUint(localPort, 10, 16) common.Must2(pro.Write([]byte{byte(p1 >> 8), byte(p1), byte(p2 >> 8), byte(p2)})) } if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pro}); err != nil { return newError("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning() } } if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to fallback request payload").Base(err).AtInfo() } return nil } writer := buf.NewWriter(connection) getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to deliver response payload").Base(err).AtInfo() } return nil } if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), task.OnSuccess(getResponse, task.Close(writer))); err != nil { common.Must(common.Interrupt(serverReader)) common.Must(common.Interrupt(serverWriter)) return newError("fallback ends").Base(err).AtInfo() } return nil } ================================================ FILE: proxy/trojan/simplified/config.go ================================================ package simplified import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/trojan" ) func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*ServerConfig) fullServer := &trojan.ServerConfig{ Users: func() (users []*protocol.User) { for _, v := range simplifiedServer.Users { account := &trojan.Account{Password: v} users = append(users, &protocol.User{ Account: serial.ToTypedMessage(account), }) } return }(), PacketEncoding: simplifiedServer.PacketEncoding, } return common.CreateObject(ctx, fullServer) })) common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedClient := config.(*ClientConfig) fullClient := &trojan.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: simplifiedClient.Address, Port: simplifiedClient.Port, User: []*protocol.User{ { Account: serial.ToTypedMessage(&trojan.Account{Password: simplifiedClient.Password}), }, }, }, }, } return common.CreateObject(ctx, fullClient) })) } ================================================ FILE: proxy/trojan/simplified/config.pb.go ================================================ package simplified import ( net "github.com/v2fly/v2ray-core/v5/common/net" packetaddr "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Users []string `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` PacketEncoding packetaddr.PacketAddrType `protobuf:"varint,2,opt,name=packet_encoding,json=packetEncoding,proto3,enum=v2ray.core.net.packetaddr.PacketAddrType" json:"packet_encoding,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_proxy_trojan_simplified_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_simplified_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_proxy_trojan_simplified_config_proto_rawDescGZIP(), []int{0} } func (x *ServerConfig) GetUsers() []string { if x != nil { return x.Users } return nil } func (x *ServerConfig) GetPacketEncoding() packetaddr.PacketAddrType { if x != nil { return x.PacketEncoding } return packetaddr.PacketAddrType(0) } type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_proxy_trojan_simplified_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_trojan_simplified_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_proxy_trojan_simplified_config_proto_rawDescGZIP(), []int{1} } func (x *ClientConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *ClientConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *ClientConfig) GetPassword() string { if x != nil { return x.Password } return "" } var File_proxy_trojan_simplified_config_proto protoreflect.FileDescriptor const file_proxy_trojan_simplified_config_proto_rawDesc = "" + "\n" + "$proxy/trojan/simplified/config.proto\x12\"v2ray.core.proxy.trojan.simplified\x1a common/protoext/extensions.proto\x1a\x18common/net/address.proto\x1a\"common/net/packetaddr/config.proto\"\x8f\x01\n" + "\fServerConfig\x12\x14\n" + "\x05users\x18\x01 \x03(\tR\x05users\x12R\n" + "\x0fpacket_encoding\x18\x02 \x01(\x0e2).v2ray.core.net.packetaddr.PacketAddrTypeR\x0epacketEncoding:\x15\x82\xb5\x18\x11\n" + "\ainbound\x12\x06trojan\"\x93\x01\n" + "\fClientConfig\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x12\x1a\n" + "\bpassword\x18\x03 \x01(\tR\bpassword:\x16\x82\xb5\x18\x12\n" + "\boutbound\x12\x06trojanB\x87\x01\n" + "&com.v2ray.core.proxy.trojan.simplifiedP\x01Z6github.com/v2fly/v2ray-core/v5/proxy/trojan/simplified\xaa\x02\"V2Ray.Core.Proxy.Trojan.Simplifiedb\x06proto3" var ( file_proxy_trojan_simplified_config_proto_rawDescOnce sync.Once file_proxy_trojan_simplified_config_proto_rawDescData []byte ) func file_proxy_trojan_simplified_config_proto_rawDescGZIP() []byte { file_proxy_trojan_simplified_config_proto_rawDescOnce.Do(func() { file_proxy_trojan_simplified_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_trojan_simplified_config_proto_rawDesc), len(file_proxy_trojan_simplified_config_proto_rawDesc))) }) return file_proxy_trojan_simplified_config_proto_rawDescData } var file_proxy_trojan_simplified_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_trojan_simplified_config_proto_goTypes = []any{ (*ServerConfig)(nil), // 0: v2ray.core.proxy.trojan.simplified.ServerConfig (*ClientConfig)(nil), // 1: v2ray.core.proxy.trojan.simplified.ClientConfig (packetaddr.PacketAddrType)(0), // 2: v2ray.core.net.packetaddr.PacketAddrType (*net.IPOrDomain)(nil), // 3: v2ray.core.common.net.IPOrDomain } var file_proxy_trojan_simplified_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.trojan.simplified.ServerConfig.packet_encoding:type_name -> v2ray.core.net.packetaddr.PacketAddrType 3, // 1: v2ray.core.proxy.trojan.simplified.ClientConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_proxy_trojan_simplified_config_proto_init() } func file_proxy_trojan_simplified_config_proto_init() { if File_proxy_trojan_simplified_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_trojan_simplified_config_proto_rawDesc), len(file_proxy_trojan_simplified_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_trojan_simplified_config_proto_goTypes, DependencyIndexes: file_proxy_trojan_simplified_config_proto_depIdxs, MessageInfos: file_proxy_trojan_simplified_config_proto_msgTypes, }.Build() File_proxy_trojan_simplified_config_proto = out.File file_proxy_trojan_simplified_config_proto_goTypes = nil file_proxy_trojan_simplified_config_proto_depIdxs = nil } ================================================ FILE: proxy/trojan/simplified/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.trojan.simplified; option csharp_namespace = "V2Ray.Core.Proxy.Trojan.Simplified"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/trojan/simplified"; option java_package = "com.v2ray.core.proxy.trojan.simplified"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "common/net/address.proto"; import "common/net/packetaddr/config.proto"; message ServerConfig{ option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "trojan"; repeated string users = 1; v2ray.core.net.packetaddr.PacketAddrType packet_encoding = 2; } message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "trojan"; v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; string password = 3; } ================================================ FILE: proxy/trojan/trojan.go ================================================ package trojan ================================================ FILE: proxy/trojan/validator.go ================================================ package trojan import ( "strings" "sync" "github.com/v2fly/v2ray-core/v5/common/protocol" ) // Validator stores valid trojan users. type Validator struct { // Considering email's usage here, map + sync.Mutex/RWMutex may have better performance. email sync.Map users sync.Map } // Add a trojan user, Email must be empty or unique. func (v *Validator) Add(u *protocol.MemoryUser) error { if u.Email != "" { _, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u) if loaded { return newError("User ", u.Email, " already exists.") } } v.users.Store(hexString(u.Account.(*MemoryAccount).Key), u) return nil } // Del a trojan user with a non-empty Email. func (v *Validator) Del(e string) error { if e == "" { return newError("Email must not be empty.") } le := strings.ToLower(e) u, _ := v.email.Load(le) if u == nil { return newError("User ", e, " not found.") } v.email.Delete(le) v.users.Delete(hexString(u.(*protocol.MemoryUser).Account.(*MemoryAccount).Key)) return nil } // Get a trojan user with hashed key, nil if user doesn't exist. func (v *Validator) Get(hash string) *protocol.MemoryUser { u, _ := v.users.Load(hash) if u != nil { return u.(*protocol.MemoryUser) } return nil } ================================================ FILE: proxy/vless/account.go ================================================ //go:build !confonly // +build !confonly package vless import ( "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/uuid" ) // AsAccount implements protocol.Account.AsAccount(). func (a *Account) AsAccount() (protocol.Account, error) { id, err := uuid.ParseString(a.Id) if err != nil { return nil, newError("failed to parse ID").Base(err).AtError() } return &MemoryAccount{ ID: protocol.NewID(id), Flow: a.Flow, // needs parser here? Encryption: a.Encryption, // needs parser here? }, nil } // MemoryAccount is an in-memory form of VLess account. type MemoryAccount struct { // ID of the account. ID *protocol.ID // Flow of the account. Flow string // Encryption of the account. Used for client connections, and only accepts "none" for now. Encryption string } // Equals implements protocol.Account.Equals(). func (a *MemoryAccount) Equals(account protocol.Account) bool { vlessAccount, ok := account.(*MemoryAccount) if !ok { return false } return a.ID.Equals(vlessAccount.ID) } ================================================ FILE: proxy/vless/account.pb.go ================================================ package vless import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Account struct { state protoimpl.MessageState `protogen:"open.v1"` // ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57". Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Flow settings. Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` // Encryption settings. Only applies to client side, and only accepts "none" for now. Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Account) Reset() { *x = Account{} mi := &file_proxy_vless_account_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Account) String() string { return protoimpl.X.MessageStringOf(x) } func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_account_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Account.ProtoReflect.Descriptor instead. func (*Account) Descriptor() ([]byte, []int) { return file_proxy_vless_account_proto_rawDescGZIP(), []int{0} } func (x *Account) GetId() string { if x != nil { return x.Id } return "" } func (x *Account) GetFlow() string { if x != nil { return x.Flow } return "" } func (x *Account) GetEncryption() string { if x != nil { return x.Encryption } return "" } var File_proxy_vless_account_proto protoreflect.FileDescriptor const file_proxy_vless_account_proto_rawDesc = "" + "\n" + "\x19proxy/vless/account.proto\x12\x16v2ray.core.proxy.vless\"M\n" + "\aAccount\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + "\x04flow\x18\x02 \x01(\tR\x04flow\x12\x1e\n" + "\n" + "encryption\x18\x03 \x01(\tR\n" + "encryptionBc\n" + "\x1acom.v2ray.core.proxy.vlessP\x01Z*github.com/v2fly/v2ray-core/v5/proxy/vless\xaa\x02\x16V2Ray.Core.Proxy.Vlessb\x06proto3" var ( file_proxy_vless_account_proto_rawDescOnce sync.Once file_proxy_vless_account_proto_rawDescData []byte ) func file_proxy_vless_account_proto_rawDescGZIP() []byte { file_proxy_vless_account_proto_rawDescOnce.Do(func() { file_proxy_vless_account_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vless_account_proto_rawDesc), len(file_proxy_vless_account_proto_rawDesc))) }) return file_proxy_vless_account_proto_rawDescData } var file_proxy_vless_account_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_vless_account_proto_goTypes = []any{ (*Account)(nil), // 0: v2ray.core.proxy.vless.Account } var file_proxy_vless_account_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_proxy_vless_account_proto_init() } func file_proxy_vless_account_proto_init() { if File_proxy_vless_account_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vless_account_proto_rawDesc), len(file_proxy_vless_account_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vless_account_proto_goTypes, DependencyIndexes: file_proxy_vless_account_proto_depIdxs, MessageInfos: file_proxy_vless_account_proto_msgTypes, }.Build() File_proxy_vless_account_proto = out.File file_proxy_vless_account_proto_goTypes = nil file_proxy_vless_account_proto_depIdxs = nil } ================================================ FILE: proxy/vless/account.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vless; option csharp_namespace = "V2Ray.Core.Proxy.Vless"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vless"; option java_package = "com.v2ray.core.proxy.vless"; option java_multiple_files = true; message Account { // ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57". string id = 1; // Flow settings. string flow = 2; // Encryption settings. Only applies to client side, and only accepts "none" for now. string encryption = 3; } ================================================ FILE: proxy/vless/encoding/addons.go ================================================ //go:build !confonly // +build !confonly package encoding import ( "io" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/protocol" ) // EncodeHeaderAddons Add addons byte to the header func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error { if err := buffer.WriteByte(0); err != nil { return newError("failed to write addons protobuf length").Base(err) } return nil } func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) { addons := new(Addons) buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 1); err != nil { return nil, newError("failed to read addons protobuf length").Base(err) } if length := int32(buffer.Byte(0)); length != 0 { buffer.Clear() if _, err := buffer.ReadFullFrom(reader, length); err != nil { return nil, newError("failed to read addons protobuf value").Base(err) } if err := proto.Unmarshal(buffer.Bytes(), addons); err != nil { return nil, newError("failed to unmarshal addons protobuf value").Base(err) } } return addons, nil } // EncodeBodyAddons returns a Writer that auto-encrypt content written by caller. func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, addons *Addons) buf.Writer { if request.Command == protocol.RequestCommandUDP { return NewMultiLengthPacketWriter(writer.(buf.Writer)) } return buf.NewWriter(writer) } // DecodeBodyAddons returns a Reader from which caller can fetch decrypted body. func DecodeBodyAddons(reader io.Reader, request *protocol.RequestHeader, addons *Addons) buf.Reader { if request.Command == protocol.RequestCommandUDP { return NewLengthPacketReader(reader) } return buf.NewReader(reader) } func NewMultiLengthPacketWriter(writer buf.Writer) *MultiLengthPacketWriter { return &MultiLengthPacketWriter{ Writer: writer, } } type MultiLengthPacketWriter struct { buf.Writer } func (w *MultiLengthPacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { defer buf.ReleaseMulti(mb) if len(mb)+1 > 64*1024*1024 { return errors.New("value too large") } sliceSize := len(mb) + 1 mb2Write := make(buf.MultiBuffer, 0, sliceSize) for _, b := range mb { length := b.Len() if length == 0 || length+2 > buf.Size { continue } eb := buf.New() if err := eb.WriteByte(byte(length >> 8)); err != nil { eb.Release() continue } if err := eb.WriteByte(byte(length)); err != nil { eb.Release() continue } if _, err := eb.Write(b.Bytes()); err != nil { eb.Release() continue } mb2Write = append(mb2Write, eb) } if mb2Write.IsEmpty() { return nil } return w.Writer.WriteMultiBuffer(mb2Write) } type LengthPacketWriter struct { io.Writer cache []byte } func (w *LengthPacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { length := mb.Len() // none of mb is nil if length == 0 { return nil } defer func() { w.cache = w.cache[:0] }() w.cache = append(w.cache, byte(length>>8), byte(length)) for i, b := range mb { w.cache = append(w.cache, b.Bytes()...) b.Release() mb[i] = nil } if _, err := w.Write(w.cache); err != nil { return newError("failed to write a packet").Base(err) } return nil } func NewLengthPacketReader(reader io.Reader) *LengthPacketReader { return &LengthPacketReader{ Reader: reader, cache: make([]byte, 2), } } type LengthPacketReader struct { io.Reader cache []byte } func (r *LengthPacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if _, err := io.ReadFull(r.Reader, r.cache); err != nil { // maybe EOF return nil, newError("failed to read packet length").Base(err) } length := int32(r.cache[0])<<8 | int32(r.cache[1]) mb := make(buf.MultiBuffer, 0, length/buf.Size+1) for length > 0 { size := length if size > buf.Size { size = buf.Size } length -= size b := buf.New() if _, err := b.ReadFullFrom(r.Reader, size); err != nil { return nil, newError("failed to read packet payload").Base(err) } mb = append(mb, b) } return mb, nil } ================================================ FILE: proxy/vless/encoding/addons.pb.go ================================================ package encoding import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Addons struct { state protoimpl.MessageState `protogen:"open.v1"` Flow string `protobuf:"bytes,1,opt,name=Flow,proto3" json:"Flow,omitempty"` Seed []byte `protobuf:"bytes,2,opt,name=Seed,proto3" json:"Seed,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Addons) Reset() { *x = Addons{} mi := &file_proxy_vless_encoding_addons_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Addons) String() string { return protoimpl.X.MessageStringOf(x) } func (*Addons) ProtoMessage() {} func (x *Addons) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_encoding_addons_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Addons.ProtoReflect.Descriptor instead. func (*Addons) Descriptor() ([]byte, []int) { return file_proxy_vless_encoding_addons_proto_rawDescGZIP(), []int{0} } func (x *Addons) GetFlow() string { if x != nil { return x.Flow } return "" } func (x *Addons) GetSeed() []byte { if x != nil { return x.Seed } return nil } var File_proxy_vless_encoding_addons_proto protoreflect.FileDescriptor const file_proxy_vless_encoding_addons_proto_rawDesc = "" + "\n" + "!proxy/vless/encoding/addons.proto\x12\x1fv2ray.core.proxy.vless.encoding\"0\n" + "\x06Addons\x12\x12\n" + "\x04Flow\x18\x01 \x01(\tR\x04Flow\x12\x12\n" + "\x04Seed\x18\x02 \x01(\fR\x04SeedB~\n" + "#com.v2ray.core.proxy.vless.encodingP\x01Z3github.com/v2fly/v2ray-core/v5/proxy/vless/encoding\xaa\x02\x1fV2Ray.Core.Proxy.Vless.Encodingb\x06proto3" var ( file_proxy_vless_encoding_addons_proto_rawDescOnce sync.Once file_proxy_vless_encoding_addons_proto_rawDescData []byte ) func file_proxy_vless_encoding_addons_proto_rawDescGZIP() []byte { file_proxy_vless_encoding_addons_proto_rawDescOnce.Do(func() { file_proxy_vless_encoding_addons_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vless_encoding_addons_proto_rawDesc), len(file_proxy_vless_encoding_addons_proto_rawDesc))) }) return file_proxy_vless_encoding_addons_proto_rawDescData } var file_proxy_vless_encoding_addons_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_vless_encoding_addons_proto_goTypes = []any{ (*Addons)(nil), // 0: v2ray.core.proxy.vless.encoding.Addons } var file_proxy_vless_encoding_addons_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_proxy_vless_encoding_addons_proto_init() } func file_proxy_vless_encoding_addons_proto_init() { if File_proxy_vless_encoding_addons_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vless_encoding_addons_proto_rawDesc), len(file_proxy_vless_encoding_addons_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vless_encoding_addons_proto_goTypes, DependencyIndexes: file_proxy_vless_encoding_addons_proto_depIdxs, MessageInfos: file_proxy_vless_encoding_addons_proto_msgTypes, }.Build() File_proxy_vless_encoding_addons_proto = out.File file_proxy_vless_encoding_addons_proto_goTypes = nil file_proxy_vless_encoding_addons_proto_depIdxs = nil } ================================================ FILE: proxy/vless/encoding/addons.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vless.encoding; option csharp_namespace = "V2Ray.Core.Proxy.Vless.Encoding"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vless/encoding"; option java_package = "com.v2ray.core.proxy.vless.encoding"; option java_multiple_files = true; message Addons { string Flow = 1; bytes Seed = 2; } ================================================ FILE: proxy/vless/encoding/encoding.go ================================================ package encoding //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "io" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/proxy/vless" ) const ( Version = byte(0) ) var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6), protocol.PortThenAddress(), ) // EncodeRequestHeader writes encoded request header into the given writer. func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons) error { buffer := buf.StackNew() defer buffer.Release() if err := buffer.WriteByte(request.Version); err != nil { return newError("failed to write request version").Base(err) } if _, err := buffer.Write(request.User.Account.(*vless.MemoryAccount).ID.Bytes()); err != nil { return newError("failed to write request user id").Base(err) } if err := EncodeHeaderAddons(&buffer, requestAddons); err != nil { return newError("failed to encode request header addons").Base(err) } if err := buffer.WriteByte(byte(request.Command)); err != nil { return newError("failed to write request command").Base(err) } if request.Command != protocol.RequestCommandMux { if err := addrParser.WriteAddressPort(&buffer, request.Address, request.Port); err != nil { return newError("failed to write request address and port").Base(err) } } if _, err := writer.Write(buffer.Bytes()); err != nil { return newError("failed to write request header").Base(err) } return nil } // DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream. func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validator *vless.Validator) (*protocol.RequestHeader, *Addons, bool, error) { buffer := buf.StackNew() defer buffer.Release() request := new(protocol.RequestHeader) if isfb { request.Version = first.Byte(0) } else { if _, err := buffer.ReadFullFrom(reader, 1); err != nil { return nil, nil, false, newError("failed to read request version").Base(err) } request.Version = buffer.Byte(0) } switch request.Version { case 0: var id [16]byte if isfb { copy(id[:], first.BytesRange(1, 17)) } else { buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 16); err != nil { return nil, nil, false, newError("failed to read request user id").Base(err) } copy(id[:], buffer.Bytes()) } if request.User = validator.Get(id); request.User == nil { return nil, nil, isfb, newError("invalid request user id") } if isfb { first.Advance(17) } requestAddons, err := DecodeHeaderAddons(&buffer, reader) if err != nil { return nil, nil, false, newError("failed to decode request header addons").Base(err) } buffer.Clear() if _, err := buffer.ReadFullFrom(reader, 1); err != nil { return nil, nil, false, newError("failed to read request command").Base(err) } request.Command = protocol.RequestCommand(buffer.Byte(0)) switch request.Command { case protocol.RequestCommandMux: request.Address = net.DomainAddress("v1.mux.cool") request.Port = 0 case protocol.RequestCommandTCP, protocol.RequestCommandUDP: if addr, port, err := addrParser.ReadAddressPort(&buffer, reader); err == nil { request.Address = addr request.Port = port } } if request.Address == nil { return nil, nil, false, newError("invalid request address") } return request, requestAddons, false, nil default: return nil, nil, isfb, newError("invalid request version") } } // EncodeResponseHeader writes encoded response header into the given writer. func EncodeResponseHeader(writer io.Writer, request *protocol.RequestHeader, responseAddons *Addons) error { buffer := buf.StackNew() defer buffer.Release() if err := buffer.WriteByte(request.Version); err != nil { return newError("failed to write response version").Base(err) } if err := EncodeHeaderAddons(&buffer, responseAddons); err != nil { return newError("failed to encode response header addons").Base(err) } if _, err := writer.Write(buffer.Bytes()); err != nil { return newError("failed to write response header").Base(err) } return nil } // DecodeResponseHeader decodes and returns (if successful) a ResponseHeader from an input stream. func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*Addons, error) { buffer := buf.StackNew() defer buffer.Release() if _, err := buffer.ReadFullFrom(reader, 1); err != nil { return nil, newError("failed to read response version").Base(err) } if buffer.Byte(0) != request.Version { return nil, newError("unexpected response version. Expecting ", int(request.Version), " but actually ", int(buffer.Byte(0))) } responseAddons, err := DecodeHeaderAddons(&buffer, reader) if err != nil { return nil, newError("failed to decode response header addons").Base(err) } return responseAddons, nil } ================================================ FILE: proxy/vless/encoding/encoding_test.go ================================================ package encoding_test import ( "testing" "github.com/google/go-cmp/cmp" "google.golang.org/protobuf/testing/protocmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/vless" . "github.com/v2fly/v2ray-core/v5/proxy/vless/encoding" ) func toAccount(a *vless.Account) protocol.Account { account, err := a.AsAccount() common.Must(err) return account } func TestRequestSerialization(t *testing.T) { user := &protocol.MemoryUser{ Level: 0, Email: "test@v2fly.org", } id := uuid.New() account := &vless.Account{ Id: id.String(), } user.Account = toAccount(account) expectedRequest := &protocol.RequestHeader{ Version: Version, User: user, Command: protocol.RequestCommandTCP, Address: net.DomainAddress("www.v2fly.org"), Port: net.Port(443), } expectedAddons := &Addons{} buffer := buf.StackNew() common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) Validator := new(vless.Validator) Validator.Add(user) actualRequest, actualAddons, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) common.Must(err) if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { t.Error(r) } if r := cmp.Diff(actualAddons, expectedAddons, protocmp.Transform()); r != "" { t.Error(r) } } func TestInvalidRequest(t *testing.T) { user := &protocol.MemoryUser{ Level: 0, Email: "test@v2fly.org", } id := uuid.New() account := &vless.Account{ Id: id.String(), } user.Account = toAccount(account) expectedRequest := &protocol.RequestHeader{ Version: Version, User: user, Command: protocol.RequestCommand(100), Address: net.DomainAddress("www.v2fly.org"), Port: net.Port(443), } expectedAddons := &Addons{} buffer := buf.StackNew() common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) Validator := new(vless.Validator) Validator.Add(user) _, _, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) if err == nil { t.Error("nil error") } } func TestMuxRequest(t *testing.T) { user := &protocol.MemoryUser{ Level: 0, Email: "test@v2fly.org", } id := uuid.New() account := &vless.Account{ Id: id.String(), } user.Account = toAccount(account) expectedRequest := &protocol.RequestHeader{ Version: Version, User: user, Command: protocol.RequestCommandMux, Address: net.DomainAddress("v1.mux.cool"), } expectedAddons := &Addons{} buffer := buf.StackNew() common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) Validator := new(vless.Validator) Validator.Add(user) actualRequest, actualAddons, _, err := DecodeRequestHeader(false, nil, &buffer, Validator) common.Must(err) if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { t.Error(r) } if r := cmp.Diff(actualAddons, expectedAddons, protocmp.Transform()); r != "" { t.Error(r) } } ================================================ FILE: proxy/vless/encoding/errors.generated.go ================================================ package encoding import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vless/errors.generated.go ================================================ package vless import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vless/inbound/config.go ================================================ package inbound ================================================ FILE: proxy/vless/inbound/config.pb.go ================================================ package inbound import ( protocol "github.com/v2fly/v2ray-core/v5/common/protocol" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Fallback struct { state protoimpl.MessageState `protogen:"open.v1"` Alpn string `protobuf:"bytes,1,opt,name=alpn,proto3" json:"alpn,omitempty"` Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` Dest string `protobuf:"bytes,4,opt,name=dest,proto3" json:"dest,omitempty"` Xver uint64 `protobuf:"varint,5,opt,name=xver,proto3" json:"xver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Fallback) Reset() { *x = Fallback{} mi := &file_proxy_vless_inbound_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Fallback) String() string { return protoimpl.X.MessageStringOf(x) } func (*Fallback) ProtoMessage() {} func (x *Fallback) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_inbound_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Fallback.ProtoReflect.Descriptor instead. func (*Fallback) Descriptor() ([]byte, []int) { return file_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{0} } func (x *Fallback) GetAlpn() string { if x != nil { return x.Alpn } return "" } func (x *Fallback) GetPath() string { if x != nil { return x.Path } return "" } func (x *Fallback) GetType() string { if x != nil { return x.Type } return "" } func (x *Fallback) GetDest() string { if x != nil { return x.Dest } return "" } func (x *Fallback) GetXver() uint64 { if x != nil { return x.Xver } return 0 } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Clients []*protocol.User `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` // Decryption settings. Only applies to server side, and only accepts "none" // for now. Decryption string `protobuf:"bytes,2,opt,name=decryption,proto3" json:"decryption,omitempty"` Fallbacks []*Fallback `protobuf:"bytes,3,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_vless_inbound_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_inbound_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetClients() []*protocol.User { if x != nil { return x.Clients } return nil } func (x *Config) GetDecryption() string { if x != nil { return x.Decryption } return "" } func (x *Config) GetFallbacks() []*Fallback { if x != nil { return x.Fallbacks } return nil } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Users []string `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_proxy_vless_inbound_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_inbound_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{2} } func (x *SimplifiedConfig) GetUsers() []string { if x != nil { return x.Users } return nil } var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor const file_proxy_vless_inbound_config_proto_rawDesc = "" + "\n" + " proxy/vless/inbound/config.proto\x12\x1ev2ray.core.proxy.vless.inbound\x1a\x1acommon/protocol/user.proto\x1a common/protoext/extensions.proto\"n\n" + "\bFallback\x12\x12\n" + "\x04alpn\x18\x01 \x01(\tR\x04alpn\x12\x12\n" + "\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" + "\x04type\x18\x03 \x01(\tR\x04type\x12\x12\n" + "\x04dest\x18\x04 \x01(\tR\x04dest\x12\x12\n" + "\x04xver\x18\x05 \x01(\x04R\x04xver\"\xac\x01\n" + "\x06Config\x12:\n" + "\aclients\x18\x01 \x03(\v2 .v2ray.core.common.protocol.UserR\aclients\x12\x1e\n" + "\n" + "decryption\x18\x02 \x01(\tR\n" + "decryption\x12F\n" + "\tfallbacks\x18\x03 \x03(\v2(.v2ray.core.proxy.vless.inbound.FallbackR\tfallbacks\">\n" + "\x10SimplifiedConfig\x12\x14\n" + "\x05users\x18\x01 \x03(\tR\x05users:\x14\x82\xb5\x18\x10\n" + "\ainbound\x12\x05vlessB{\n" + "\"com.v2ray.core.proxy.vless.inboundP\x01Z2github.com/v2fly/v2ray-core/v5/proxy/vless/inbound\xaa\x02\x1eV2Ray.Core.Proxy.Vless.Inboundb\x06proto3" var ( file_proxy_vless_inbound_config_proto_rawDescOnce sync.Once file_proxy_vless_inbound_config_proto_rawDescData []byte ) func file_proxy_vless_inbound_config_proto_rawDescGZIP() []byte { file_proxy_vless_inbound_config_proto_rawDescOnce.Do(func() { file_proxy_vless_inbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vless_inbound_config_proto_rawDesc), len(file_proxy_vless_inbound_config_proto_rawDesc))) }) return file_proxy_vless_inbound_config_proto_rawDescData } var file_proxy_vless_inbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_proxy_vless_inbound_config_proto_goTypes = []any{ (*Fallback)(nil), // 0: v2ray.core.proxy.vless.inbound.Fallback (*Config)(nil), // 1: v2ray.core.proxy.vless.inbound.Config (*SimplifiedConfig)(nil), // 2: v2ray.core.proxy.vless.inbound.SimplifiedConfig (*protocol.User)(nil), // 3: v2ray.core.common.protocol.User } var file_proxy_vless_inbound_config_proto_depIdxs = []int32{ 3, // 0: v2ray.core.proxy.vless.inbound.Config.clients:type_name -> v2ray.core.common.protocol.User 0, // 1: v2ray.core.proxy.vless.inbound.Config.fallbacks:type_name -> v2ray.core.proxy.vless.inbound.Fallback 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_proxy_vless_inbound_config_proto_init() } func file_proxy_vless_inbound_config_proto_init() { if File_proxy_vless_inbound_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vless_inbound_config_proto_rawDesc), len(file_proxy_vless_inbound_config_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vless_inbound_config_proto_goTypes, DependencyIndexes: file_proxy_vless_inbound_config_proto_depIdxs, MessageInfos: file_proxy_vless_inbound_config_proto_msgTypes, }.Build() File_proxy_vless_inbound_config_proto = out.File file_proxy_vless_inbound_config_proto_goTypes = nil file_proxy_vless_inbound_config_proto_depIdxs = nil } ================================================ FILE: proxy/vless/inbound/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vless.inbound; option csharp_namespace = "V2Ray.Core.Proxy.Vless.Inbound"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vless/inbound"; option java_package = "com.v2ray.core.proxy.vless.inbound"; option java_multiple_files = true; import "common/protocol/user.proto"; import "common/protoext/extensions.proto"; message Fallback { string alpn = 1; string path = 2; string type = 3; string dest = 4; uint64 xver = 5; } message Config { repeated v2ray.core.common.protocol.User clients = 1; // Decryption settings. Only applies to server side, and only accepts "none" // for now. string decryption = 2; repeated Fallback fallbacks = 3; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "vless"; repeated string users = 1; } ================================================ FILE: proxy/vless/inbound/errors.generated.go ================================================ package inbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vless/inbound/inbound.go ================================================ package inbound //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "io" "strconv" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/dns" feature_inbound "github.com/v2fly/v2ray-core/v5/features/inbound" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/proxy/vless" "github.com/v2fly/v2ray-core/v5/proxy/vless/encoding" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { var dc dns.Client if err := core.RequireFeatures(ctx, func(d dns.Client) error { dc = d return nil }); err != nil { return nil, err } return New(ctx, config.(*Config), dc) })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*SimplifiedConfig) fullConfig := &Config{ Clients: func() (users []*protocol.User) { for _, v := range simplifiedServer.Users { account := &vless.Account{Id: v} users = append(users, &protocol.User{ Account: serial.ToTypedMessage(account), }) } return }(), Decryption: "none", } return common.CreateObject(ctx, fullConfig) })) } // Handler is an inbound connection handler that handles messages in VLess protocol. type Handler struct { inboundHandlerManager feature_inbound.Manager policyManager policy.Manager validator *vless.Validator dns dns.Client fallbacks map[string]map[string]*Fallback // or nil // regexps map[string]*regexp.Regexp // or nil } // New creates a new VLess inbound handler. func New(ctx context.Context, config *Config, dc dns.Client) (*Handler, error) { v := core.MustFromContext(ctx) handler := &Handler{ inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), validator: new(vless.Validator), dns: dc, } for _, user := range config.Clients { u, err := user.ToMemoryUser() if err != nil { return nil, newError("failed to get VLESS user").Base(err).AtError() } if err := handler.AddUser(ctx, u); err != nil { return nil, newError("failed to initiate user").Base(err).AtError() } } if config.Fallbacks != nil { handler.fallbacks = make(map[string]map[string]*Fallback) // handler.regexps = make(map[string]*regexp.Regexp) for _, fb := range config.Fallbacks { if handler.fallbacks[fb.Alpn] == nil { handler.fallbacks[fb.Alpn] = make(map[string]*Fallback) } handler.fallbacks[fb.Alpn][fb.Path] = fb /* if fb.Path != "" { if r, err := regexp.Compile(fb.Path); err != nil { return nil, newError("invalid path regexp").Base(err).AtError() } else { handler.regexps[fb.Path] = r } } */ } if handler.fallbacks[""] != nil { for alpn, pfb := range handler.fallbacks { if alpn != "" { // && alpn != "h2" { for path, fb := range handler.fallbacks[""] { if pfb[path] == nil { pfb[path] = fb } } } } } } return handler, nil } // Close implements common.Closable.Close(). func (h *Handler) Close() error { return errors.Combine(common.Close(h.validator)) } // AddUser implements proxy.UserManager.AddUser(). func (h *Handler) AddUser(ctx context.Context, u *protocol.MemoryUser) error { return h.validator.Add(u) } // RemoveUser implements proxy.UserManager.RemoveUser(). func (h *Handler) RemoveUser(ctx context.Context, e string) error { return h.validator.Del(e) } // Network implements proxy.Inbound.Network(). func (*Handler) Network() []net.Network { return []net.Network{net.Network_TCP, net.Network_UNIX} } // Process implements proxy.Inbound.Process(). func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher routing.Dispatcher) error { sid := session.ExportIDToError(ctx) iConn := connection statConn, ok := iConn.(*internet.StatCouterConnection) if ok { iConn = statConn.Connection } sessionPolicy := h.policyManager.ForLevel(0) if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() } first := buf.New() defer first.Release() firstLen, _ := first.ReadFrom(connection) newError("firstLen = ", firstLen).AtInfo().WriteToLog(sid) reader := &buf.BufferedReader{ Reader: buf.NewReader(connection), Buffer: buf.MultiBuffer{first}, } var request *protocol.RequestHeader var requestAddons *encoding.Addons var err error apfb := h.fallbacks isfb := apfb != nil if isfb && firstLen < 18 { err = newError("fallback directly") } else { request, requestAddons, isfb, err = encoding.DecodeRequestHeader(isfb, first, reader, h.validator) } if err != nil { if isfb { if err := connection.SetReadDeadline(time.Time{}); err != nil { newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid) } newError("fallback starts").Base(err).AtInfo().WriteToLog(sid) alpn := "" if len(apfb) > 1 || apfb[""] == nil { if tlsConn, ok := iConn.(*tls.Conn); ok { alpn = tlsConn.ConnectionState().NegotiatedProtocol newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid) } if apfb[alpn] == nil { alpn = "" } } pfb := apfb[alpn] if pfb == nil { return newError(`failed to find the default "alpn" config`).AtWarning() } path := "" if len(pfb) > 1 || pfb[""] == nil { /* if lines := bytes.Split(firstBytes, []byte{'\r', '\n'}); len(lines) > 1 { if s := bytes.Split(lines[0], []byte{' '}); len(s) == 3 { if len(s[0]) < 8 && len(s[1]) > 0 && len(s[2]) == 8 { newError("realPath = " + string(s[1])).AtInfo().WriteToLog(sid) for _, fb := range pfb { if fb.Path != "" && h.regexps[fb.Path].Match(s[1]) { path = fb.Path break } } } } } */ if firstLen >= 18 && first.Byte(4) != '*' { // not h2c firstBytes := first.Bytes() for i := 4; i <= 8; i++ { // 5 -> 9 if firstBytes[i] == '/' && firstBytes[i-1] == ' ' { search := len(firstBytes) if search > 64 { search = 64 // up to about 60 } for j := i + 1; j < search; j++ { k := firstBytes[j] if k == '\r' || k == '\n' { // avoid logging \r or \n break } if k == ' ' { path = string(firstBytes[i:j]) newError("realPath = " + path).AtInfo().WriteToLog(sid) if pfb[path] == nil { path = "" } break } } break } } } } fb := pfb[path] if fb == nil { return newError(`failed to find the default "path" config`).AtWarning() } ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) var conn net.Conn if err := retry.ExponentialBackoff(5, 100).On(func() error { var dialer net.Dialer conn, err = dialer.DialContext(ctx, fb.Type, fb.Dest) if err != nil { return err } return nil }); err != nil { return newError("failed to dial to " + fb.Dest).Base(err).AtWarning() } defer conn.Close() serverReader := buf.NewReader(conn) serverWriter := buf.NewWriter(conn) postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) if fb.Xver != 0 { remoteAddr, remotePort, err := net.SplitHostPort(connection.RemoteAddr().String()) if err != nil { return err } localAddr, localPort, err := net.SplitHostPort(connection.LocalAddr().String()) if err != nil { return err } ipv4 := true for i := 0; i < len(remoteAddr); i++ { if remoteAddr[i] == ':' { ipv4 = false break } } pro := buf.New() defer pro.Release() switch fb.Xver { case 1: if ipv4 { pro.Write([]byte("PROXY TCP4 " + remoteAddr + " " + localAddr + " " + remotePort + " " + localPort + "\r\n")) } else { pro.Write([]byte("PROXY TCP6 " + remoteAddr + " " + localAddr + " " + remotePort + " " + localPort + "\r\n")) } case 2: pro.Write([]byte("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x21")) // signature + v2 + PROXY if ipv4 { pro.Write([]byte("\x11\x00\x0C")) // AF_INET + STREAM + 12 bytes pro.Write(net.ParseIP(remoteAddr).To4()) pro.Write(net.ParseIP(localAddr).To4()) } else { pro.Write([]byte("\x21\x00\x24")) // AF_INET6 + STREAM + 36 bytes pro.Write(net.ParseIP(remoteAddr).To16()) pro.Write(net.ParseIP(localAddr).To16()) } p1, _ := strconv.ParseUint(remotePort, 10, 16) p2, _ := strconv.ParseUint(localPort, 10, 16) pro.Write([]byte{byte(p1 >> 8), byte(p1), byte(p2 >> 8), byte(p2)}) } if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pro}); err != nil { return newError("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning() } } if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to fallback request payload").Base(err).AtInfo() } return nil } writer := buf.NewWriter(connection) getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to deliver response payload").Base(err).AtInfo() } return nil } if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), task.OnSuccess(getResponse, task.Close(writer))); err != nil { common.Interrupt(serverReader) common.Interrupt(serverWriter) return newError("fallback ends").Base(err).AtInfo() } return nil } if errors.Cause(err) != io.EOF { log.Record(&log.AccessMessage{ From: connection.RemoteAddr(), To: "", Status: log.AccessRejected, Reason: err, }) err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo() } return err } if err := connection.SetReadDeadline(time.Time{}); err != nil { newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid) } newError("received request for ", request.Destination()).AtInfo().WriteToLog(sid) inbound := session.InboundFromContext(ctx) if inbound == nil { panic("no inbound metadata") } inbound.User = request.User responseAddons := &encoding.Addons{} if request.Command != protocol.RequestCommandMux { ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: connection.RemoteAddr(), To: request.Destination(), Status: log.AccessAccepted, Reason: "", Email: request.User.Email, }) } sessionPolicy = h.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) link, err := dispatcher.Dispatch(ctx, request.Destination()) if err != nil { return newError("failed to dispatch request to ", request.Destination()).Base(err).AtWarning() } serverReader := link.Reader // .(*pipe.Reader) serverWriter := link.Writer // .(*pipe.Writer) postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) // default: clientReader := reader clientReader := encoding.DecodeBodyAddons(reader, request, requestAddons) // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer request payload").Base(err).AtInfo() } return nil } getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) bufferWriter := buf.NewBufferedWriter(buf.NewWriter(connection)) if err := encoding.EncodeResponseHeader(bufferWriter, request, responseAddons); err != nil { return newError("failed to encode response header").Base(err).AtWarning() } // default: clientWriter := bufferWriter clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons) { multiBuffer, err := serverReader.ReadMultiBuffer() if err != nil { return err // ... } if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil { return err // ... } } // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer if err := bufferWriter.SetBuffered(false); err != nil { return newError("failed to write A response payload").Base(err).AtWarning() } // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer response payload").Base(err).AtInfo() } return nil } if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), getResponse); err != nil { common.Interrupt(serverReader) common.Interrupt(serverWriter) return newError("connection ends").Base(err).AtInfo() } return nil } ================================================ FILE: proxy/vless/outbound/config.go ================================================ package outbound ================================================ FILE: proxy/vless/outbound/config.pb.go ================================================ package outbound import ( net "github.com/v2fly/v2ray-core/v5/common/net" protocol "github.com/v2fly/v2ray-core/v5/common/protocol" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Vnext []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=vnext,proto3" json:"vnext,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_vless_outbound_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_outbound_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_vless_outbound_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetVnext() []*protocol.ServerEndpoint { if x != nil { return x.Vnext } return nil } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Uuid string `protobuf:"bytes,3,opt,name=uuid,proto3" json:"uuid,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_proxy_vless_outbound_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vless_outbound_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_proxy_vless_outbound_config_proto_rawDescGZIP(), []int{1} } func (x *SimplifiedConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *SimplifiedConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *SimplifiedConfig) GetUuid() string { if x != nil { return x.Uuid } return "" } var File_proxy_vless_outbound_config_proto protoreflect.FileDescriptor const file_proxy_vless_outbound_config_proto_rawDesc = "" + "\n" + "!proxy/vless/outbound/config.proto\x12\x1fv2ray.core.proxy.vless.outbound\x1a!common/protocol/server_spec.proto\x1a\x18common/net/address.proto\x1a common/protoext/extensions.proto\"J\n" + "\x06Config\x12@\n" + "\x05vnext\x18\x01 \x03(\v2*.v2ray.core.common.protocol.ServerEndpointR\x05vnext\"\x8e\x01\n" + "\x10SimplifiedConfig\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x12\x12\n" + "\x04uuid\x18\x03 \x01(\tR\x04uuid:\x15\x82\xb5\x18\x11\n" + "\boutbound\x12\x05vlessB~\n" + "#com.v2ray.core.proxy.vless.outboundP\x01Z3github.com/v2fly/v2ray-core/v5/proxy/vless/outbound\xaa\x02\x1fV2Ray.Core.Proxy.Vless.Outboundb\x06proto3" var ( file_proxy_vless_outbound_config_proto_rawDescOnce sync.Once file_proxy_vless_outbound_config_proto_rawDescData []byte ) func file_proxy_vless_outbound_config_proto_rawDescGZIP() []byte { file_proxy_vless_outbound_config_proto_rawDescOnce.Do(func() { file_proxy_vless_outbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vless_outbound_config_proto_rawDesc), len(file_proxy_vless_outbound_config_proto_rawDesc))) }) return file_proxy_vless_outbound_config_proto_rawDescData } var file_proxy_vless_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_vless_outbound_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.proxy.vless.outbound.Config (*SimplifiedConfig)(nil), // 1: v2ray.core.proxy.vless.outbound.SimplifiedConfig (*protocol.ServerEndpoint)(nil), // 2: v2ray.core.common.protocol.ServerEndpoint (*net.IPOrDomain)(nil), // 3: v2ray.core.common.net.IPOrDomain } var file_proxy_vless_outbound_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.vless.outbound.Config.vnext:type_name -> v2ray.core.common.protocol.ServerEndpoint 3, // 1: v2ray.core.proxy.vless.outbound.SimplifiedConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_proxy_vless_outbound_config_proto_init() } func file_proxy_vless_outbound_config_proto_init() { if File_proxy_vless_outbound_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vless_outbound_config_proto_rawDesc), len(file_proxy_vless_outbound_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vless_outbound_config_proto_goTypes, DependencyIndexes: file_proxy_vless_outbound_config_proto_depIdxs, MessageInfos: file_proxy_vless_outbound_config_proto_msgTypes, }.Build() File_proxy_vless_outbound_config_proto = out.File file_proxy_vless_outbound_config_proto_goTypes = nil file_proxy_vless_outbound_config_proto_depIdxs = nil } ================================================ FILE: proxy/vless/outbound/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vless.outbound; option csharp_namespace = "V2Ray.Core.Proxy.Vless.Outbound"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vless/outbound"; option java_package = "com.v2ray.core.proxy.vless.outbound"; option java_multiple_files = true; import "common/protocol/server_spec.proto"; import "common/net/address.proto"; import "common/protoext/extensions.proto"; message Config { repeated v2ray.core.common.protocol.ServerEndpoint vnext = 1; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "vless"; v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; string uuid = 3; } ================================================ FILE: proxy/vless/outbound/errors.generated.go ================================================ package outbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vless/outbound/outbound.go ================================================ package outbound //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/proxy/vless" "github.com/v2fly/v2ray-core/v5/proxy/vless/encoding" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedClient := config.(*SimplifiedConfig) fullClient := &Config{Vnext: []*protocol.ServerEndpoint{ { Address: simplifiedClient.Address, Port: simplifiedClient.Port, User: []*protocol.User{ { Account: serial.ToTypedMessage(&vless.Account{Id: simplifiedClient.Uuid, Encryption: "none"}), }, }, }, }} return common.CreateObject(ctx, fullClient) })) } // Handler is an outbound connection handler for VLess protocol. type Handler struct { serverList *protocol.ServerList serverPicker protocol.ServerPicker policyManager policy.Manager } // New creates a new VLess outbound handler. func New(ctx context.Context, config *Config) (*Handler, error) { serverList := protocol.NewServerList() for _, rec := range config.Vnext { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { return nil, newError("failed to parse server spec").Base(err).AtError() } serverList.AddServer(s) } v := core.MustFromContext(ctx) handler := &Handler{ serverList: serverList, serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return handler, nil } // Process implements proxy.Outbound.Process(). func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { var rec *protocol.ServerSpec var conn internet.Connection if err := retry.ExponentialBackoff(5, 200).On(func() error { rec = h.serverPicker.PickServer() var err error conn, err = dialer.Dial(ctx, rec.Destination()) if err != nil { return err } return nil }); err != nil { return newError("failed to find an available destination").Base(err).AtWarning() } defer conn.Close() outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified").AtError() } target := outbound.Target newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).AtInfo().WriteToLog(session.ExportIDToError(ctx)) command := protocol.RequestCommandTCP if target.Network == net.Network_UDP { command = protocol.RequestCommandUDP } if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" { command = protocol.RequestCommandMux } request := &protocol.RequestHeader{ Version: encoding.Version, User: rec.PickUser(), Command: command, Address: target.Address, Port: target.Port, } account := request.User.Account.(*vless.MemoryAccount) requestAddons := &encoding.Addons{ Flow: account.Flow, } sessionPolicy := h.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) clientReader := link.Reader // .(*pipe.Reader) clientWriter := link.Writer // .(*pipe.Writer) postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) if err := encoding.EncodeRequestHeader(bufferWriter, request, requestAddons); err != nil { return newError("failed to encode request header").Base(err).AtWarning() } // default: serverWriter := bufferWriter serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons) if err := buf.CopyOnceTimeout(clientReader, serverWriter, proxy.FirstPayloadTimeout); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { return err // ... } // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer if err := bufferWriter.SetBuffered(false); err != nil { return newError("failed to write A request payload").Base(err).AtWarning() } // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer request payload").Base(err).AtInfo() } return nil } getResponse := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) responseAddons, err := encoding.DecodeResponseHeader(conn, request) if err != nil { return newError("failed to decode response header").Base(err).AtInfo() } // default: serverReader := buf.NewReader(conn) serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons) // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer response payload").Base(err).AtInfo() } return nil } if err := task.Run(ctx, postRequest, task.OnSuccess(getResponse, task.Close(clientWriter))); err != nil { return newError("connection ends").Base(err).AtInfo() } return nil } ================================================ FILE: proxy/vless/validator.go ================================================ package vless import ( "strings" "sync" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/uuid" ) // Validator stores valid VLESS users. type Validator struct { // Considering email's usage here, map + sync.Mutex/RWMutex may have better performance. email sync.Map users sync.Map } // Add a VLESS user, Email must be empty or unique. func (v *Validator) Add(u *protocol.MemoryUser) error { if u.Email != "" { _, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u) if loaded { return newError("User ", u.Email, " already exists.") } } v.users.Store(u.Account.(*MemoryAccount).ID.UUID(), u) return nil } // Del a VLESS user with a non-empty Email. func (v *Validator) Del(e string) error { if e == "" { return newError("Email must not be empty.") } le := strings.ToLower(e) u, _ := v.email.Load(le) if u == nil { return newError("User ", e, " not found.") } v.email.Delete(le) v.users.Delete(u.(*protocol.MemoryUser).Account.(*MemoryAccount).ID.UUID()) return nil } // Get a VLESS user with UUID, nil if user doesn't exist. func (v *Validator) Get(id uuid.UUID) *protocol.MemoryUser { u, _ := v.users.Load(id) if u != nil { return u.(*protocol.MemoryUser) } return nil } ================================================ FILE: proxy/vless/vless.go ================================================ // Package vless contains the implementation of VLess protocol and transportation. // // VLess contains both inbound and outbound connections. VLess inbound is usually used on servers // together with 'freedom' to talk to final destination, while VLess outbound is usually used on // clients with 'socks' for proxying. package vless //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: proxy/vlite/inbound/config.pb.go ================================================ package inbound import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type UDPProtocolConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` ScramblePacket bool `protobuf:"varint,4,opt,name=scramble_packet,json=scramblePacket,proto3" json:"scramble_packet,omitempty"` EnableFec bool `protobuf:"varint,5,opt,name=enable_fec,json=enableFec,proto3" json:"enable_fec,omitempty"` EnableStabilization bool `protobuf:"varint,6,opt,name=enable_stabilization,json=enableStabilization,proto3" json:"enable_stabilization,omitempty"` EnableRenegotiation bool `protobuf:"varint,7,opt,name=enable_renegotiation,json=enableRenegotiation,proto3" json:"enable_renegotiation,omitempty"` HandshakeMaskingPaddingSize uint32 `protobuf:"varint,8,opt,name=handshake_masking_padding_size,json=handshakeMaskingPaddingSize,proto3" json:"handshake_masking_padding_size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UDPProtocolConfig) Reset() { *x = UDPProtocolConfig{} mi := &file_proxy_vlite_inbound_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UDPProtocolConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*UDPProtocolConfig) ProtoMessage() {} func (x *UDPProtocolConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vlite_inbound_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UDPProtocolConfig.ProtoReflect.Descriptor instead. func (*UDPProtocolConfig) Descriptor() ([]byte, []int) { return file_proxy_vlite_inbound_config_proto_rawDescGZIP(), []int{0} } func (x *UDPProtocolConfig) GetPassword() string { if x != nil { return x.Password } return "" } func (x *UDPProtocolConfig) GetScramblePacket() bool { if x != nil { return x.ScramblePacket } return false } func (x *UDPProtocolConfig) GetEnableFec() bool { if x != nil { return x.EnableFec } return false } func (x *UDPProtocolConfig) GetEnableStabilization() bool { if x != nil { return x.EnableStabilization } return false } func (x *UDPProtocolConfig) GetEnableRenegotiation() bool { if x != nil { return x.EnableRenegotiation } return false } func (x *UDPProtocolConfig) GetHandshakeMaskingPaddingSize() uint32 { if x != nil { return x.HandshakeMaskingPaddingSize } return 0 } var File_proxy_vlite_inbound_config_proto protoreflect.FileDescriptor const file_proxy_vlite_inbound_config_proto_rawDesc = "" + "\n" + " proxy/vlite/inbound/config.proto\x12\x1ev2ray.core.proxy.vlite.inbound\x1a common/protoext/extensions.proto\"\xb9\x02\n" + "\x11UDPProtocolConfig\x12\x1a\n" + "\bpassword\x18\x03 \x01(\tR\bpassword\x12'\n" + "\x0fscramble_packet\x18\x04 \x01(\bR\x0escramblePacket\x12\x1d\n" + "\n" + "enable_fec\x18\x05 \x01(\bR\tenableFec\x121\n" + "\x14enable_stabilization\x18\x06 \x01(\bR\x13enableStabilization\x121\n" + "\x14enable_renegotiation\x18\a \x01(\bR\x13enableRenegotiation\x12C\n" + "\x1ehandshake_masking_padding_size\x18\b \x01(\rR\x1bhandshakeMaskingPaddingSize:\x15\x82\xb5\x18\x11\n" + "\ainbound\x12\x06vliteuB{\n" + "\"com.v2ray.core.proxy.vlite.inboundP\x01Z2github.com/v2fly/v2ray-core/v5/proxy/vlite/inbound\xaa\x02\x1eV2Ray.Core.Proxy.Vlite.Inboundb\x06proto3" var ( file_proxy_vlite_inbound_config_proto_rawDescOnce sync.Once file_proxy_vlite_inbound_config_proto_rawDescData []byte ) func file_proxy_vlite_inbound_config_proto_rawDescGZIP() []byte { file_proxy_vlite_inbound_config_proto_rawDescOnce.Do(func() { file_proxy_vlite_inbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vlite_inbound_config_proto_rawDesc), len(file_proxy_vlite_inbound_config_proto_rawDesc))) }) return file_proxy_vlite_inbound_config_proto_rawDescData } var file_proxy_vlite_inbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_vlite_inbound_config_proto_goTypes = []any{ (*UDPProtocolConfig)(nil), // 0: v2ray.core.proxy.vlite.inbound.UDPProtocolConfig } var file_proxy_vlite_inbound_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_proxy_vlite_inbound_config_proto_init() } func file_proxy_vlite_inbound_config_proto_init() { if File_proxy_vlite_inbound_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vlite_inbound_config_proto_rawDesc), len(file_proxy_vlite_inbound_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vlite_inbound_config_proto_goTypes, DependencyIndexes: file_proxy_vlite_inbound_config_proto_depIdxs, MessageInfos: file_proxy_vlite_inbound_config_proto_msgTypes, }.Build() File_proxy_vlite_inbound_config_proto = out.File file_proxy_vlite_inbound_config_proto_goTypes = nil file_proxy_vlite_inbound_config_proto_depIdxs = nil } ================================================ FILE: proxy/vlite/inbound/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vlite.inbound; option csharp_namespace = "V2Ray.Core.Proxy.Vlite.Inbound"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vlite/inbound"; option java_package = "com.v2ray.core.proxy.vlite.inbound"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message UDPProtocolConfig { option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "vliteu"; string password = 3; bool scramble_packet = 4; bool enable_fec = 5; bool enable_stabilization = 6; bool enable_renegotiation = 7; uint32 handshake_masking_padding_size = 8; } ================================================ FILE: proxy/vlite/inbound/connAdp.go ================================================ package inbound import ( "io" "net" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/signal/done" ) func newUDPConnAdaptor(conn net.Conn, done *done.Instance) net.Conn { return &udpConnAdp{ Conn: conn, reader: buf.NewPacketReader(conn), cachedMultiBuffer: nil, finished: done, } } type udpConnAdp struct { net.Conn reader buf.Reader cachedMultiBuffer buf.MultiBuffer finished *done.Instance } func (u *udpConnAdp) Read(p []byte) (n int, err error) { if u.cachedMultiBuffer.IsEmpty() { u.cachedMultiBuffer, err = u.reader.ReadMultiBuffer() if err != nil { return 0, newError("unable to read from connection").Base(err) } } var buffer *buf.Buffer u.cachedMultiBuffer, buffer = buf.SplitFirst(u.cachedMultiBuffer) defer buffer.Release() n = copy(p, buffer.Bytes()) if n != int(buffer.Len()) { return 0, io.ErrShortBuffer } return n, nil } func (u *udpConnAdp) Close() error { u.finished.Close() return u.Conn.Close() } ================================================ FILE: proxy/vlite/inbound/errors.generated.go ================================================ package inbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vlite/inbound/inbound.go ================================================ package inbound import ( "context" "io" gonet "net" "strconv" "sync" "github.com/mustafaturan/bus" "github.com/xiaokangwang/VLite/interfaces" "github.com/xiaokangwang/VLite/interfaces/ibus" "github.com/xiaokangwang/VLite/transport" udpsctpserver "github.com/xiaokangwang/VLite/transport/packetsctp/sctprelay" "github.com/xiaokangwang/VLite/transport/packetuni/puniServer" "github.com/xiaokangwang/VLite/transport/udp/udpServer" "github.com/xiaokangwang/VLite/transport/udp/udpuni/udpunis" "github.com/xiaokangwang/VLite/transport/uni/uniserver" "github.com/xiaokangwang/VLite/workers/server" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func NewUDPInboundHandler(ctx context.Context, config *UDPProtocolConfig) (*Handler, error) { proxyEnvironment := envctx.EnvironmentFromContext(ctx).(environment.ProxyEnvironment) statusInstance, err := createStatusFromConfig(config) if err != nil { return nil, newError("unable to initialize vlite").Base(err) } proxyEnvironment.TransientStorage().Put(ctx, "status", statusInstance) return &Handler{ctx: ctx}, nil } type Handler struct { ctx context.Context } func (h *Handler) Network() []net.Network { list := []net.Network{net.Network_UDP} return list } type status struct { config *UDPProtocolConfig password []byte msgbus *bus.Bus ctx context.Context transport transport.UnderlayTransportListener access sync.Mutex } func (s *status) RelayStream(conn io.ReadWriteCloser, ctx context.Context) { //nolint:revive } func (s *status) Connection(conn gonet.Conn, connctx context.Context) context.Context { //nolint:revive,stylecheck S_S2CTraffic := make(chan server.UDPServerTxToClientTraffic, 8) //nolint:revive,stylecheck S_S2CDataTraffic := make(chan server.UDPServerTxToClientDataTraffic, 8) //nolint:revive,stylecheck S_C2STraffic := make(chan server.UDPServerRxFromClientTraffic, 8) //nolint:revive,stylecheck S_S2CTraffic2 := make(chan interfaces.TrafficWithChannelTag, 8) //nolint:revive,stylecheck S_S2CDataTraffic2 := make(chan interfaces.TrafficWithChannelTag, 8) //nolint:revive,stylecheck S_C2STraffic2 := make(chan interfaces.TrafficWithChannelTag, 8) //nolint:revive,stylecheck go func(ctx context.Context) { for { select { case data := <-S_S2CTraffic: S_S2CTraffic2 <- interfaces.TrafficWithChannelTag(data) case <-ctx.Done(): return } } }(connctx) go func(ctx context.Context) { for { select { case data := <-S_S2CDataTraffic: S_S2CDataTraffic2 <- interfaces.TrafficWithChannelTag(data) case <-ctx.Done(): return } } }(connctx) go func(ctx context.Context) { for { select { case data := <-S_C2STraffic2: S_C2STraffic <- server.UDPServerRxFromClientTraffic(data) case <-ctx.Done(): return } } }(connctx) if !s.config.EnableStabilization || !s.config.EnableRenegotiation { relay := udpsctpserver.NewPacketRelayServer(conn, S_S2CTraffic2, S_S2CDataTraffic2, S_C2STraffic2, s, s.password, connctx) udpserver := server.UDPServer(connctx, S_S2CTraffic, S_S2CDataTraffic, S_C2STraffic, relay) _ = udpserver } else { relay := puniServer.NewPacketUniServer(S_S2CTraffic2, S_S2CDataTraffic2, S_C2STraffic2, s, s.password, connctx) relay.OnAutoCarrier(conn, connctx) udpserver := server.UDPServer(connctx, S_S2CTraffic, S_S2CDataTraffic, S_C2STraffic, relay) _ = udpserver } return connctx } func createStatusFromConfig(config *UDPProtocolConfig) (*status, error) { //nolint:unparam s := &status{ctx: context.Background(), config: config} s.password = []byte(config.Password) s.msgbus = ibus.NewMessageBus() s.ctx = context.WithValue(s.ctx, interfaces.ExtraOptionsMessageBus, s.msgbus) //nolint:revive,staticcheck if config.ScramblePacket { s.ctx = context.WithValue(s.ctx, interfaces.ExtraOptionsUDPShouldMask, true) //nolint:revive,staticcheck } if s.config.EnableFec { s.ctx = context.WithValue(s.ctx, interfaces.ExtraOptionsUDPFECEnabled, true) //nolint:revive,staticcheck } s.ctx = context.WithValue(s.ctx, interfaces.ExtraOptionsUDPMask, string(s.password)) //nolint:revive,staticcheck if config.HandshakeMaskingPaddingSize != 0 { ctxv := &interfaces.ExtraOptionsUsePacketArmorValue{PacketArmorPaddingTo: int(config.HandshakeMaskingPaddingSize), UsePacketArmor: true} s.ctx = context.WithValue(s.ctx, interfaces.ExtraOptionsUsePacketArmor, ctxv) //nolint:revive,staticcheck } return s, nil } func enableInterface(s *status) error { //nolint: unparam s.transport = s if s.config.EnableStabilization { s.transport = uniserver.NewUnifiedConnectionTransportHub(s, s.ctx) } if s.config.EnableStabilization { s.transport = udpunis.NewUdpUniServer(string(s.password), s.ctx, s.transport) } return nil } func (h *Handler) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher routing.Dispatcher) error { proxyEnvironment := envctx.EnvironmentFromContext(h.ctx).(environment.ProxyEnvironment) statusInstanceIfce, err := proxyEnvironment.TransientStorage().Get(ctx, "status") if err != nil { return newError("uninitialized handler").Base(err) } statusInstance := statusInstanceIfce.(*status) err = h.ensureStarted(statusInstance) if err != nil { return newError("unable to initialize").Base(err) } finish := done.New() conn = newUDPConnAdaptor(conn, finish) var initialData [1600]byte c, err := conn.Read(initialData[:]) if err != nil { return newError("unable to read initial data").Base(err) } connID := session.IDFromContext(ctx) vconn, connctx := udpServer.PrepareIncomingUDPConnection(conn, statusInstance.ctx, initialData[:c], strconv.FormatInt(int64(connID), 10)) connctx = statusInstance.transport.Connection(vconn, connctx) if connctx == nil { return newError("invalid connection discarded") } <-finish.Wait() return nil } func (h *Handler) ensureStarted(s *status) error { s.access.Lock() defer s.access.Unlock() if s.transport == nil { err := enableInterface(s) if err != nil { return err } } return nil } func init() { common.Must(common.RegisterConfig((*UDPProtocolConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewUDPInboundHandler(ctx, config.(*UDPProtocolConfig)) })) } ================================================ FILE: proxy/vlite/outbound/config.pb.go ================================================ package outbound import ( net "github.com/v2fly/v2ray-core/v5/common/net" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type UDPProtocolConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` ScramblePacket bool `protobuf:"varint,4,opt,name=scramble_packet,json=scramblePacket,proto3" json:"scramble_packet,omitempty"` EnableFec bool `protobuf:"varint,5,opt,name=enable_fec,json=enableFec,proto3" json:"enable_fec,omitempty"` EnableStabilization bool `protobuf:"varint,6,opt,name=enable_stabilization,json=enableStabilization,proto3" json:"enable_stabilization,omitempty"` EnableRenegotiation bool `protobuf:"varint,7,opt,name=enable_renegotiation,json=enableRenegotiation,proto3" json:"enable_renegotiation,omitempty"` HandshakeMaskingPaddingSize uint32 `protobuf:"varint,8,opt,name=handshake_masking_padding_size,json=handshakeMaskingPaddingSize,proto3" json:"handshake_masking_padding_size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UDPProtocolConfig) Reset() { *x = UDPProtocolConfig{} mi := &file_proxy_vlite_outbound_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UDPProtocolConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*UDPProtocolConfig) ProtoMessage() {} func (x *UDPProtocolConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vlite_outbound_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UDPProtocolConfig.ProtoReflect.Descriptor instead. func (*UDPProtocolConfig) Descriptor() ([]byte, []int) { return file_proxy_vlite_outbound_config_proto_rawDescGZIP(), []int{0} } func (x *UDPProtocolConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *UDPProtocolConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *UDPProtocolConfig) GetPassword() string { if x != nil { return x.Password } return "" } func (x *UDPProtocolConfig) GetScramblePacket() bool { if x != nil { return x.ScramblePacket } return false } func (x *UDPProtocolConfig) GetEnableFec() bool { if x != nil { return x.EnableFec } return false } func (x *UDPProtocolConfig) GetEnableStabilization() bool { if x != nil { return x.EnableStabilization } return false } func (x *UDPProtocolConfig) GetEnableRenegotiation() bool { if x != nil { return x.EnableRenegotiation } return false } func (x *UDPProtocolConfig) GetHandshakeMaskingPaddingSize() uint32 { if x != nil { return x.HandshakeMaskingPaddingSize } return 0 } var File_proxy_vlite_outbound_config_proto protoreflect.FileDescriptor const file_proxy_vlite_outbound_config_proto_rawDesc = "" + "\n" + "!proxy/vlite/outbound/config.proto\x12\x1fv2ray.core.proxy.vlite.outbound\x1a\x18common/net/address.proto\x1a common/protoext/extensions.proto\"\x8b\x03\n" + "\x11UDPProtocolConfig\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x12\x1a\n" + "\bpassword\x18\x03 \x01(\tR\bpassword\x12'\n" + "\x0fscramble_packet\x18\x04 \x01(\bR\x0escramblePacket\x12\x1d\n" + "\n" + "enable_fec\x18\x05 \x01(\bR\tenableFec\x121\n" + "\x14enable_stabilization\x18\x06 \x01(\bR\x13enableStabilization\x121\n" + "\x14enable_renegotiation\x18\a \x01(\bR\x13enableRenegotiation\x12C\n" + "\x1ehandshake_masking_padding_size\x18\b \x01(\rR\x1bhandshakeMaskingPaddingSize:\x16\x82\xb5\x18\x12\n" + "\boutbound\x12\x06vliteuB~\n" + "#com.v2ray.core.proxy.vlite.outboundP\x01Z3github.com/v2fly/v2ray-core/v5/proxy/vlite/outbound\xaa\x02\x1fV2Ray.Core.Proxy.Vlite.Outboundb\x06proto3" var ( file_proxy_vlite_outbound_config_proto_rawDescOnce sync.Once file_proxy_vlite_outbound_config_proto_rawDescData []byte ) func file_proxy_vlite_outbound_config_proto_rawDescGZIP() []byte { file_proxy_vlite_outbound_config_proto_rawDescOnce.Do(func() { file_proxy_vlite_outbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vlite_outbound_config_proto_rawDesc), len(file_proxy_vlite_outbound_config_proto_rawDesc))) }) return file_proxy_vlite_outbound_config_proto_rawDescData } var file_proxy_vlite_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_vlite_outbound_config_proto_goTypes = []any{ (*UDPProtocolConfig)(nil), // 0: v2ray.core.proxy.vlite.outbound.UDPProtocolConfig (*net.IPOrDomain)(nil), // 1: v2ray.core.common.net.IPOrDomain } var file_proxy_vlite_outbound_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.proxy.vlite.outbound.UDPProtocolConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_proxy_vlite_outbound_config_proto_init() } func file_proxy_vlite_outbound_config_proto_init() { if File_proxy_vlite_outbound_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vlite_outbound_config_proto_rawDesc), len(file_proxy_vlite_outbound_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vlite_outbound_config_proto_goTypes, DependencyIndexes: file_proxy_vlite_outbound_config_proto_depIdxs, MessageInfos: file_proxy_vlite_outbound_config_proto_msgTypes, }.Build() File_proxy_vlite_outbound_config_proto = out.File file_proxy_vlite_outbound_config_proto_goTypes = nil file_proxy_vlite_outbound_config_proto_depIdxs = nil } ================================================ FILE: proxy/vlite/outbound/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vlite.outbound; option csharp_namespace = "V2Ray.Core.Proxy.Vlite.Outbound"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vlite/outbound"; option java_package = "com.v2ray.core.proxy.vlite.outbound"; option java_multiple_files = true; import "common/net/address.proto"; import "common/protoext/extensions.proto"; message UDPProtocolConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "vliteu"; v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; string password = 3; bool scramble_packet = 4; bool enable_fec = 5; bool enable_stabilization = 6; bool enable_renegotiation = 7; uint32 handshake_masking_padding_size = 8; } ================================================ FILE: proxy/vlite/outbound/errors.generated.go ================================================ package outbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vlite/outbound/outbound.go ================================================ package outbound import ( "context" "fmt" "sync" "time" "github.com/mustafaturan/bus" "github.com/xiaokangwang/VLite/ass/udpconn2tun" "github.com/xiaokangwang/VLite/interfaces" "github.com/xiaokangwang/VLite/interfaces/ibus" vltransport "github.com/xiaokangwang/VLite/transport" udpsctpserver "github.com/xiaokangwang/VLite/transport/packetsctp/sctprelay" "github.com/xiaokangwang/VLite/transport/packetuni/puniClient" "github.com/xiaokangwang/VLite/transport/udp/udpClient" "github.com/xiaokangwang/VLite/transport/udp/udpuni/udpunic" "github.com/xiaokangwang/VLite/transport/uni/uniclient" client2 "github.com/xiaokangwang/VLite/workers/client" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func NewUDPOutboundHandler(ctx context.Context, config *UDPProtocolConfig) (*Handler, error) { proxyEnvironment := envctx.EnvironmentFromContext(ctx).(environment.ProxyEnvironment) statusInstance, err := createStatusFromConfig(config) if err != nil { return nil, newError("unable to initialize vlite").Base(err) } proxyEnvironment.TransientStorage().Put(ctx, "status", statusInstance) return &Handler{ctx: ctx}, nil } type Handler struct { ctx context.Context } // Process implements proxy.Outbound.Process(). func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { proxyEnvironment := envctx.EnvironmentFromContext(h.ctx).(environment.ProxyEnvironment) statusInstanceIfce, err := proxyEnvironment.TransientStorage().Get(ctx, "status") if err != nil { return newError("uninitialized handler").Base(err) } statusInstance := statusInstanceIfce.(*status) err = h.ensureStarted(statusInstance) if err != nil { return newError("unable to initialize").Base(err) } connid := session.IDFromContext(ctx) outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified") } destination := outbound.Target packetConnOut := statusInstance.connAdp.DialUDP(net.UDPAddr{Port: int(connid % 65535)}) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, time.Second*600) if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil { requestDone := func() error { return udp.CopyPacketConn(packetConnOut, packetConn, udp.UpdateActivity(timer)) } responseDone := func() error { return udp.CopyPacketConn(packetConn, packetConnOut, udp.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil { return newError("connection ends").Base(err) } } return newError("unrecognized connection") } func (h *Handler) ensureStarted(s *status) error { s.access.Lock() defer s.access.Unlock() if s.TunnelRxFromTun == nil { err := enableInterface(s) if err != nil { return err } } return nil } type status struct { ctx context.Context password []byte msgbus *bus.Bus udpdialer vltransport.UnderlayTransportDialer puni *puniClient.PacketUniClient udprelay *udpsctpserver.PacketSCTPRelay udpserver *client2.UDPClientContext TunnelTxToTun chan interfaces.UDPPacket TunnelRxFromTun chan interfaces.UDPPacket connAdp *udpconn2tun.UDPConn2Tun config UDPProtocolConfig access sync.Mutex } func createStatusFromConfig(config *UDPProtocolConfig) (*status, error) { //nolint:unparam s := &status{password: []byte(config.Password)} ctx := context.Background() s.msgbus = ibus.NewMessageBus() ctx = context.WithValue(ctx, interfaces.ExtraOptionsMessageBus, s.msgbus) //nolint:revive,staticcheck ctx = context.WithValue(ctx, interfaces.ExtraOptionsDisableAutoQuitForClient, true) //nolint:revive,staticcheck if config.EnableFec { ctx = context.WithValue(ctx, interfaces.ExtraOptionsUDPFECEnabled, true) //nolint:revive,staticcheck } if config.ScramblePacket { ctx = context.WithValue(ctx, interfaces.ExtraOptionsUDPShouldMask, true) //nolint:revive,staticcheck } ctx = context.WithValue(ctx, interfaces.ExtraOptionsUDPMask, string(s.password)) //nolint:revive,staticcheck if config.HandshakeMaskingPaddingSize != 0 { ctxv := &interfaces.ExtraOptionsUsePacketArmorValue{PacketArmorPaddingTo: int(config.HandshakeMaskingPaddingSize), UsePacketArmor: true} ctx = context.WithValue(ctx, interfaces.ExtraOptionsUsePacketArmor, ctxv) //nolint:revive,staticcheck } destinationString := fmt.Sprintf("%v:%v", config.Address.AsAddress().String(), config.Port) s.udpdialer = udpClient.NewUdpClient(destinationString, ctx) if config.EnableStabilization { s.udpdialer = udpunic.NewUdpUniClient(string(s.password), ctx, s.udpdialer) s.udpdialer = uniclient.NewUnifiedConnectionClient(s.udpdialer, ctx) } s.ctx = ctx return s, nil } func enableInterface(s *status) error { conn, err, connctx := s.udpdialer.Connect(s.ctx) if err != nil { return newError("unable to connect to remote").Base(err) } C_C2STraffic := make(chan client2.UDPClientTxToServerTraffic, 8) //nolint:revive,stylecheck C_C2SDataTraffic := make(chan client2.UDPClientTxToServerDataTraffic, 8) //nolint:revive,stylecheck C_S2CTraffic := make(chan client2.UDPClientRxFromServerTraffic, 8) //nolint:revive,stylecheck C_C2STraffic2 := make(chan interfaces.TrafficWithChannelTag, 8) //nolint:revive,stylecheck C_C2SDataTraffic2 := make(chan interfaces.TrafficWithChannelTag, 8) //nolint:revive,stylecheck C_S2CTraffic2 := make(chan interfaces.TrafficWithChannelTag, 8) //nolint:revive,stylecheck go func(ctx context.Context) { for { select { case data := <-C_C2STraffic: C_C2STraffic2 <- interfaces.TrafficWithChannelTag(data) case <-ctx.Done(): return } } }(connctx) go func(ctx context.Context) { for { select { case data := <-C_C2SDataTraffic: C_C2SDataTraffic2 <- interfaces.TrafficWithChannelTag(data) case <-ctx.Done(): return } } }(connctx) go func(ctx context.Context) { for { select { case data := <-C_S2CTraffic2: C_S2CTraffic <- client2.UDPClientRxFromServerTraffic(data) case <-ctx.Done(): return } } }(connctx) TunnelTxToTun := make(chan interfaces.UDPPacket) TunnelRxFromTun := make(chan interfaces.UDPPacket) s.TunnelTxToTun = TunnelTxToTun s.TunnelRxFromTun = TunnelRxFromTun if s.config.EnableStabilization && s.config.EnableRenegotiation { s.puni = puniClient.NewPacketUniClient(C_C2STraffic2, C_C2SDataTraffic2, C_S2CTraffic2, s.password, connctx) s.puni.OnAutoCarrier(conn, connctx) s.udpserver = client2.UDPClient(connctx, C_C2STraffic, C_C2SDataTraffic, C_S2CTraffic, TunnelTxToTun, TunnelRxFromTun, s.puni) } else { s.udprelay = udpsctpserver.NewPacketRelayClient(conn, C_C2STraffic2, C_C2SDataTraffic2, C_S2CTraffic2, s.password, connctx) s.udpserver = client2.UDPClient(connctx, C_C2STraffic, C_C2SDataTraffic, C_S2CTraffic, TunnelTxToTun, TunnelRxFromTun, s.udprelay) } s.ctx = connctx s.connAdp = udpconn2tun.NewUDPConn2Tun(TunnelTxToTun, TunnelRxFromTun) return nil } func init() { common.Must(common.RegisterConfig((*UDPProtocolConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewUDPOutboundHandler(ctx, config.(*UDPProtocolConfig)) })) } ================================================ FILE: proxy/vlite/vlite.go ================================================ // Package vlite contains the integration code for VLite protocol variety // // VLite is currently an experimental protocol, its stability is not guaranteed. package vlite ================================================ FILE: proxy/vmess/account.go ================================================ //go:build !confonly // +build !confonly package vmess import ( "strings" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/uuid" ) // MemoryAccount is an in-memory form of VMess account. type MemoryAccount struct { // ID is the main ID of the account. ID *protocol.ID // AlterIDs are the alternative IDs of the account. AlterIDs []*protocol.ID // Security type of the account. Used for client connections. Security protocol.SecurityType AuthenticatedLengthExperiment bool NoTerminationSignal bool } // AnyValidID returns an ID that is either the main ID or one of the alternative IDs if any. func (a *MemoryAccount) AnyValidID() *protocol.ID { if len(a.AlterIDs) == 0 { return a.ID } return a.AlterIDs[dice.Roll(len(a.AlterIDs))] } // Equals implements protocol.Account. func (a *MemoryAccount) Equals(account protocol.Account) bool { vmessAccount, ok := account.(*MemoryAccount) if !ok { return false } // TODO: handle AlterIds difference return a.ID.Equals(vmessAccount.ID) } // AsAccount implements protocol.Account. func (a *Account) AsAccount() (protocol.Account, error) { id, err := uuid.ParseString(a.Id) if err != nil { return nil, newError("failed to parse ID").Base(err).AtError() } protoID := protocol.NewID(id) var AuthenticatedLength, NoTerminationSignal bool if strings.Contains(a.TestsEnabled, "AuthenticatedLength") { AuthenticatedLength = true } if strings.Contains(a.TestsEnabled, "NoTerminationSignal") { NoTerminationSignal = true } return &MemoryAccount{ ID: protoID, AlterIDs: protocol.NewAlterIDs(protoID, uint16(a.AlterId)), Security: a.SecuritySettings.GetSecurityType(), AuthenticatedLengthExperiment: AuthenticatedLength, NoTerminationSignal: NoTerminationSignal, }, nil } ================================================ FILE: proxy/vmess/account.pb.go ================================================ package vmess import ( protocol "github.com/v2fly/v2ray-core/v5/common/protocol" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Account struct { state protoimpl.MessageState `protogen:"open.v1"` // ID of the account, in the form of a UUID, e.g., // "66ad4540-b58c-4ad2-9926-ea63445a9b57". Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Number of alternative IDs. Client and server must share the same number. AlterId uint32 `protobuf:"varint,2,opt,name=alter_id,json=alterId,proto3" json:"alter_id,omitempty"` // Security settings. Only applies to client side. SecuritySettings *protocol.SecurityConfig `protobuf:"bytes,3,opt,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"` // Define tests enabled for this account TestsEnabled string `protobuf:"bytes,4,opt,name=tests_enabled,json=testsEnabled,proto3" json:"tests_enabled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Account) Reset() { *x = Account{} mi := &file_proxy_vmess_account_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Account) String() string { return protoimpl.X.MessageStringOf(x) } func (*Account) ProtoMessage() {} func (x *Account) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_account_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Account.ProtoReflect.Descriptor instead. func (*Account) Descriptor() ([]byte, []int) { return file_proxy_vmess_account_proto_rawDescGZIP(), []int{0} } func (x *Account) GetId() string { if x != nil { return x.Id } return "" } func (x *Account) GetAlterId() uint32 { if x != nil { return x.AlterId } return 0 } func (x *Account) GetSecuritySettings() *protocol.SecurityConfig { if x != nil { return x.SecuritySettings } return nil } func (x *Account) GetTestsEnabled() string { if x != nil { return x.TestsEnabled } return "" } var File_proxy_vmess_account_proto protoreflect.FileDescriptor const file_proxy_vmess_account_proto_rawDesc = "" + "\n" + "\x19proxy/vmess/account.proto\x12\x16v2ray.core.proxy.vmess\x1a\x1dcommon/protocol/headers.proto\"\xb2\x01\n" + "\aAccount\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x19\n" + "\balter_id\x18\x02 \x01(\rR\aalterId\x12W\n" + "\x11security_settings\x18\x03 \x01(\v2*.v2ray.core.common.protocol.SecurityConfigR\x10securitySettings\x12#\n" + "\rtests_enabled\x18\x04 \x01(\tR\ftestsEnabledBc\n" + "\x1acom.v2ray.core.proxy.vmessP\x01Z*github.com/v2fly/v2ray-core/v5/proxy/vmess\xaa\x02\x16V2Ray.Core.Proxy.Vmessb\x06proto3" var ( file_proxy_vmess_account_proto_rawDescOnce sync.Once file_proxy_vmess_account_proto_rawDescData []byte ) func file_proxy_vmess_account_proto_rawDescGZIP() []byte { file_proxy_vmess_account_proto_rawDescOnce.Do(func() { file_proxy_vmess_account_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vmess_account_proto_rawDesc), len(file_proxy_vmess_account_proto_rawDesc))) }) return file_proxy_vmess_account_proto_rawDescData } var file_proxy_vmess_account_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_vmess_account_proto_goTypes = []any{ (*Account)(nil), // 0: v2ray.core.proxy.vmess.Account (*protocol.SecurityConfig)(nil), // 1: v2ray.core.common.protocol.SecurityConfig } var file_proxy_vmess_account_proto_depIdxs = []int32{ 1, // 0: v2ray.core.proxy.vmess.Account.security_settings:type_name -> v2ray.core.common.protocol.SecurityConfig 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_proxy_vmess_account_proto_init() } func file_proxy_vmess_account_proto_init() { if File_proxy_vmess_account_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vmess_account_proto_rawDesc), len(file_proxy_vmess_account_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vmess_account_proto_goTypes, DependencyIndexes: file_proxy_vmess_account_proto_depIdxs, MessageInfos: file_proxy_vmess_account_proto_msgTypes, }.Build() File_proxy_vmess_account_proto = out.File file_proxy_vmess_account_proto_goTypes = nil file_proxy_vmess_account_proto_depIdxs = nil } ================================================ FILE: proxy/vmess/account.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vmess; option csharp_namespace = "V2Ray.Core.Proxy.Vmess"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vmess"; option java_package = "com.v2ray.core.proxy.vmess"; option java_multiple_files = true; import "common/protocol/headers.proto"; message Account { // ID of the account, in the form of a UUID, e.g., // "66ad4540-b58c-4ad2-9926-ea63445a9b57". string id = 1; // Number of alternative IDs. Client and server must share the same number. uint32 alter_id = 2; // Security settings. Only applies to client side. v2ray.core.common.protocol.SecurityConfig security_settings = 3; // Define tests enabled for this account string tests_enabled = 4; } ================================================ FILE: proxy/vmess/aead/authid.go ================================================ package aead import ( "bytes" "crypto/aes" "crypto/cipher" rand3 "crypto/rand" "encoding/binary" "errors" "hash/crc32" "io" "math" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/antireplay" ) var ( ErrNotFound = errors.New("user do not exist") ErrReplay = errors.New("replayed request") ) func CreateAuthID(cmdKey []byte, time int64) [16]byte { buf := bytes.NewBuffer(nil) common.Must(binary.Write(buf, binary.BigEndian, time)) var zero uint32 common.Must2(io.CopyN(buf, rand3.Reader, 4)) zero = crc32.ChecksumIEEE(buf.Bytes()) common.Must(binary.Write(buf, binary.BigEndian, zero)) aesBlock := NewCipherFromKey(cmdKey) if buf.Len() != 16 { panic("Size unexpected") } var result [16]byte aesBlock.Encrypt(result[:], buf.Bytes()) return result } func NewCipherFromKey(cmdKey []byte) cipher.Block { aesBlock, err := aes.NewCipher(KDF16(cmdKey, KDFSaltConstAuthIDEncryptionKey)) if err != nil { panic(err) } return aesBlock } type AuthIDDecoder struct { s cipher.Block } func NewAuthIDDecoder(cmdKey []byte) *AuthIDDecoder { return &AuthIDDecoder{NewCipherFromKey(cmdKey)} } func (aidd *AuthIDDecoder) Decode(data [16]byte) (int64, uint32, int32, []byte) { aidd.s.Decrypt(data[:], data[:]) var t int64 var zero uint32 var rand int32 reader := bytes.NewReader(data[:]) common.Must(binary.Read(reader, binary.BigEndian, &t)) common.Must(binary.Read(reader, binary.BigEndian, &rand)) common.Must(binary.Read(reader, binary.BigEndian, &zero)) return t, zero, rand, data[:] } func NewAuthIDDecoderHolder() *AuthIDDecoderHolder { return &AuthIDDecoderHolder{make(map[string]*AuthIDDecoderItem), antireplay.NewReplayFilter(120)} } type AuthIDDecoderHolder struct { decoders map[string]*AuthIDDecoderItem filter *antireplay.ReplayFilter } type AuthIDDecoderItem struct { dec *AuthIDDecoder ticket interface{} } func NewAuthIDDecoderItem(key [16]byte, ticket interface{}) *AuthIDDecoderItem { return &AuthIDDecoderItem{ dec: NewAuthIDDecoder(key[:]), ticket: ticket, } } func (a *AuthIDDecoderHolder) AddUser(key [16]byte, ticket interface{}) { a.decoders[string(key[:])] = NewAuthIDDecoderItem(key, ticket) } func (a *AuthIDDecoderHolder) RemoveUser(key [16]byte) { delete(a.decoders, string(key[:])) } func (a *AuthIDDecoderHolder) Match(authID [16]byte) (interface{}, error) { for _, v := range a.decoders { t, z, _, d := v.dec.Decode(authID) if z != crc32.ChecksumIEEE(d[:12]) { continue } if t < 0 { continue } if math.Abs(math.Abs(float64(t))-float64(time.Now().Unix())) > 120 { continue } if !a.filter.Check(authID[:]) { return nil, ErrReplay } return v.ticket, nil } return nil, ErrNotFound } ================================================ FILE: proxy/vmess/aead/authid_test.go ================================================ package aead import ( "fmt" "strconv" "testing" "time" "github.com/stretchr/testify/assert" ) func TestCreateAuthID(t *testing.T) { key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") authid := CreateAuthID(key, time.Now().Unix()) fmt.Println(key) fmt.Println(authid) } func TestCreateAuthIDAndDecode(t *testing.T) { key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") authid := CreateAuthID(key, time.Now().Unix()) fmt.Println(key) fmt.Println(authid) AuthDecoder := NewAuthIDDecoderHolder() var keyw [16]byte copy(keyw[:], key) AuthDecoder.AddUser(keyw, "Demo User") res, err := AuthDecoder.Match(authid) fmt.Println(res) fmt.Println(err) assert.Equal(t, "Demo User", res) assert.Nil(t, err) } func TestCreateAuthIDAndDecode2(t *testing.T) { key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") authid := CreateAuthID(key, time.Now().Unix()) fmt.Println(key) fmt.Println(authid) AuthDecoder := NewAuthIDDecoderHolder() var keyw [16]byte copy(keyw[:], key) AuthDecoder.AddUser(keyw, "Demo User") res, err := AuthDecoder.Match(authid) fmt.Println(res) fmt.Println(err) assert.Equal(t, "Demo User", res) assert.Nil(t, err) key2 := KDF16([]byte("Demo Key for Auth ID Test2"), "Demo Path for Auth ID Test") authid2 := CreateAuthID(key2, time.Now().Unix()) res2, err2 := AuthDecoder.Match(authid2) assert.EqualError(t, err2, "user do not exist") assert.Nil(t, res2) } func TestCreateAuthIDAndDecodeMassive(t *testing.T) { key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") authid := CreateAuthID(key, time.Now().Unix()) fmt.Println(key) fmt.Println(authid) AuthDecoder := NewAuthIDDecoderHolder() var keyw [16]byte copy(keyw[:], key) AuthDecoder.AddUser(keyw, "Demo User") res, err := AuthDecoder.Match(authid) fmt.Println(res) fmt.Println(err) assert.Equal(t, "Demo User", res) assert.Nil(t, err) for i := 0; i <= 10000; i++ { key2 := KDF16([]byte("Demo Key for Auth ID Test2"), "Demo Path for Auth ID Test", strconv.Itoa(i)) var keyw2 [16]byte copy(keyw2[:], key2) AuthDecoder.AddUser(keyw2, "Demo User"+strconv.Itoa(i)) } authid3 := CreateAuthID(key, time.Now().Unix()) res2, err2 := AuthDecoder.Match(authid3) assert.Equal(t, "Demo User", res2) assert.Nil(t, err2) } func TestCreateAuthIDAndDecodeSuperMassive(t *testing.T) { key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") authid := CreateAuthID(key, time.Now().Unix()) fmt.Println(key) fmt.Println(authid) AuthDecoder := NewAuthIDDecoderHolder() var keyw [16]byte copy(keyw[:], key) AuthDecoder.AddUser(keyw, "Demo User") res, err := AuthDecoder.Match(authid) fmt.Println(res) fmt.Println(err) assert.Equal(t, "Demo User", res) assert.Nil(t, err) for i := 0; i <= 1000000; i++ { key2 := KDF16([]byte("Demo Key for Auth ID Test2"), "Demo Path for Auth ID Test", strconv.Itoa(i)) var keyw2 [16]byte copy(keyw2[:], key2) AuthDecoder.AddUser(keyw2, "Demo User"+strconv.Itoa(i)) } authid3 := CreateAuthID(key, time.Now().Unix()) before := time.Now() res2, err2 := AuthDecoder.Match(authid3) after := time.Now() assert.Equal(t, "Demo User", res2) assert.Nil(t, err2) fmt.Println(after.Sub(before).Seconds()) } ================================================ FILE: proxy/vmess/aead/consts.go ================================================ package aead const ( KDFSaltConstAuthIDEncryptionKey = "AES Auth ID Encryption" KDFSaltConstAEADRespHeaderLenKey = "AEAD Resp Header Len Key" KDFSaltConstAEADRespHeaderLenIV = "AEAD Resp Header Len IV" KDFSaltConstAEADRespHeaderPayloadKey = "AEAD Resp Header Key" KDFSaltConstAEADRespHeaderPayloadIV = "AEAD Resp Header IV" KDFSaltConstVMessAEADKDF = "VMess AEAD KDF" KDFSaltConstVMessHeaderPayloadAEADKey = "VMess Header AEAD Key" KDFSaltConstVMessHeaderPayloadAEADIV = "VMess Header AEAD Nonce" KDFSaltConstVMessHeaderPayloadLengthAEADKey = "VMess Header AEAD Key_Length" KDFSaltConstVMessHeaderPayloadLengthAEADIV = "VMess Header AEAD Nonce_Length" ) ================================================ FILE: proxy/vmess/aead/encrypt.go ================================================ package aead import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/binary" "io" "time" "github.com/v2fly/v2ray-core/v5/common" ) func SealVMessAEADHeader(key [16]byte, data []byte) []byte { generatedAuthID := CreateAuthID(key[:], time.Now().Unix()) connectionNonce := make([]byte, 8) if _, err := io.ReadFull(rand.Reader, connectionNonce); err != nil { panic(err.Error()) } aeadPayloadLengthSerializeBuffer := bytes.NewBuffer(nil) headerPayloadDataLen := uint16(len(data)) common.Must(binary.Write(aeadPayloadLengthSerializeBuffer, binary.BigEndian, headerPayloadDataLen)) aeadPayloadLengthSerializedByte := aeadPayloadLengthSerializeBuffer.Bytes() var payloadHeaderLengthAEADEncrypted []byte { payloadHeaderLengthAEADKey := KDF16(key[:], KDFSaltConstVMessHeaderPayloadLengthAEADKey, string(generatedAuthID[:]), string(connectionNonce)) payloadHeaderLengthAEADNonce := KDF(key[:], KDFSaltConstVMessHeaderPayloadLengthAEADIV, string(generatedAuthID[:]), string(connectionNonce))[:12] payloadHeaderLengthAEADAESBlock, err := aes.NewCipher(payloadHeaderLengthAEADKey) if err != nil { panic(err.Error()) } payloadHeaderAEAD, err := cipher.NewGCM(payloadHeaderLengthAEADAESBlock) if err != nil { panic(err.Error()) } payloadHeaderLengthAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderLengthAEADNonce, aeadPayloadLengthSerializedByte, generatedAuthID[:]) } var payloadHeaderAEADEncrypted []byte { payloadHeaderAEADKey := KDF16(key[:], KDFSaltConstVMessHeaderPayloadAEADKey, string(generatedAuthID[:]), string(connectionNonce)) payloadHeaderAEADNonce := KDF(key[:], KDFSaltConstVMessHeaderPayloadAEADIV, string(generatedAuthID[:]), string(connectionNonce))[:12] payloadHeaderAEADAESBlock, err := aes.NewCipher(payloadHeaderAEADKey) if err != nil { panic(err.Error()) } payloadHeaderAEAD, err := cipher.NewGCM(payloadHeaderAEADAESBlock) if err != nil { panic(err.Error()) } payloadHeaderAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderAEADNonce, data, generatedAuthID[:]) } outputBuffer := bytes.NewBuffer(nil) common.Must2(outputBuffer.Write(generatedAuthID[:])) // 16 common.Must2(outputBuffer.Write(payloadHeaderLengthAEADEncrypted)) // 2+16 common.Must2(outputBuffer.Write(connectionNonce)) // 8 common.Must2(outputBuffer.Write(payloadHeaderAEADEncrypted)) return outputBuffer.Bytes() } func OpenVMessAEADHeader(key [16]byte, authid [16]byte, data io.Reader) ([]byte, bool, int, error) { var payloadHeaderLengthAEADEncrypted [18]byte var nonce [8]byte var bytesRead int authidCheckValueReadBytesCounts, err := io.ReadFull(data, payloadHeaderLengthAEADEncrypted[:]) bytesRead += authidCheckValueReadBytesCounts if err != nil { return nil, false, bytesRead, err } nonceReadBytesCounts, err := io.ReadFull(data, nonce[:]) bytesRead += nonceReadBytesCounts if err != nil { return nil, false, bytesRead, err } // Decrypt Length var decryptedAEADHeaderLengthPayloadResult []byte { payloadHeaderLengthAEADKey := KDF16(key[:], KDFSaltConstVMessHeaderPayloadLengthAEADKey, string(authid[:]), string(nonce[:])) payloadHeaderLengthAEADNonce := KDF(key[:], KDFSaltConstVMessHeaderPayloadLengthAEADIV, string(authid[:]), string(nonce[:]))[:12] payloadHeaderAEADAESBlock, err := aes.NewCipher(payloadHeaderLengthAEADKey) if err != nil { panic(err.Error()) } payloadHeaderLengthAEAD, err := cipher.NewGCM(payloadHeaderAEADAESBlock) if err != nil { panic(err.Error()) } decryptedAEADHeaderLengthPayload, erropenAEAD := payloadHeaderLengthAEAD.Open(nil, payloadHeaderLengthAEADNonce, payloadHeaderLengthAEADEncrypted[:], authid[:]) if erropenAEAD != nil { return nil, true, bytesRead, erropenAEAD } decryptedAEADHeaderLengthPayloadResult = decryptedAEADHeaderLengthPayload } var length uint16 common.Must(binary.Read(bytes.NewReader(decryptedAEADHeaderLengthPayloadResult), binary.BigEndian, &length)) var decryptedAEADHeaderPayloadR []byte var payloadHeaderAEADEncryptedReadedBytesCounts int { payloadHeaderAEADKey := KDF16(key[:], KDFSaltConstVMessHeaderPayloadAEADKey, string(authid[:]), string(nonce[:])) payloadHeaderAEADNonce := KDF(key[:], KDFSaltConstVMessHeaderPayloadAEADIV, string(authid[:]), string(nonce[:]))[:12] // 16 == AEAD Tag size payloadHeaderAEADEncrypted := make([]byte, length+16) payloadHeaderAEADEncryptedReadedBytesCounts, err = io.ReadFull(data, payloadHeaderAEADEncrypted) bytesRead += payloadHeaderAEADEncryptedReadedBytesCounts if err != nil { return nil, false, bytesRead, err } payloadHeaderAEADAESBlock, err := aes.NewCipher(payloadHeaderAEADKey) if err != nil { panic(err.Error()) } payloadHeaderAEAD, err := cipher.NewGCM(payloadHeaderAEADAESBlock) if err != nil { panic(err.Error()) } decryptedAEADHeaderPayload, erropenAEAD := payloadHeaderAEAD.Open(nil, payloadHeaderAEADNonce, payloadHeaderAEADEncrypted, authid[:]) if erropenAEAD != nil { return nil, true, bytesRead, erropenAEAD } decryptedAEADHeaderPayloadR = decryptedAEADHeaderPayload } return decryptedAEADHeaderPayloadR, false, bytesRead, nil } ================================================ FILE: proxy/vmess/aead/encrypt_test.go ================================================ package aead import ( "bytes" "fmt" "io" "testing" "github.com/stretchr/testify/assert" ) func TestOpenVMessAEADHeader(t *testing.T) { TestHeader := []byte("Test Header") key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") var keyw [16]byte copy(keyw[:], key) sealed := SealVMessAEADHeader(keyw, TestHeader) AEADR := bytes.NewReader(sealed) var authid [16]byte io.ReadFull(AEADR, authid[:]) out, _, _, err := OpenVMessAEADHeader(keyw, authid, AEADR) fmt.Println(string(out)) fmt.Println(err) } func TestOpenVMessAEADHeader2(t *testing.T) { TestHeader := []byte("Test Header") key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") var keyw [16]byte copy(keyw[:], key) sealed := SealVMessAEADHeader(keyw, TestHeader) AEADR := bytes.NewReader(sealed) var authid [16]byte io.ReadFull(AEADR, authid[:]) out, _, readen, err := OpenVMessAEADHeader(keyw, authid, AEADR) assert.Equal(t, len(sealed)-16-AEADR.Len(), readen) assert.Equal(t, string(TestHeader), string(out)) assert.Nil(t, err) } func TestOpenVMessAEADHeader4(t *testing.T) { for i := 0; i <= 60; i++ { TestHeader := []byte("Test Header") key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") var keyw [16]byte copy(keyw[:], key) sealed := SealVMessAEADHeader(keyw, TestHeader) var sealedm [16]byte copy(sealedm[:], sealed) sealed[i] ^= 0xff AEADR := bytes.NewReader(sealed) var authid [16]byte io.ReadFull(AEADR, authid[:]) out, drain, readen, err := OpenVMessAEADHeader(keyw, authid, AEADR) assert.Equal(t, len(sealed)-16-AEADR.Len(), readen) assert.Equal(t, true, drain) assert.NotNil(t, err) if err == nil { fmt.Println(">") } assert.Nil(t, out) } } func TestOpenVMessAEADHeader4Massive(t *testing.T) { for j := 0; j < 1000; j++ { for i := 0; i <= 60; i++ { TestHeader := []byte("Test Header") key := KDF16([]byte("Demo Key for Auth ID Test"), "Demo Path for Auth ID Test") var keyw [16]byte copy(keyw[:], key) sealed := SealVMessAEADHeader(keyw, TestHeader) var sealedm [16]byte copy(sealedm[:], sealed) sealed[i] ^= 0xff AEADR := bytes.NewReader(sealed) var authid [16]byte io.ReadFull(AEADR, authid[:]) out, drain, readen, err := OpenVMessAEADHeader(keyw, authid, AEADR) assert.Equal(t, len(sealed)-16-AEADR.Len(), readen) assert.Equal(t, true, drain) assert.NotNil(t, err) if err == nil { fmt.Println(">") } assert.Nil(t, out) } } } ================================================ FILE: proxy/vmess/aead/kdf.go ================================================ package aead import ( "crypto/hmac" "crypto/sha256" "hash" ) func KDF(key []byte, path ...string) []byte { hmacCreator := &hMacCreator{value: []byte(KDFSaltConstVMessAEADKDF)} for _, v := range path { hmacCreator = &hMacCreator{value: []byte(v), parent: hmacCreator} } hmacf := hmacCreator.Create() hmacf.Write(key) return hmacf.Sum(nil) } type hMacCreator struct { parent *hMacCreator value []byte } func (h *hMacCreator) Create() hash.Hash { if h.parent == nil { return hmac.New(sha256.New, h.value) } return hmac.New(h.parent.Create, h.value) } func KDF16(key []byte, path ...string) []byte { r := KDF(key, path...) return r[:16] } ================================================ FILE: proxy/vmess/aead/kdf_test.go ================================================ package aead import ( "encoding/hex" "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestKDFValue(t *testing.T) { GeneratedKey := KDF([]byte("Demo Key for KDF Value Test"), "Demo Path for KDF Value Test", "Demo Path for KDF Value Test2", "Demo Path for KDF Value Test3") fmt.Println(hex.EncodeToString(GeneratedKey)) assert.Equal(t, "53e9d7e1bd7bd25022b71ead07d8a596efc8a845c7888652fd684b4903dc8892", hex.EncodeToString(GeneratedKey), "Should generate expected KDF Value") } ================================================ FILE: proxy/vmess/encoding/auth.go ================================================ package encoding import ( "crypto/md5" "encoding/binary" "hash/fnv" "golang.org/x/crypto/sha3" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/crypto" ) // Authenticate authenticates a byte array using Fnv hash. func Authenticate(b []byte) uint32 { fnv1hash := fnv.New32a() common.Must2(fnv1hash.Write(b)) return fnv1hash.Sum32() } type NoOpAuthenticator struct{} func (NoOpAuthenticator) NonceSize() int { return 0 } func (NoOpAuthenticator) Overhead() int { return 0 } // Seal implements AEAD.Seal(). func (NoOpAuthenticator) Seal(dst, nonce, plaintext, additionalData []byte) []byte { return append(dst[:0], plaintext...) } // Open implements AEAD.Open(). func (NoOpAuthenticator) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { return append(dst[:0], ciphertext...), nil } // FnvAuthenticator is an AEAD based on Fnv hash. type FnvAuthenticator struct{} // NonceSize implements AEAD.NonceSize(). func (*FnvAuthenticator) NonceSize() int { return 0 } // Overhead impelements AEAD.Overhead(). func (*FnvAuthenticator) Overhead() int { return 4 } // Seal implements AEAD.Seal(). func (*FnvAuthenticator) Seal(dst, nonce, plaintext, additionalData []byte) []byte { dst = append(dst, 0, 0, 0, 0) binary.BigEndian.PutUint32(dst, Authenticate(plaintext)) return append(dst, plaintext...) } // Open implements AEAD.Open(). func (*FnvAuthenticator) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { if binary.BigEndian.Uint32(ciphertext[:4]) != Authenticate(ciphertext[4:]) { return dst, newError("invalid authentication") } return append(dst, ciphertext[4:]...), nil } // GenerateChacha20Poly1305Key generates a 32-byte key from a given 16-byte array. func GenerateChacha20Poly1305Key(b []byte) []byte { key := make([]byte, 32) t := md5.Sum(b) copy(key, t[:]) t = md5.Sum(key[:16]) copy(key[16:], t[:]) return key } type ShakeSizeParser struct { shake sha3.ShakeHash buffer [2]byte } func NewShakeSizeParser(nonce []byte) *ShakeSizeParser { shake := sha3.NewShake128() common.Must2(shake.Write(nonce)) return &ShakeSizeParser{ shake: shake, } } func (*ShakeSizeParser) SizeBytes() int32 { return 2 } func (s *ShakeSizeParser) next() uint16 { common.Must2(s.shake.Read(s.buffer[:])) return binary.BigEndian.Uint16(s.buffer[:]) } func (s *ShakeSizeParser) Decode(b []byte) (uint16, error) { mask := s.next() size := binary.BigEndian.Uint16(b) return mask ^ size, nil } func (s *ShakeSizeParser) Encode(size uint16, b []byte) []byte { mask := s.next() binary.BigEndian.PutUint16(b, mask^size) return b[:2] } func (s *ShakeSizeParser) NextPaddingLen() uint16 { return s.next() % 64 } func (s *ShakeSizeParser) MaxPaddingLen() uint16 { return 64 } type AEADSizeParser struct { crypto.AEADChunkSizeParser } func NewAEADSizeParser(auth *crypto.AEADAuthenticator) *AEADSizeParser { return &AEADSizeParser{crypto.AEADChunkSizeParser{Auth: auth}} } ================================================ FILE: proxy/vmess/encoding/auth_test.go ================================================ package encoding_test import ( "crypto/rand" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/proxy/vmess/encoding" ) func TestFnvAuth(t *testing.T) { fnvAuth := new(FnvAuthenticator) expectedText := make([]byte, 256) _, err := rand.Read(expectedText) common.Must(err) buffer := make([]byte, 512) b := fnvAuth.Seal(buffer[:0], nil, expectedText, nil) b, err = fnvAuth.Open(buffer[:0], nil, b, nil) common.Must(err) if r := cmp.Diff(b, expectedText); r != "" { t.Error(r) } } ================================================ FILE: proxy/vmess/encoding/client.go ================================================ package encoding import ( "bytes" "context" "crypto/aes" "crypto/cipher" "crypto/md5" "crypto/rand" "crypto/sha256" "encoding/binary" "hash" "hash/fnv" "io" "golang.org/x/crypto/chacha20poly1305" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/bitmask" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/crypto" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/drain" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/vmess" vmessaead "github.com/v2fly/v2ray-core/v5/proxy/vmess/aead" ) func hashTimestamp(h hash.Hash, t protocol.Timestamp) []byte { common.Must2(serial.WriteUint64(h, uint64(t))) common.Must2(serial.WriteUint64(h, uint64(t))) common.Must2(serial.WriteUint64(h, uint64(t))) common.Must2(serial.WriteUint64(h, uint64(t))) return h.Sum(nil) } // ClientSession stores connection session info for VMess client. type ClientSession struct { isAEAD bool idHash protocol.IDHash requestBodyKey [16]byte requestBodyIV [16]byte responseBodyKey [16]byte responseBodyIV [16]byte responseReader io.Reader responseHeader byte readDrainer drain.Drainer } // NewClientSession creates a new ClientSession. func NewClientSession(ctx context.Context, isAEAD bool, idHash protocol.IDHash, behaviorSeed int64) *ClientSession { session := &ClientSession{ isAEAD: isAEAD, idHash: idHash, } randomBytes := make([]byte, 33) // 16 + 16 + 1 common.Must2(rand.Read(randomBytes)) copy(session.requestBodyKey[:], randomBytes[:16]) copy(session.requestBodyIV[:], randomBytes[16:32]) session.responseHeader = randomBytes[32] if !session.isAEAD { session.responseBodyKey = md5.Sum(session.requestBodyKey[:]) session.responseBodyIV = md5.Sum(session.requestBodyIV[:]) } else { BodyKey := sha256.Sum256(session.requestBodyKey[:]) copy(session.responseBodyKey[:], BodyKey[:16]) BodyIV := sha256.Sum256(session.requestBodyIV[:]) copy(session.responseBodyIV[:], BodyIV[:16]) } { var err error session.readDrainer, err = drain.NewBehaviorSeedLimitedDrainer(behaviorSeed, 18, 3266, 64) if err != nil { newError("unable to initialize drainer").Base(err).WriteToLog() session.readDrainer = drain.NewNopDrainer() } } return session } func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) error { timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)() account := header.User.Account.(*vmess.MemoryAccount) if !c.isAEAD { idHash := c.idHash(account.AnyValidID().Bytes()) common.Must2(serial.WriteUint64(idHash, uint64(timestamp))) common.Must2(writer.Write(idHash.Sum(nil))) } buffer := buf.New() defer buffer.Release() common.Must(buffer.WriteByte(Version)) common.Must2(buffer.Write(c.requestBodyIV[:])) common.Must2(buffer.Write(c.requestBodyKey[:])) common.Must(buffer.WriteByte(c.responseHeader)) common.Must(buffer.WriteByte(byte(header.Option))) paddingLen := dice.RollWith(16, rand.Reader) security := byte(paddingLen<<4) | byte(header.Security) common.Must2(buffer.Write([]byte{security, byte(0), byte(header.Command)})) if header.Command != protocol.RequestCommandMux { if err := addrParser.WriteAddressPort(buffer, header.Address, header.Port); err != nil { return newError("failed to writer address and port").Base(err) } } if paddingLen > 0 { common.Must2(buffer.ReadFullFrom(rand.Reader, int32(paddingLen))) } { fnv1a := fnv.New32a() common.Must2(fnv1a.Write(buffer.Bytes())) hashBytes := buffer.Extend(int32(fnv1a.Size())) fnv1a.Sum(hashBytes[:0]) } if !c.isAEAD { iv := hashTimestamp(md5.New(), timestamp) aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv) aesStream.XORKeyStream(buffer.Bytes(), buffer.Bytes()) common.Must2(writer.Write(buffer.Bytes())) } else { var fixedLengthCmdKey [16]byte copy(fixedLengthCmdKey[:], account.ID.CmdKey()) vmessout := vmessaead.SealVMessAEADHeader(fixedLengthCmdKey, buffer.Bytes()) common.Must2(io.Copy(writer, bytes.NewReader(vmessout))) } return nil } func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) { var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{} if request.Option.Has(protocol.RequestOptionChunkMasking) { sizeParser = NewShakeSizeParser(c.requestBodyIV[:]) } var padding crypto.PaddingLengthGenerator if request.Option.Has(protocol.RequestOptionGlobalPadding) { var ok bool padding, ok = sizeParser.(crypto.PaddingLengthGenerator) if !ok { return nil, newError("invalid option: RequestOptionGlobalPadding") } } switch request.Security { case protocol.SecurityType_NONE: if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamWriter(sizeParser, writer), nil } auth := &crypto.AEADAuthenticator{ AEAD: new(NoOpAuthenticator), NonceGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil } return buf.NewWriter(writer), nil case protocol.SecurityType_LEGACY: aesStream := crypto.NewAesEncryptionStream(c.requestBodyKey[:], c.requestBodyIV[:]) cryptionWriter := crypto.NewCryptionWriter(aesStream, writer) if request.Option.Has(protocol.RequestOptionChunkStream) { auth := &crypto.AEADAuthenticator{ AEAD: new(FnvAuthenticator), NonceGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType(), padding), nil } return &buf.SequentialWriter{Writer: cryptionWriter}, nil case protocol.SecurityType_AES128_GCM: aead := crypto.NewAesGcm(c.requestBodyKey[:]) auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len") AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey) lengthAuth := &crypto.AEADAuthenticator{ AEAD: AuthenticatedLengthKeyAEAD, NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser = NewAEADSizeParser(lengthAuth) } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil case protocol.SecurityType_CHACHA20_POLY1305: aead, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey[:])) common.Must(err) auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len") AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey)) common.Must(err) lengthAuth := &crypto.AEADAuthenticator{ AEAD: AuthenticatedLengthKeyAEAD, NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser = NewAEADSizeParser(lengthAuth) } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil default: return nil, newError("invalid option: Security") } } func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) { if !c.isAEAD { aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:]) c.responseReader = crypto.NewCryptionReader(aesStream, reader) } else { aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) var aeadEncryptedResponseHeaderLength [18]byte var decryptedResponseHeaderLength int var decryptedResponseHeaderLengthBinaryDeserializeBuffer uint16 if n, err := io.ReadFull(reader, aeadEncryptedResponseHeaderLength[:]); err != nil { c.readDrainer.AcknowledgeReceive(n) return nil, drain.WithError(c.readDrainer, reader, newError("Unable to Read Header Len").Base(err)) } else { // nolint: revive c.readDrainer.AcknowledgeReceive(n) } if decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil); err != nil { return nil, drain.WithError(c.readDrainer, reader, newError("Failed To Decrypt Length").Base(err)) } else { // nolint: revive common.Must(binary.Read(bytes.NewReader(decryptedResponseHeaderLengthBinaryBuffer), binary.BigEndian, &decryptedResponseHeaderLengthBinaryDeserializeBuffer)) decryptedResponseHeaderLength = int(decryptedResponseHeaderLengthBinaryDeserializeBuffer) } aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16) if n, err := io.ReadFull(reader, encryptedResponseHeaderBuffer); err != nil { c.readDrainer.AcknowledgeReceive(n) return nil, drain.WithError(c.readDrainer, reader, newError("Unable to Read Header Data").Base(err)) } else { // nolint: revive c.readDrainer.AcknowledgeReceive(n) } if decryptedResponseHeaderBuffer, err := aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil); err != nil { return nil, drain.WithError(c.readDrainer, reader, newError("Failed To Decrypt Payload").Base(err)) } else { // nolint: revive c.responseReader = bytes.NewReader(decryptedResponseHeaderBuffer) } } buffer := buf.StackNew() defer buffer.Release() if _, err := buffer.ReadFullFrom(c.responseReader, 4); err != nil { return nil, newError("failed to read response header").Base(err).AtWarning() } if buffer.Byte(0) != c.responseHeader { return nil, newError("unexpected response header. Expecting ", int(c.responseHeader), " but actually ", int(buffer.Byte(0))) } header := &protocol.ResponseHeader{ Option: bitmask.Byte(buffer.Byte(1)), } if buffer.Byte(2) != 0 { cmdID := buffer.Byte(2) dataLen := int32(buffer.Byte(3)) buffer.Clear() if _, err := buffer.ReadFullFrom(c.responseReader, dataLen); err != nil { return nil, newError("failed to read response command").Base(err) } command, err := UnmarshalCommand(cmdID, buffer.Bytes()) if err == nil { header.Command = command } } if c.isAEAD { aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:]) c.responseReader = crypto.NewCryptionReader(aesStream, reader) } return header, nil } func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, reader io.Reader) (buf.Reader, error) { var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{} if request.Option.Has(protocol.RequestOptionChunkMasking) { sizeParser = NewShakeSizeParser(c.responseBodyIV[:]) } var padding crypto.PaddingLengthGenerator if request.Option.Has(protocol.RequestOptionGlobalPadding) { var ok bool padding, ok = sizeParser.(crypto.PaddingLengthGenerator) if !ok { return nil, newError("invalid option: RequestOptionGlobalPadding") } } switch request.Security { case protocol.SecurityType_NONE: if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamReader(sizeParser, reader), nil } auth := &crypto.AEADAuthenticator{ AEAD: new(NoOpAuthenticator), NonceGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil } return buf.NewReader(reader), nil case protocol.SecurityType_LEGACY: if request.Option.Has(protocol.RequestOptionChunkStream) { auth := &crypto.AEADAuthenticator{ AEAD: new(FnvAuthenticator), NonceGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType(), padding), nil } return buf.NewReader(c.responseReader), nil case protocol.SecurityType_AES128_GCM: aead := crypto.NewAesGcm(c.responseBodyKey[:]) auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len") AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey) lengthAuth := &crypto.AEADAuthenticator{ AEAD: AuthenticatedLengthKeyAEAD, NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser = NewAEADSizeParser(lengthAuth) } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil case protocol.SecurityType_CHACHA20_POLY1305: aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey[:])) auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len") AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey)) common.Must(err) lengthAuth := &crypto.AEADAuthenticator{ AEAD: AuthenticatedLengthKeyAEAD, NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser = NewAEADSizeParser(lengthAuth) } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil default: return nil, newError("invalid option: Security") } } func GenerateChunkNonce(nonce []byte, size uint32) crypto.BytesGenerator { c := append([]byte(nil), nonce...) count := uint16(0) return func() []byte { binary.BigEndian.PutUint16(c, count) count++ return c[:size] } } ================================================ FILE: proxy/vmess/encoding/commands.go ================================================ package encoding import ( "encoding/binary" "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" ) var ( ErrCommandTypeMismatch = newError("Command type mismatch.") ErrUnknownCommand = newError("Unknown command.") ErrCommandTooLarge = newError("Command too large.") ErrInsufficientLength = newError("Insufficient length.") ErrInvalidAuth = newError("Invalid auth.") ) func MarshalCommand(command interface{}, writer io.Writer) error { if command == nil { return ErrUnknownCommand } var cmdID byte var factory CommandFactory switch command.(type) { case *protocol.CommandSwitchAccount: factory = new(CommandSwitchAccountFactory) cmdID = 1 default: return ErrUnknownCommand } buffer := buf.New() defer buffer.Release() err := factory.Marshal(command, buffer) if err != nil { return err } auth := Authenticate(buffer.Bytes()) length := buffer.Len() + 4 if length > 255 { return ErrCommandTooLarge } common.Must2(writer.Write([]byte{cmdID, byte(length), byte(auth >> 24), byte(auth >> 16), byte(auth >> 8), byte(auth)})) common.Must2(writer.Write(buffer.Bytes())) return nil } func UnmarshalCommand(cmdID byte, data []byte) (protocol.ResponseCommand, error) { if len(data) <= 4 { return nil, ErrInsufficientLength } expectedAuth := Authenticate(data[4:]) actualAuth := binary.BigEndian.Uint32(data[:4]) if expectedAuth != actualAuth { return nil, ErrInvalidAuth } var factory CommandFactory switch cmdID { case 1: factory = new(CommandSwitchAccountFactory) default: return nil, ErrUnknownCommand } return factory.Unmarshal(data[4:]) } type CommandFactory interface { Marshal(command interface{}, writer io.Writer) error Unmarshal(data []byte) (interface{}, error) } type CommandSwitchAccountFactory struct{} func (f *CommandSwitchAccountFactory) Marshal(command interface{}, writer io.Writer) error { cmd, ok := command.(*protocol.CommandSwitchAccount) if !ok { return ErrCommandTypeMismatch } hostStr := "" if cmd.Host != nil { hostStr = cmd.Host.String() } common.Must2(writer.Write([]byte{byte(len(hostStr))})) if len(hostStr) > 0 { common.Must2(writer.Write([]byte(hostStr))) } common.Must2(serial.WriteUint16(writer, cmd.Port.Value())) idBytes := cmd.ID.Bytes() common.Must2(writer.Write(idBytes)) common.Must2(serial.WriteUint16(writer, cmd.AlterIds)) common.Must2(writer.Write([]byte{byte(cmd.Level)})) common.Must2(writer.Write([]byte{cmd.ValidMin})) return nil } func (f *CommandSwitchAccountFactory) Unmarshal(data []byte) (interface{}, error) { cmd := new(protocol.CommandSwitchAccount) if len(data) == 0 { return nil, ErrInsufficientLength } lenHost := int(data[0]) if len(data) < lenHost+1 { return nil, ErrInsufficientLength } if lenHost > 0 { cmd.Host = net.ParseAddress(string(data[1 : 1+lenHost])) } portStart := 1 + lenHost if len(data) < portStart+2 { return nil, ErrInsufficientLength } cmd.Port = net.PortFromBytes(data[portStart : portStart+2]) idStart := portStart + 2 if len(data) < idStart+16 { return nil, ErrInsufficientLength } cmd.ID, _ = uuid.ParseBytes(data[idStart : idStart+16]) alterIDStart := idStart + 16 if len(data) < alterIDStart+2 { return nil, ErrInsufficientLength } cmd.AlterIds = binary.BigEndian.Uint16(data[alterIDStart : alterIDStart+2]) levelStart := alterIDStart + 2 if len(data) < levelStart+1 { return nil, ErrInsufficientLength } cmd.Level = uint32(data[levelStart]) timeStart := levelStart + 1 if len(data) < timeStart+1 { return nil, ErrInsufficientLength } cmd.ValidMin = data[timeStart] return cmd, nil } ================================================ FILE: proxy/vmess/encoding/commands_test.go ================================================ package encoding_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/uuid" . "github.com/v2fly/v2ray-core/v5/proxy/vmess/encoding" ) func TestSwitchAccount(t *testing.T) { sa := &protocol.CommandSwitchAccount{ Port: 1234, ID: uuid.New(), AlterIds: 1024, Level: 128, ValidMin: 16, } buffer := buf.New() common.Must(MarshalCommand(sa, buffer)) cmd, err := UnmarshalCommand(1, buffer.BytesFrom(2)) common.Must(err) sa2, ok := cmd.(*protocol.CommandSwitchAccount) if !ok { t.Fatal("failed to convert command to CommandSwitchAccount") } if r := cmp.Diff(sa2, sa); r != "" { t.Error(r) } } func TestSwitchAccountBugOffByOne(t *testing.T) { sa := &protocol.CommandSwitchAccount{ Port: 1234, ID: uuid.New(), AlterIds: 1024, Level: 128, ValidMin: 16, } buffer := buf.New() csaf := CommandSwitchAccountFactory{} common.Must(csaf.Marshal(sa, buffer)) Payload := buffer.Bytes() cmd, err := csaf.Unmarshal(Payload[:len(Payload)-1]) assert.Error(t, err) assert.Nil(t, cmd) } ================================================ FILE: proxy/vmess/encoding/encoding.go ================================================ package encoding import ( "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const ( Version = byte(1) ) var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6), protocol.PortThenAddress(), ) ================================================ FILE: proxy/vmess/encoding/encoding_test.go ================================================ package encoding_test import ( "context" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/vmess" . "github.com/v2fly/v2ray-core/v5/proxy/vmess/encoding" ) func toAccount(a *vmess.Account) protocol.Account { account, err := a.AsAccount() common.Must(err) return account } func TestRequestSerialization(t *testing.T) { user := &protocol.MemoryUser{ Level: 0, Email: "test@v2fly.org", } id := uuid.New() account := &vmess.Account{ Id: id.String(), AlterId: 0, } user.Account = toAccount(account) expectedRequest := &protocol.RequestHeader{ Version: 1, User: user, Command: protocol.RequestCommandTCP, Address: net.DomainAddress("www.v2fly.org"), Port: net.Port(443), Security: protocol.SecurityType_AES128_GCM, } buffer := buf.New() client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) buffer2 := buf.New() buffer2.Write(buffer.Bytes()) sessionHistory := NewSessionHistory() defer common.Close(sessionHistory) userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator.Add(user) defer common.Close(userValidator) server := NewServerSession(userValidator, sessionHistory) actualRequest, err := server.DecodeRequestHeader(buffer) common.Must(err) if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { t.Error(r) } _, err = server.DecodeRequestHeader(buffer2) // anti replay attack if err == nil { t.Error("nil error") } } func TestInvalidRequest(t *testing.T) { user := &protocol.MemoryUser{ Level: 0, Email: "test@v2fly.org", } id := uuid.New() account := &vmess.Account{ Id: id.String(), AlterId: 0, } user.Account = toAccount(account) expectedRequest := &protocol.RequestHeader{ Version: 1, User: user, Command: protocol.RequestCommand(100), Address: net.DomainAddress("www.v2fly.org"), Port: net.Port(443), Security: protocol.SecurityType_AES128_GCM, } buffer := buf.New() client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) buffer2 := buf.New() buffer2.Write(buffer.Bytes()) sessionHistory := NewSessionHistory() defer common.Close(sessionHistory) userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator.Add(user) defer common.Close(userValidator) server := NewServerSession(userValidator, sessionHistory) _, err := server.DecodeRequestHeader(buffer) if err == nil { t.Error("nil error") } } func TestMuxRequest(t *testing.T) { user := &protocol.MemoryUser{ Level: 0, Email: "test@v2fly.org", } id := uuid.New() account := &vmess.Account{ Id: id.String(), AlterId: 0, } user.Account = toAccount(account) expectedRequest := &protocol.RequestHeader{ Version: 1, User: user, Command: protocol.RequestCommandMux, Security: protocol.SecurityType_AES128_GCM, Address: net.DomainAddress("v1.mux.cool"), } buffer := buf.New() client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) buffer2 := buf.New() buffer2.Write(buffer.Bytes()) sessionHistory := NewSessionHistory() defer common.Close(sessionHistory) userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator.Add(user) defer common.Close(userValidator) server := NewServerSession(userValidator, sessionHistory) actualRequest, err := server.DecodeRequestHeader(buffer) common.Must(err) if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { t.Error(r) } } ================================================ FILE: proxy/vmess/encoding/errors.generated.go ================================================ package encoding import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vmess/encoding/server.go ================================================ package encoding import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/md5" "crypto/sha256" "encoding/binary" "hash/fnv" "io" "sync" "time" "golang.org/x/crypto/chacha20poly1305" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/bitmask" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/crypto" "github.com/v2fly/v2ray-core/v5/common/drain" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/proxy/vmess" vmessaead "github.com/v2fly/v2ray-core/v5/proxy/vmess/aead" ) type sessionID struct { user [16]byte key [16]byte nonce [16]byte } // SessionHistory keeps track of historical session ids, to prevent replay attacks. type SessionHistory struct { sync.RWMutex cache map[sessionID]time.Time task *task.Periodic } // NewSessionHistory creates a new SessionHistory object. func NewSessionHistory() *SessionHistory { h := &SessionHistory{ cache: make(map[sessionID]time.Time, 128), } h.task = &task.Periodic{ Interval: time.Second * 30, Execute: h.removeExpiredEntries, } return h } // Close implements common.Closable. func (h *SessionHistory) Close() error { return h.task.Close() } func (h *SessionHistory) addIfNotExits(session sessionID) bool { h.Lock() if expire, found := h.cache[session]; found && expire.After(time.Now()) { h.Unlock() return false } h.cache[session] = time.Now().Add(time.Minute * 3) h.Unlock() common.Must(h.task.Start()) return true } func (h *SessionHistory) removeExpiredEntries() error { now := time.Now() h.Lock() defer h.Unlock() if len(h.cache) == 0 { return newError("nothing to do") } for session, expire := range h.cache { if expire.Before(now) { delete(h.cache, session) } } if len(h.cache) == 0 { h.cache = make(map[sessionID]time.Time, 128) } return nil } // ServerSession keeps information for a session in VMess server. type ServerSession struct { userValidator *vmess.TimedUserValidator sessionHistory *SessionHistory requestBodyKey [16]byte requestBodyIV [16]byte responseBodyKey [16]byte responseBodyIV [16]byte responseWriter io.Writer responseHeader byte isAEADRequest bool isAEADForced bool } // NewServerSession creates a new ServerSession, using the given UserValidator. // The ServerSession instance doesn't take ownership of the validator. func NewServerSession(validator *vmess.TimedUserValidator, sessionHistory *SessionHistory) *ServerSession { return &ServerSession{ userValidator: validator, sessionHistory: sessionHistory, } } // SetAEADForced sets isAEADForced for a ServerSession. func (s *ServerSession) SetAEADForced(isAEADForced bool) { s.isAEADForced = isAEADForced } func parseSecurityType(b byte) protocol.SecurityType { if _, f := protocol.SecurityType_name[int32(b)]; f { st := protocol.SecurityType(b) // For backward compatibility. if st == protocol.SecurityType_UNKNOWN { st = protocol.SecurityType_LEGACY } return st } return protocol.SecurityType_UNKNOWN } // DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream. func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error) { buffer := buf.New() drainer, err := drain.NewBehaviorSeedLimitedDrainer(int64(s.userValidator.GetBehaviorSeed()), 16+38, 3266, 64) if err != nil { return nil, newError("failed to initialize drainer").Base(err) } drainConnection := func(e error) error { // We read a deterministic generated length of data before closing the connection to offset padding read pattern drainer.AcknowledgeReceive(int(buffer.Len())) return drain.WithError(drainer, reader, e) } defer func() { buffer.Release() }() if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil { return nil, newError("failed to read request header").Base(err) } var decryptor io.Reader var vmessAccount *vmess.MemoryAccount user, foundAEAD, errorAEAD := s.userValidator.GetAEAD(buffer.Bytes()) var fixedSizeAuthID [16]byte copy(fixedSizeAuthID[:], buffer.Bytes()) switch { case foundAEAD: vmessAccount = user.Account.(*vmess.MemoryAccount) var fixedSizeCmdKey [16]byte copy(fixedSizeCmdKey[:], vmessAccount.ID.CmdKey()) aeadData, shouldDrain, bytesRead, errorReason := vmessaead.OpenVMessAEADHeader(fixedSizeCmdKey, fixedSizeAuthID, reader) if errorReason != nil { if shouldDrain { drainer.AcknowledgeReceive(bytesRead) return nil, drainConnection(newError("AEAD read failed").Base(errorReason)) } return nil, drainConnection(newError("AEAD read failed, drain skipped").Base(errorReason)) } decryptor = bytes.NewReader(aeadData) s.isAEADRequest = true case errorAEAD == vmessaead.ErrNotFound: userLegacy, timestamp, valid, userValidationError := s.userValidator.Get(buffer.Bytes()) if !valid || userValidationError != nil { return nil, drainConnection(newError("invalid user").Base(userValidationError)) } if s.isAEADForced { return nil, drainConnection(newError("invalid user: VMessAEAD is enforced and a non VMessAEAD connection is received. You can still disable this security feature with environment variable v2ray.vmess.aead.forced = false . You will not be able to enable legacy header workaround in the future.")) } if s.userValidator.ShouldShowLegacyWarn() { newError("Critical Warning: potentially invalid user: a non VMessAEAD connection is received. From 2022 Jan 1st, this kind of connection will be rejected by default. You should update or replace your client software now. This message will not be shown for further violation on this inbound.").AtWarning().WriteToLog() } user = userLegacy iv := hashTimestamp(md5.New(), timestamp) vmessAccount = userLegacy.Account.(*vmess.MemoryAccount) aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv) decryptor = crypto.NewCryptionReader(aesStream, reader) default: return nil, drainConnection(newError("invalid user").Base(errorAEAD)) } drainer.AcknowledgeReceive(int(buffer.Len())) buffer.Clear() if _, err := buffer.ReadFullFrom(decryptor, 38); err != nil { return nil, newError("failed to read request header").Base(err) } request := &protocol.RequestHeader{ User: user, Version: buffer.Byte(0), } copy(s.requestBodyIV[:], buffer.BytesRange(1, 17)) // 16 bytes copy(s.requestBodyKey[:], buffer.BytesRange(17, 33)) // 16 bytes var sid sessionID copy(sid.user[:], vmessAccount.ID.Bytes()) sid.key = s.requestBodyKey sid.nonce = s.requestBodyIV if !s.sessionHistory.addIfNotExits(sid) { if !s.isAEADRequest { drainErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) if drainErr != nil { return nil, drainConnection(newError("duplicated session id, possibly under replay attack, and failed to taint userHash").Base(drainErr)) } return nil, drainConnection(newError("duplicated session id, possibly under replay attack, userHash tainted")) } return nil, newError("duplicated session id, possibly under replay attack, but this is a AEAD request") } s.responseHeader = buffer.Byte(33) // 1 byte request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte paddingLen := int(buffer.Byte(35) >> 4) request.Security = parseSecurityType(buffer.Byte(35) & 0x0F) // 1 bytes reserved request.Command = protocol.RequestCommand(buffer.Byte(37)) switch request.Command { case protocol.RequestCommandMux: request.Address = net.DomainAddress("v1.mux.cool") request.Port = 0 case protocol.RequestCommandTCP, protocol.RequestCommandUDP: if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil { request.Address = addr request.Port = port } } if paddingLen > 0 { if _, err := buffer.ReadFullFrom(decryptor, int32(paddingLen)); err != nil { if !s.isAEADRequest { burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) if burnErr != nil { return nil, newError("failed to read padding, failed to taint userHash").Base(burnErr).Base(err) } return nil, newError("failed to read padding, userHash tainted").Base(err) } return nil, newError("failed to read padding").Base(err) } } if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil { if !s.isAEADRequest { burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) if burnErr != nil { return nil, newError("failed to read checksum, failed to taint userHash").Base(burnErr).Base(err) } return nil, newError("failed to read checksum, userHash tainted").Base(err) } return nil, newError("failed to read checksum").Base(err) } fnv1a := fnv.New32a() common.Must2(fnv1a.Write(buffer.BytesTo(-4))) actualHash := fnv1a.Sum32() expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4)) if actualHash != expectedHash { if !s.isAEADRequest { Autherr := newError("invalid auth, legacy userHash tainted") burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) if burnErr != nil { Autherr = newError("invalid auth, can't taint legacy userHash").Base(burnErr) } // It is possible that we are under attack described in https://github.com/v2ray/v2ray-core/issues/2523 return nil, drainConnection(Autherr) } return nil, newError("invalid auth, but this is a AEAD request") } if request.Address == nil { return nil, newError("invalid remote address") } if request.Security == protocol.SecurityType_UNKNOWN || request.Security == protocol.SecurityType_AUTO { return nil, newError("unknown security type: ", request.Security) } return request, nil } // DecodeRequestBody returns Reader from which caller can fetch decrypted body. func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) (buf.Reader, error) { var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{} if request.Option.Has(protocol.RequestOptionChunkMasking) { sizeParser = NewShakeSizeParser(s.requestBodyIV[:]) } var padding crypto.PaddingLengthGenerator if request.Option.Has(protocol.RequestOptionGlobalPadding) { var ok bool padding, ok = sizeParser.(crypto.PaddingLengthGenerator) if !ok { return nil, newError("invalid option: RequestOptionGlobalPadding") } } switch request.Security { case protocol.SecurityType_NONE: if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamReader(sizeParser, reader), nil } auth := &crypto.AEADAuthenticator{ AEAD: new(NoOpAuthenticator), NonceGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil } return buf.NewReader(reader), nil case protocol.SecurityType_LEGACY: aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:]) cryptionReader := crypto.NewCryptionReader(aesStream, reader) if request.Option.Has(protocol.RequestOptionChunkStream) { auth := &crypto.AEADAuthenticator{ AEAD: new(FnvAuthenticator), NonceGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding), nil } return buf.NewReader(cryptionReader), nil case protocol.SecurityType_AES128_GCM: aead := crypto.NewAesGcm(s.requestBodyKey[:]) auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len") AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey) lengthAuth := &crypto.AEADAuthenticator{ AEAD: AuthenticatedLengthKeyAEAD, NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser = NewAEADSizeParser(lengthAuth) } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil case protocol.SecurityType_CHACHA20_POLY1305: aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:])) auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len") AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey)) common.Must(err) lengthAuth := &crypto.AEADAuthenticator{ AEAD: AuthenticatedLengthKeyAEAD, NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser = NewAEADSizeParser(lengthAuth) } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil default: return nil, newError("invalid option: Security") } } // EncodeResponseHeader writes encoded response header into the given writer. func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) { var encryptionWriter io.Writer if !s.isAEADRequest { s.responseBodyKey = md5.Sum(s.requestBodyKey[:]) s.responseBodyIV = md5.Sum(s.requestBodyIV[:]) } else { BodyKey := sha256.Sum256(s.requestBodyKey[:]) copy(s.responseBodyKey[:], BodyKey[:16]) BodyIV := sha256.Sum256(s.requestBodyIV[:]) copy(s.responseBodyIV[:], BodyIV[:16]) } aesStream := crypto.NewAesEncryptionStream(s.responseBodyKey[:], s.responseBodyIV[:]) encryptionWriter = crypto.NewCryptionWriter(aesStream, writer) s.responseWriter = encryptionWriter aeadEncryptedHeaderBuffer := bytes.NewBuffer(nil) if s.isAEADRequest { encryptionWriter = aeadEncryptedHeaderBuffer } common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)})) err := MarshalCommand(header.Command, encryptionWriter) if err != nil { common.Must2(encryptionWriter.Write([]byte{0x00, 0x00})) } if s.isAEADRequest { aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) aeadResponseHeaderLengthEncryptionBuffer := bytes.NewBuffer(nil) decryptedResponseHeaderLengthBinaryDeserializeBuffer := uint16(aeadEncryptedHeaderBuffer.Len()) common.Must(binary.Write(aeadResponseHeaderLengthEncryptionBuffer, binary.BigEndian, decryptedResponseHeaderLengthBinaryDeserializeBuffer)) AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil) common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength))) aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil) common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload))) } } // EncodeResponseBody returns a Writer that auto-encrypt content written by caller. func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) { var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{} if request.Option.Has(protocol.RequestOptionChunkMasking) { sizeParser = NewShakeSizeParser(s.responseBodyIV[:]) } var padding crypto.PaddingLengthGenerator if request.Option.Has(protocol.RequestOptionGlobalPadding) { var ok bool padding, ok = sizeParser.(crypto.PaddingLengthGenerator) if !ok { return nil, newError("invalid option: RequestOptionGlobalPadding") } } switch request.Security { case protocol.SecurityType_NONE: if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamWriter(sizeParser, writer), nil } auth := &crypto.AEADAuthenticator{ AEAD: new(NoOpAuthenticator), NonceGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil } return buf.NewWriter(writer), nil case protocol.SecurityType_LEGACY: if request.Option.Has(protocol.RequestOptionChunkStream) { auth := &crypto.AEADAuthenticator{ AEAD: new(FnvAuthenticator), NonceGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding), nil } return &buf.SequentialWriter{Writer: s.responseWriter}, nil case protocol.SecurityType_AES128_GCM: aead := crypto.NewAesGcm(s.responseBodyKey[:]) auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len") AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey) lengthAuth := &crypto.AEADAuthenticator{ AEAD: AuthenticatedLengthKeyAEAD, NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser = NewAEADSizeParser(lengthAuth) } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil case protocol.SecurityType_CHACHA20_POLY1305: aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:])) auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } if request.Option.Has(protocol.RequestOptionAuthenticatedLength) { AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len") AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey)) common.Must(err) lengthAuth := &crypto.AEADAuthenticator{ AEAD: AuthenticatedLengthKeyAEAD, NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), AdditionalDataGenerator: crypto.GenerateEmptyBytes(), } sizeParser = NewAEADSizeParser(lengthAuth) } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil default: return nil, newError("invalid option: Security") } } ================================================ FILE: proxy/vmess/errors.generated.go ================================================ package vmess import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vmess/inbound/config.go ================================================ package inbound // GetDefaultValue returns default settings of DefaultConfig. func (c *Config) GetDefaultValue() *DefaultConfig { if c.GetDefault() == nil { return &DefaultConfig{} } return c.Default } ================================================ FILE: proxy/vmess/inbound/config.pb.go ================================================ package inbound import ( protocol "github.com/v2fly/v2ray-core/v5/common/protocol" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type DetourConfig struct { state protoimpl.MessageState `protogen:"open.v1"` To string `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DetourConfig) Reset() { *x = DetourConfig{} mi := &file_proxy_vmess_inbound_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DetourConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*DetourConfig) ProtoMessage() {} func (x *DetourConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_inbound_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DetourConfig.ProtoReflect.Descriptor instead. func (*DetourConfig) Descriptor() ([]byte, []int) { return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{0} } func (x *DetourConfig) GetTo() string { if x != nil { return x.To } return "" } type DefaultConfig struct { state protoimpl.MessageState `protogen:"open.v1"` AlterId uint32 `protobuf:"varint,1,opt,name=alter_id,json=alterId,proto3" json:"alter_id,omitempty"` Level uint32 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DefaultConfig) Reset() { *x = DefaultConfig{} mi := &file_proxy_vmess_inbound_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DefaultConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*DefaultConfig) ProtoMessage() {} func (x *DefaultConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_inbound_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DefaultConfig.ProtoReflect.Descriptor instead. func (*DefaultConfig) Descriptor() ([]byte, []int) { return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{1} } func (x *DefaultConfig) GetAlterId() uint32 { if x != nil { return x.AlterId } return 0 } func (x *DefaultConfig) GetLevel() uint32 { if x != nil { return x.Level } return 0 } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"` Default *DefaultConfig `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"` Detour *DetourConfig `protobuf:"bytes,3,opt,name=detour,proto3" json:"detour,omitempty"` SecureEncryptionOnly bool `protobuf:"varint,4,opt,name=secure_encryption_only,json=secureEncryptionOnly,proto3" json:"secure_encryption_only,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_vmess_inbound_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_inbound_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{2} } func (x *Config) GetUser() []*protocol.User { if x != nil { return x.User } return nil } func (x *Config) GetDefault() *DefaultConfig { if x != nil { return x.Default } return nil } func (x *Config) GetDetour() *DetourConfig { if x != nil { return x.Detour } return nil } func (x *Config) GetSecureEncryptionOnly() bool { if x != nil { return x.SecureEncryptionOnly } return false } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Users []string `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_proxy_vmess_inbound_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_inbound_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{3} } func (x *SimplifiedConfig) GetUsers() []string { if x != nil { return x.Users } return nil } var File_proxy_vmess_inbound_config_proto protoreflect.FileDescriptor const file_proxy_vmess_inbound_config_proto_rawDesc = "" + "\n" + " proxy/vmess/inbound/config.proto\x12\x1ev2ray.core.proxy.vmess.inbound\x1a\x1acommon/protocol/user.proto\x1a common/protoext/extensions.proto\"\x1e\n" + "\fDetourConfig\x12\x0e\n" + "\x02to\x18\x01 \x01(\tR\x02to\"@\n" + "\rDefaultConfig\x12\x19\n" + "\balter_id\x18\x01 \x01(\rR\aalterId\x12\x14\n" + "\x05level\x18\x02 \x01(\rR\x05level\"\x83\x02\n" + "\x06Config\x124\n" + "\x04user\x18\x01 \x03(\v2 .v2ray.core.common.protocol.UserR\x04user\x12G\n" + "\adefault\x18\x02 \x01(\v2-.v2ray.core.proxy.vmess.inbound.DefaultConfigR\adefault\x12D\n" + "\x06detour\x18\x03 \x01(\v2,.v2ray.core.proxy.vmess.inbound.DetourConfigR\x06detour\x124\n" + "\x16secure_encryption_only\x18\x04 \x01(\bR\x14secureEncryptionOnly\">\n" + "\x10SimplifiedConfig\x12\x14\n" + "\x05users\x18\x01 \x03(\tR\x05users:\x14\x82\xb5\x18\x10\n" + "\ainbound\x12\x05vmessB{\n" + "\"com.v2ray.core.proxy.vmess.inboundP\x01Z2github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound\xaa\x02\x1eV2Ray.Core.Proxy.Vmess.Inboundb\x06proto3" var ( file_proxy_vmess_inbound_config_proto_rawDescOnce sync.Once file_proxy_vmess_inbound_config_proto_rawDescData []byte ) func file_proxy_vmess_inbound_config_proto_rawDescGZIP() []byte { file_proxy_vmess_inbound_config_proto_rawDescOnce.Do(func() { file_proxy_vmess_inbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vmess_inbound_config_proto_rawDesc), len(file_proxy_vmess_inbound_config_proto_rawDesc))) }) return file_proxy_vmess_inbound_config_proto_rawDescData } var file_proxy_vmess_inbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_proxy_vmess_inbound_config_proto_goTypes = []any{ (*DetourConfig)(nil), // 0: v2ray.core.proxy.vmess.inbound.DetourConfig (*DefaultConfig)(nil), // 1: v2ray.core.proxy.vmess.inbound.DefaultConfig (*Config)(nil), // 2: v2ray.core.proxy.vmess.inbound.Config (*SimplifiedConfig)(nil), // 3: v2ray.core.proxy.vmess.inbound.SimplifiedConfig (*protocol.User)(nil), // 4: v2ray.core.common.protocol.User } var file_proxy_vmess_inbound_config_proto_depIdxs = []int32{ 4, // 0: v2ray.core.proxy.vmess.inbound.Config.user:type_name -> v2ray.core.common.protocol.User 1, // 1: v2ray.core.proxy.vmess.inbound.Config.default:type_name -> v2ray.core.proxy.vmess.inbound.DefaultConfig 0, // 2: v2ray.core.proxy.vmess.inbound.Config.detour:type_name -> v2ray.core.proxy.vmess.inbound.DetourConfig 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_proxy_vmess_inbound_config_proto_init() } func file_proxy_vmess_inbound_config_proto_init() { if File_proxy_vmess_inbound_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vmess_inbound_config_proto_rawDesc), len(file_proxy_vmess_inbound_config_proto_rawDesc)), NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vmess_inbound_config_proto_goTypes, DependencyIndexes: file_proxy_vmess_inbound_config_proto_depIdxs, MessageInfos: file_proxy_vmess_inbound_config_proto_msgTypes, }.Build() File_proxy_vmess_inbound_config_proto = out.File file_proxy_vmess_inbound_config_proto_goTypes = nil file_proxy_vmess_inbound_config_proto_depIdxs = nil } ================================================ FILE: proxy/vmess/inbound/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vmess.inbound; option csharp_namespace = "V2Ray.Core.Proxy.Vmess.Inbound"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound"; option java_package = "com.v2ray.core.proxy.vmess.inbound"; option java_multiple_files = true; import "common/protocol/user.proto"; import "common/protoext/extensions.proto"; message DetourConfig { string to = 1; } message DefaultConfig { uint32 alter_id = 1; uint32 level = 2; } message Config { repeated v2ray.core.common.protocol.User user = 1; DefaultConfig default = 2; DetourConfig detour = 3; bool secure_encryption_only = 4; } message SimplifiedConfig{ option (v2ray.core.common.protoext.message_opt).type = "inbound"; option (v2ray.core.common.protoext.message_opt).short_name = "vmess"; repeated string users = 1; } ================================================ FILE: proxy/vmess/inbound/errors.generated.go ================================================ package inbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vmess/inbound/inbound.go ================================================ package inbound //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "io" "strings" "sync" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/common/uuid" feature_inbound "github.com/v2fly/v2ray-core/v5/features/inbound" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/encoding" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type userByEmail struct { sync.Mutex cache map[string]*protocol.MemoryUser defaultLevel uint32 defaultAlterIDs uint16 } func newUserByEmail(config *DefaultConfig) *userByEmail { return &userByEmail{ cache: make(map[string]*protocol.MemoryUser), defaultLevel: config.Level, defaultAlterIDs: uint16(config.AlterId), } } func (v *userByEmail) addNoLock(u *protocol.MemoryUser) bool { email := strings.ToLower(u.Email) _, found := v.cache[email] if found { return false } v.cache[email] = u return true } func (v *userByEmail) Add(u *protocol.MemoryUser) bool { v.Lock() defer v.Unlock() return v.addNoLock(u) } func (v *userByEmail) Get(email string) (*protocol.MemoryUser, bool) { email = strings.ToLower(email) v.Lock() defer v.Unlock() user, found := v.cache[email] if !found { id := uuid.New() rawAccount := &vmess.Account{ Id: id.String(), AlterId: uint32(v.defaultAlterIDs), } account, err := rawAccount.AsAccount() common.Must(err) user = &protocol.MemoryUser{ Level: v.defaultLevel, Email: email, Account: account, } v.cache[email] = user } return user, found } func (v *userByEmail) Remove(email string) bool { email = strings.ToLower(email) v.Lock() defer v.Unlock() if _, found := v.cache[email]; !found { return false } delete(v.cache, email) return true } // Handler is an inbound connection handler that handles messages in VMess protocol. type Handler struct { policyManager policy.Manager inboundHandlerManager feature_inbound.Manager clients *vmess.TimedUserValidator usersByEmail *userByEmail detours *DetourConfig sessionHistory *encoding.SessionHistory secure bool } // New creates a new VMess inbound handler. func New(ctx context.Context, config *Config) (*Handler, error) { v := core.MustFromContext(ctx) handler := &Handler{ policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), clients: vmess.NewTimedUserValidator(protocol.DefaultIDHash), detours: config.Detour, usersByEmail: newUserByEmail(config.GetDefaultValue()), sessionHistory: encoding.NewSessionHistory(), secure: config.SecureEncryptionOnly, } for _, user := range config.User { mUser, err := user.ToMemoryUser() if err != nil { return nil, newError("failed to get VMess user").Base(err) } if err := handler.AddUser(ctx, mUser); err != nil { return nil, newError("failed to initiate user").Base(err) } } return handler, nil } // Close implements common.Closable. func (h *Handler) Close() error { return errors.Combine( h.clients.Close(), h.sessionHistory.Close(), common.Close(h.usersByEmail)) } // Network implements proxy.Inbound.Network(). func (*Handler) Network() []net.Network { return []net.Network{net.Network_TCP, net.Network_UNIX} } func (h *Handler) GetUser(email string) *protocol.MemoryUser { user, existing := h.usersByEmail.Get(email) if !existing { h.clients.Add(user) } return user } func (h *Handler) AddUser(ctx context.Context, user *protocol.MemoryUser) error { if len(user.Email) > 0 && !h.usersByEmail.Add(user) { return newError("User ", user.Email, " already exists.") } return h.clients.Add(user) } func (h *Handler) RemoveUser(ctx context.Context, email string) error { if email == "" { return newError("Email must not be empty.") } if !h.usersByEmail.Remove(email) { return newError("User ", email, " not found.") } h.clients.Remove(email) return nil } func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, response *protocol.ResponseHeader, input buf.Reader, output *buf.BufferedWriter) error { session.EncodeResponseHeader(response, output) bodyWriter, err := session.EncodeResponseBody(request, output) if err != nil { return newError("failed to start decoding response").Base(err) } { // Optimize for small response packet data, err := input.ReadMultiBuffer() if err != nil { return err } if err := bodyWriter.WriteMultiBuffer(data); err != nil { return err } } if err := output.SetBuffered(false); err != nil { return err } if err := buf.Copy(input, bodyWriter, buf.UpdateActivity(timer)); err != nil { return err } account := request.User.Account.(*vmess.MemoryAccount) if request.Option.Has(protocol.RequestOptionChunkStream) && !account.NoTerminationSignal { if err := bodyWriter.WriteMultiBuffer(buf.MultiBuffer{}); err != nil { return err } } return nil } func isInsecureEncryption(s protocol.SecurityType) bool { return s == protocol.SecurityType_NONE || s == protocol.SecurityType_LEGACY || s == protocol.SecurityType_UNKNOWN } // Process implements proxy.Inbound.Process(). func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher routing.Dispatcher) error { sessionPolicy := h.policyManager.ForLevel(0) if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() } reader := &buf.BufferedReader{Reader: buf.NewReader(connection)} svrSession := encoding.NewServerSession(h.clients, h.sessionHistory) svrSession.SetAEADForced(aeadForced) request, err := svrSession.DecodeRequestHeader(reader) if err != nil { if errors.Cause(err) != io.EOF { log.Record(&log.AccessMessage{ From: connection.RemoteAddr(), To: "", Status: log.AccessRejected, Reason: err, }) err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtInfo() } return err } if h.secure && isInsecureEncryption(request.Security) { log.Record(&log.AccessMessage{ From: connection.RemoteAddr(), To: "", Status: log.AccessRejected, Reason: "Insecure encryption", Email: request.User.Email, }) return newError("client is using insecure encryption: ", request.Security) } if request.Command != protocol.RequestCommandMux { ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ From: connection.RemoteAddr(), To: request.Destination(), Status: log.AccessAccepted, Reason: "", Email: request.User.Email, }) } newError("received request for ", request.Destination()).WriteToLog(session.ExportIDToError(ctx)) if err := connection.SetReadDeadline(time.Time{}); err != nil { newError("unable to set back read deadline").Base(err).WriteToLog(session.ExportIDToError(ctx)) } inbound := session.InboundFromContext(ctx) if inbound == nil { panic("no inbound metadata") } inbound.User = request.User sessionPolicy = h.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) link, err := dispatcher.Dispatch(ctx, request.Destination()) if err != nil { return newError("failed to dispatch request to ", request.Destination()).Base(err) } requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) bodyReader, err := svrSession.DecodeRequestBody(request, reader) if err != nil { return newError("failed to start decoding").Base(err) } if err := buf.Copy(bodyReader, link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transfer request").Base(err) } return nil } responseDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) writer := buf.NewBufferedWriter(buf.NewWriter(connection)) defer writer.Flush() response := &protocol.ResponseHeader{ Command: h.generateCommand(ctx, request), } return transferResponse(timer, svrSession, request, response, link.Reader, writer) } requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer)) if err := task.Run(ctx, requestDonePost, responseDone); err != nil { common.Interrupt(link.Reader) common.Interrupt(link.Writer) return newError("connection ends").Base(err) } return nil } func (h *Handler) generateCommand(ctx context.Context, request *protocol.RequestHeader) protocol.ResponseCommand { if h.detours != nil { tag := h.detours.To if h.inboundHandlerManager != nil { handler, err := h.inboundHandlerManager.GetHandler(ctx, tag) if err != nil { newError("failed to get detour handler: ", tag).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) return nil } proxyHandler, port, availableMin := handler.GetRandomInboundProxy() inboundHandler, ok := proxyHandler.(*Handler) if ok && inboundHandler != nil { if availableMin > 255 { availableMin = 255 } newError("pick detour handler for port ", port, " for ", availableMin, " minutes.").AtDebug().WriteToLog(session.ExportIDToError(ctx)) user := inboundHandler.GetUser(request.User.Email) if user == nil { return nil } account := user.Account.(*vmess.MemoryAccount) return &protocol.CommandSwitchAccount{ Port: port, ID: account.ID.UUID(), AlterIds: uint16(len(account.AlterIDs)), Level: user.Level, ValidMin: byte(availableMin), } } } } return nil } var ( aeadForced = false aeadForced2022 = false ) func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedServer := config.(*SimplifiedConfig) fullConfig := &Config{ User: func() (users []*protocol.User) { for _, v := range simplifiedServer.Users { account := &vmess.Account{Id: v} users = append(users, &protocol.User{ Account: serial.ToTypedMessage(account), }) } return }(), } return common.CreateObject(ctx, fullConfig) })) defaultFlagValue := "true_by_default_2022" isAeadForced := platform.NewEnvFlag("v2ray.vmess.aead.forced").GetValue(func() string { return defaultFlagValue }) if isAeadForced == "true" { aeadForced = true } if isAeadForced == "true_by_default_2022" { aeadForced = true aeadForced2022 = true } } ================================================ FILE: proxy/vmess/outbound/command.go ================================================ package outbound import ( "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/proxy/vmess" ) func (h *Handler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) { rawAccount := &vmess.Account{ Id: cmd.ID.String(), AlterId: uint32(cmd.AlterIds), SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_LEGACY, }, } account, err := rawAccount.AsAccount() common.Must(err) user := &protocol.MemoryUser{ Email: "", Level: cmd.Level, Account: account, } dest := net.TCPDestination(cmd.Host, cmd.Port) until := time.Now().Add(time.Duration(cmd.ValidMin) * time.Minute) h.serverList.AddServer(protocol.NewServerSpec(dest, protocol.BeforeTime(until), user)) } func (h *Handler) handleCommand(dest net.Destination, cmd protocol.ResponseCommand) { switch typedCommand := cmd.(type) { case *protocol.CommandSwitchAccount: if typedCommand.Host == nil { typedCommand.Host = dest.Address } h.handleSwitchAccount(typedCommand) default: } } ================================================ FILE: proxy/vmess/outbound/config.go ================================================ package outbound ================================================ FILE: proxy/vmess/outbound/config.pb.go ================================================ package outbound import ( net "github.com/v2fly/v2ray-core/v5/common/net" protocol "github.com/v2fly/v2ray-core/v5/common/protocol" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Receiver []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=Receiver,proto3" json:"Receiver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_vmess_outbound_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_outbound_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_vmess_outbound_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetReceiver() []*protocol.ServerEndpoint { if x != nil { return x.Receiver } return nil } type SimplifiedConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Uuid string `protobuf:"bytes,3,opt,name=uuid,proto3" json:"uuid,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SimplifiedConfig) Reset() { *x = SimplifiedConfig{} mi := &file_proxy_vmess_outbound_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SimplifiedConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimplifiedConfig) ProtoMessage() {} func (x *SimplifiedConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_vmess_outbound_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimplifiedConfig.ProtoReflect.Descriptor instead. func (*SimplifiedConfig) Descriptor() ([]byte, []int) { return file_proxy_vmess_outbound_config_proto_rawDescGZIP(), []int{1} } func (x *SimplifiedConfig) GetAddress() *net.IPOrDomain { if x != nil { return x.Address } return nil } func (x *SimplifiedConfig) GetPort() uint32 { if x != nil { return x.Port } return 0 } func (x *SimplifiedConfig) GetUuid() string { if x != nil { return x.Uuid } return "" } var File_proxy_vmess_outbound_config_proto protoreflect.FileDescriptor const file_proxy_vmess_outbound_config_proto_rawDesc = "" + "\n" + "!proxy/vmess/outbound/config.proto\x12\x1fv2ray.core.proxy.vmess.outbound\x1a!common/protocol/server_spec.proto\x1a\x18common/net/address.proto\x1a common/protoext/extensions.proto\"P\n" + "\x06Config\x12F\n" + "\bReceiver\x18\x01 \x03(\v2*.v2ray.core.common.protocol.ServerEndpointR\bReceiver\"\x92\x01\n" + "\x10SimplifiedConfig\x12;\n" + "\aaddress\x18\x01 \x01(\v2!.v2ray.core.common.net.IPOrDomainR\aaddress\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\x12\x12\n" + "\x04uuid\x18\x03 \x01(\tR\x04uuid:\x19\x82\xb5\x18\x15\n" + "\boutbound\x12\x05vmess\x90\xff)\x01B~\n" + "#com.v2ray.core.proxy.vmess.outboundP\x01Z3github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound\xaa\x02\x1fV2Ray.Core.Proxy.Vmess.Outboundb\x06proto3" var ( file_proxy_vmess_outbound_config_proto_rawDescOnce sync.Once file_proxy_vmess_outbound_config_proto_rawDescData []byte ) func file_proxy_vmess_outbound_config_proto_rawDescGZIP() []byte { file_proxy_vmess_outbound_config_proto_rawDescOnce.Do(func() { file_proxy_vmess_outbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vmess_outbound_config_proto_rawDesc), len(file_proxy_vmess_outbound_config_proto_rawDesc))) }) return file_proxy_vmess_outbound_config_proto_rawDescData } var file_proxy_vmess_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_vmess_outbound_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.proxy.vmess.outbound.Config (*SimplifiedConfig)(nil), // 1: v2ray.core.proxy.vmess.outbound.SimplifiedConfig (*protocol.ServerEndpoint)(nil), // 2: v2ray.core.common.protocol.ServerEndpoint (*net.IPOrDomain)(nil), // 3: v2ray.core.common.net.IPOrDomain } var file_proxy_vmess_outbound_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.vmess.outbound.Config.Receiver:type_name -> v2ray.core.common.protocol.ServerEndpoint 3, // 1: v2ray.core.proxy.vmess.outbound.SimplifiedConfig.address:type_name -> v2ray.core.common.net.IPOrDomain 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_proxy_vmess_outbound_config_proto_init() } func file_proxy_vmess_outbound_config_proto_init() { if File_proxy_vmess_outbound_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vmess_outbound_config_proto_rawDesc), len(file_proxy_vmess_outbound_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_vmess_outbound_config_proto_goTypes, DependencyIndexes: file_proxy_vmess_outbound_config_proto_depIdxs, MessageInfos: file_proxy_vmess_outbound_config_proto_msgTypes, }.Build() File_proxy_vmess_outbound_config_proto = out.File file_proxy_vmess_outbound_config_proto_goTypes = nil file_proxy_vmess_outbound_config_proto_depIdxs = nil } ================================================ FILE: proxy/vmess/outbound/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.vmess.outbound; option csharp_namespace = "V2Ray.Core.Proxy.Vmess.Outbound"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound"; option java_package = "com.v2ray.core.proxy.vmess.outbound"; option java_multiple_files = true; import "common/protocol/server_spec.proto"; import "common/net/address.proto"; import "common/protoext/extensions.proto"; message Config { repeated v2ray.core.common.protocol.ServerEndpoint Receiver = 1; } message SimplifiedConfig { option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "vmess"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; v2ray.core.common.net.IPOrDomain address = 1; uint32 port = 2; string uuid = 3; } ================================================ FILE: proxy/vmess/outbound/errors.generated.go ================================================ package outbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/vmess/outbound/outbound.go ================================================ package outbound //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "context" "crypto/hmac" "crypto/sha256" "hash/crc64" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/platform" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/proxy" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/encoding" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" ) // Handler is an outbound connection handler for VMess protocol. type Handler struct { serverList *protocol.ServerList serverPicker protocol.ServerPicker policyManager policy.Manager } // New creates a new VMess outbound handler. func New(ctx context.Context, config *Config) (*Handler, error) { serverList := protocol.NewServerList() for _, rec := range config.Receiver { s, err := protocol.NewServerSpecFromPB(rec) if err != nil { return nil, newError("failed to parse server spec").Base(err) } serverList.AddServer(s) } v := core.MustFromContext(ctx) handler := &Handler{ serverList: serverList, serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return handler, nil } // Process implements proxy.Outbound.Process(). func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { var rec *protocol.ServerSpec var conn internet.Connection err := retry.ExponentialBackoff(5, 200).On(func() error { rec = h.serverPicker.PickServer() rawConn, err := dialer.Dial(ctx, rec.Destination()) if err != nil { return err } conn = rawConn return nil }) if err != nil { return newError("failed to find an available destination").Base(err).AtWarning() } defer conn.Close() outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified").AtError() } target := outbound.Target newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx)) command := protocol.RequestCommandTCP if target.Network == net.Network_UDP { command = protocol.RequestCommandUDP } if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" { command = protocol.RequestCommandMux } user := rec.PickUser() request := &protocol.RequestHeader{ Version: encoding.Version, User: user, Command: command, Address: target.Address, Port: target.Port, Option: protocol.RequestOptionChunkStream, } account := request.User.Account.(*vmess.MemoryAccount) request.Security = account.Security if request.Security == protocol.SecurityType_AES128_GCM || request.Security == protocol.SecurityType_NONE || request.Security == protocol.SecurityType_CHACHA20_POLY1305 { request.Option.Set(protocol.RequestOptionChunkMasking) } if shouldEnablePadding(request.Security) && request.Option.Has(protocol.RequestOptionChunkMasking) { request.Option.Set(protocol.RequestOptionGlobalPadding) } if request.Security == protocol.SecurityType_ZERO { request.Security = protocol.SecurityType_NONE request.Option.Clear(protocol.RequestOptionChunkStream) request.Option.Clear(protocol.RequestOptionChunkMasking) } if account.AuthenticatedLengthExperiment { request.Option.Set(protocol.RequestOptionAuthenticatedLength) } input := link.Reader output := link.Writer isAEAD := false if !aeadDisabled && len(account.AlterIDs) == 0 { isAEAD = true } hashkdf := hmac.New(sha256.New, []byte("VMessBF")) hashkdf.Write(account.ID.Bytes()) behaviorSeed := crc64.Checksum(hashkdf.Sum(nil), crc64.MakeTable(crc64.ISO)) session := encoding.NewClientSession(ctx, isAEAD, protocol.DefaultIDHash, int64(behaviorSeed)) sessionPolicy := h.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) requestDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) writer := buf.NewBufferedWriter(buf.NewWriter(conn)) if err := session.EncodeRequestHeader(request, writer); err != nil { return newError("failed to encode request").Base(err).AtWarning() } bodyWriter, err := session.EncodeRequestBody(request, writer) if err != nil { return newError("failed to start encoding").Base(err) } if err := buf.CopyOnceTimeout(input, bodyWriter, proxy.FirstPayloadTimeout); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { return newError("failed to write first payload").Base(err) } if err := writer.SetBuffered(false); err != nil { return err } if err := buf.Copy(input, bodyWriter, buf.UpdateActivity(timer)); err != nil { return err } if request.Option.Has(protocol.RequestOptionChunkStream) && !account.NoTerminationSignal { if err := bodyWriter.WriteMultiBuffer(buf.MultiBuffer{}); err != nil { return err } } return nil } responseDone := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) reader := &buf.BufferedReader{Reader: buf.NewReader(conn)} header, err := session.DecodeResponseHeader(reader) if err != nil { return newError("failed to read header").Base(err) } h.handleCommand(rec.Destination(), header.Command) bodyReader, err := session.DecodeResponseBody(request, reader) if err != nil { return newError("failed to start encoding response").Base(err) } return buf.Copy(bodyReader, output, buf.UpdateActivity(timer)) } responseDonePost := task.OnSuccess(responseDone, task.Close(output)) if err := task.Run(ctx, requestDone, responseDonePost); err != nil { return newError("connection ends").Base(err) } return nil } var ( enablePadding = false aeadDisabled = false ) func shouldEnablePadding(s protocol.SecurityType) bool { return enablePadding || s == protocol.SecurityType_AES128_GCM || s == protocol.SecurityType_CHACHA20_POLY1305 || s == protocol.SecurityType_AUTO } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return New(ctx, config.(*Config)) })) common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { simplifiedClient := config.(*SimplifiedConfig) fullClient := &Config{Receiver: []*protocol.ServerEndpoint{ { Address: simplifiedClient.Address, Port: simplifiedClient.Port, User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{Id: simplifiedClient.Uuid}), }, }, }, }} return common.CreateObject(ctx, fullClient) })) const defaultFlagValue = "NOT_DEFINED_AT_ALL" paddingValue := platform.NewEnvFlag("v2ray.vmess.padding").GetValue(func() string { return defaultFlagValue }) if paddingValue != defaultFlagValue { enablePadding = true } isAeadDisabled := platform.NewEnvFlag("v2ray.vmess.aead.disabled").GetValue(func() string { return defaultFlagValue }) if isAeadDisabled == "true" { aeadDisabled = true } } ================================================ FILE: proxy/vmess/validator.go ================================================ package vmess import ( "crypto/hmac" "crypto/sha256" "hash/crc64" "strings" "sync" "sync/atomic" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/proxy/vmess/aead" ) const ( updateInterval = 10 * time.Second cacheDurationSec = 120 ) type user struct { user protocol.MemoryUser lastSec protocol.Timestamp } // TimedUserValidator is a user Validator based on time. type TimedUserValidator struct { sync.RWMutex users []*user userHash map[[16]byte]indexTimePair hasher protocol.IDHash baseTime protocol.Timestamp task *task.Periodic behaviorSeed uint64 behaviorFused bool aeadDecoderHolder *aead.AuthIDDecoderHolder legacyWarnShown bool } type indexTimePair struct { user *user timeInc uint32 taintedFuse *uint32 } // NewTimedUserValidator creates a new TimedUserValidator. func NewTimedUserValidator(hasher protocol.IDHash) *TimedUserValidator { tuv := &TimedUserValidator{ users: make([]*user, 0, 16), userHash: make(map[[16]byte]indexTimePair, 1024), hasher: hasher, baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*2), aeadDecoderHolder: aead.NewAuthIDDecoderHolder(), } tuv.task = &task.Periodic{ Interval: updateInterval, Execute: func() error { tuv.updateUserHash() return nil }, } common.Must(tuv.task.Start()) return tuv } // visible for testing func (v *TimedUserValidator) GetBaseTime() protocol.Timestamp { return v.baseTime } func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) { var hashValue [16]byte genEndSec := nowSec + cacheDurationSec genHashForID := func(id *protocol.ID) { idHash := v.hasher(id.Bytes()) genBeginSec := user.lastSec if genBeginSec < nowSec-cacheDurationSec { genBeginSec = nowSec - cacheDurationSec } for ts := genBeginSec; ts <= genEndSec; ts++ { common.Must2(serial.WriteUint64(idHash, uint64(ts))) idHash.Sum(hashValue[:0]) idHash.Reset() v.userHash[hashValue] = indexTimePair{ user: user, timeInc: uint32(ts - v.baseTime), taintedFuse: new(uint32), } } } account := user.user.Account.(*MemoryAccount) genHashForID(account.ID) for _, id := range account.AlterIDs { genHashForID(id) } user.lastSec = genEndSec } func (v *TimedUserValidator) removeExpiredHashes(expire uint32) { for key, pair := range v.userHash { if pair.timeInc < expire { delete(v.userHash, key) } } } func (v *TimedUserValidator) updateUserHash() { now := time.Now() nowSec := protocol.Timestamp(now.Unix()) v.Lock() defer v.Unlock() for _, user := range v.users { v.generateNewHashes(nowSec, user) } expire := protocol.Timestamp(now.Unix() - cacheDurationSec) if expire > v.baseTime { v.removeExpiredHashes(uint32(expire - v.baseTime)) } } func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error { v.Lock() defer v.Unlock() nowSec := time.Now().Unix() uu := &user{ user: *u, lastSec: protocol.Timestamp(nowSec - cacheDurationSec), } v.users = append(v.users, uu) v.generateNewHashes(protocol.Timestamp(nowSec), uu) account := uu.user.Account.(*MemoryAccount) if !v.behaviorFused { hashkdf := hmac.New(sha256.New, []byte("VMESSBSKDF")) hashkdf.Write(account.ID.Bytes()) v.behaviorSeed = crc64.Update(v.behaviorSeed, crc64.MakeTable(crc64.ECMA), hashkdf.Sum(nil)) } var cmdkeyfl [16]byte copy(cmdkeyfl[:], account.ID.CmdKey()) v.aeadDecoderHolder.AddUser(cmdkeyfl, u) return nil } func (v *TimedUserValidator) Get(userHash []byte) (*protocol.MemoryUser, protocol.Timestamp, bool, error) { v.RLock() defer v.RUnlock() v.behaviorFused = true var fixedSizeHash [16]byte copy(fixedSizeHash[:], userHash) pair, found := v.userHash[fixedSizeHash] if found { user := pair.user.user if atomic.LoadUint32(pair.taintedFuse) == 0 { return &user, protocol.Timestamp(pair.timeInc) + v.baseTime, true, nil } return nil, 0, false, ErrTainted } return nil, 0, false, ErrNotFound } func (v *TimedUserValidator) GetAEAD(userHash []byte) (*protocol.MemoryUser, bool, error) { v.RLock() defer v.RUnlock() var userHashFL [16]byte copy(userHashFL[:], userHash) userd, err := v.aeadDecoderHolder.Match(userHashFL) if err != nil { return nil, false, err } return userd.(*protocol.MemoryUser), true, err } func (v *TimedUserValidator) Remove(email string) bool { v.Lock() defer v.Unlock() email = strings.ToLower(email) idx := -1 for i, u := range v.users { if strings.EqualFold(u.user.Email, email) { idx = i var cmdkeyfl [16]byte copy(cmdkeyfl[:], u.user.Account.(*MemoryAccount).ID.CmdKey()) v.aeadDecoderHolder.RemoveUser(cmdkeyfl) break } } if idx == -1 { return false } ulen := len(v.users) v.users[idx] = v.users[ulen-1] v.users[ulen-1] = nil v.users = v.users[:ulen-1] return true } // Close implements common.Closable. func (v *TimedUserValidator) Close() error { return v.task.Close() } func (v *TimedUserValidator) GetBehaviorSeed() uint64 { v.Lock() defer v.Unlock() v.behaviorFused = true if v.behaviorSeed == 0 { v.behaviorSeed = dice.RollUint64() } return v.behaviorSeed } func (v *TimedUserValidator) BurnTaintFuse(userHash []byte) error { v.RLock() defer v.RUnlock() var userHashFL [16]byte copy(userHashFL[:], userHash) pair, found := v.userHash[userHashFL] if found { if atomic.CompareAndSwapUint32(pair.taintedFuse, 0, 1) { return nil } return ErrTainted } return ErrNotFound } /* ShouldShowLegacyWarn will return whether a Legacy Warning should be shown Not guaranteed to only return true once for every inbound, but it is okay. */ func (v *TimedUserValidator) ShouldShowLegacyWarn() bool { if v.legacyWarnShown { return false } v.legacyWarnShown = true return true } var ErrNotFound = newError("Not Found") var ErrTainted = newError("ErrTainted") ================================================ FILE: proxy/vmess/validator_test.go ================================================ package vmess_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" . "github.com/v2fly/v2ray-core/v5/proxy/vmess" ) func toAccount(a *Account) protocol.Account { account, err := a.AsAccount() common.Must(err) return account } func TestUserValidator(t *testing.T) { hasher := protocol.DefaultIDHash v := NewTimedUserValidator(hasher) defer common.Close(v) id := uuid.New() user := &protocol.MemoryUser{ Email: "test", Account: toAccount(&Account{ Id: id.String(), AlterId: 8, }), } common.Must(v.Add(user)) { testSmallLag := func(lag int64) { ts := int64(v.GetBaseTime()) + lag + 240 idHash := hasher(id.Bytes()) common.Must2(serial.WriteUint64(idHash, uint64(ts))) userHash := idHash.Sum(nil) euser, ets, found, _ := v.Get(userHash) if !found { t.Fatal("user not found") } if euser.Email != user.Email { t.Error("unexpected user email: ", euser.Email, " want ", user.Email) } if int64(ets) != ts { t.Error("unexpected timestamp: ", ets, " want ", ts) } } testSmallLag(0) testSmallLag(40) testSmallLag(-40) testSmallLag(80) testSmallLag(-80) testSmallLag(120) testSmallLag(-120) } { testBigLag := func(lag int64) { ts := int64(v.GetBaseTime()) + lag + 240 idHash := hasher(id.Bytes()) common.Must2(serial.WriteUint64(idHash, uint64(ts))) userHash := idHash.Sum(nil) euser, _, found, _ := v.Get(userHash) if found || euser != nil { t.Error("unexpected user") } } testBigLag(121) testBigLag(-121) testBigLag(310) testBigLag(-310) testBigLag(500) testBigLag(-500) } if v := v.Remove(user.Email); !v { t.Error("unable to remove user") } if v := v.Remove(user.Email); v { t.Error("remove user twice") } } func BenchmarkUserValidator(b *testing.B) { for i := 0; i < b.N; i++ { hasher := protocol.DefaultIDHash v := NewTimedUserValidator(hasher) for j := 0; j < 1500; j++ { id := uuid.New() v.Add(&protocol.MemoryUser{ Email: "test", Account: toAccount(&Account{ Id: id.String(), AlterId: 16, }), }) } common.Close(v) } } ================================================ FILE: proxy/vmess/vmess.go ================================================ // Package vmess contains the implementation of VMess protocol and transportation. // // VMess contains both inbound and outbound connections. VMess inbound is usually used on servers // together with 'freedom' to talk to final destination, while VMess outbound is usually used on // clients with 'socks' for proxying. package vmess //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: proxy/vmess/vmessCtxInterface.go ================================================ package vmess // example const AlterID = "VMessCtxInterface_AlterID" ================================================ FILE: proxy/wireguard/outbound/config.pb.go ================================================ package outbound import ( _ "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" gvisorstack "github.com/v2fly/v2ray-core/v5/common/packetswitch/gvisorstack" _ "github.com/v2fly/v2ray-core/v5/common/protoext" wgcommon "github.com/v2fly/v2ray-core/v5/proxy/wireguard/wgcommon" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config_DomainStrategy int32 const ( Config_AS_IS Config_DomainStrategy = 0 Config_USE_IP Config_DomainStrategy = 1 Config_USE_IP4 Config_DomainStrategy = 2 Config_USE_IP6 Config_DomainStrategy = 3 ) // Enum value maps for Config_DomainStrategy. var ( Config_DomainStrategy_name = map[int32]string{ 0: "AS_IS", 1: "USE_IP", 2: "USE_IP4", 3: "USE_IP6", } Config_DomainStrategy_value = map[string]int32{ "AS_IS": 0, "USE_IP": 1, "USE_IP4": 2, "USE_IP6": 3, } ) func (x Config_DomainStrategy) Enum() *Config_DomainStrategy { p := new(Config_DomainStrategy) *p = x return p } func (x Config_DomainStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Config_DomainStrategy) Descriptor() protoreflect.EnumDescriptor { return file_proxy_wireguard_outbound_config_proto_enumTypes[0].Descriptor() } func (Config_DomainStrategy) Type() protoreflect.EnumType { return &file_proxy_wireguard_outbound_config_proto_enumTypes[0] } func (x Config_DomainStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Config_DomainStrategy.Descriptor instead. func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) { return file_proxy_wireguard_outbound_config_proto_rawDescGZIP(), []int{0, 0} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` WgDevice *wgcommon.DeviceConfig `protobuf:"bytes,1,opt,name=wg_device,json=wgDevice,proto3" json:"wg_device,omitempty"` Stack *gvisorstack.Config `protobuf:"bytes,2,opt,name=stack,proto3" json:"stack,omitempty"` // v2ray.core.net.packetaddr.PacketAddrType outbound_packet_encoding = 3; ListenOnSystemNetwork bool `protobuf:"varint,4,opt,name=listen_on_system_network,json=listenOnSystemNetwork,proto3" json:"listen_on_system_network,omitempty"` DomainStrategy Config_DomainStrategy `protobuf:"varint,5,opt,name=domain_strategy,json=domainStrategy,proto3,enum=v2ray.core.proxy.wireguard.outbound.Config_DomainStrategy" json:"domain_strategy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_proxy_wireguard_outbound_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_proxy_wireguard_outbound_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_proxy_wireguard_outbound_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetWgDevice() *wgcommon.DeviceConfig { if x != nil { return x.WgDevice } return nil } func (x *Config) GetStack() *gvisorstack.Config { if x != nil { return x.Stack } return nil } func (x *Config) GetListenOnSystemNetwork() bool { if x != nil { return x.ListenOnSystemNetwork } return false } func (x *Config) GetDomainStrategy() Config_DomainStrategy { if x != nil { return x.DomainStrategy } return Config_AS_IS } var File_proxy_wireguard_outbound_config_proto protoreflect.FileDescriptor const file_proxy_wireguard_outbound_config_proto_rawDesc = "" + "\n" + "%proxy/wireguard/outbound/config.proto\x12#v2ray.core.proxy.wireguard.outbound\x1a%proxy/wireguard/wgcommon/config.proto\x1a,common/packetswitch/gvisorstack/config.proto\x1a\"common/net/packetaddr/config.proto\x1a common/protoext/extensions.proto\"\x9e\x03\n" + "\x06Config\x12N\n" + "\twg_device\x18\x01 \x01(\v21.v2ray.core.proxy.wireguard.wgcommon.DeviceConfigR\bwgDevice\x12H\n" + "\x05stack\x18\x02 \x01(\v22.v2ray.core.common.packetswitch.gvisorstack.ConfigR\x05stack\x127\n" + "\x18listen_on_system_network\x18\x04 \x01(\bR\x15listenOnSystemNetwork\x12c\n" + "\x0fdomain_strategy\x18\x05 \x01(\x0e2:.v2ray.core.proxy.wireguard.outbound.Config.DomainStrategyR\x0edomainStrategy\"A\n" + "\x0eDomainStrategy\x12\t\n" + "\x05AS_IS\x10\x00\x12\n" + "\n" + "\x06USE_IP\x10\x01\x12\v\n" + "\aUSE_IP4\x10\x02\x12\v\n" + "\aUSE_IP6\x10\x03:\x19\x82\xb5\x18\x15\n" + "\boutbound\x12\twireguardB\x8a\x01\n" + "'com.v2ray.core.proxy.wireguard.outboundP\x01Z7github.com/v2fly/v2ray-core/v5/proxy/wireguard/outbound\xaa\x02#V2Ray.Core.Proxy.Wireguard.Outboundb\x06proto3" var ( file_proxy_wireguard_outbound_config_proto_rawDescOnce sync.Once file_proxy_wireguard_outbound_config_proto_rawDescData []byte ) func file_proxy_wireguard_outbound_config_proto_rawDescGZIP() []byte { file_proxy_wireguard_outbound_config_proto_rawDescOnce.Do(func() { file_proxy_wireguard_outbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_wireguard_outbound_config_proto_rawDesc), len(file_proxy_wireguard_outbound_config_proto_rawDesc))) }) return file_proxy_wireguard_outbound_config_proto_rawDescData } var file_proxy_wireguard_outbound_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_proxy_wireguard_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_wireguard_outbound_config_proto_goTypes = []any{ (Config_DomainStrategy)(0), // 0: v2ray.core.proxy.wireguard.outbound.Config.DomainStrategy (*Config)(nil), // 1: v2ray.core.proxy.wireguard.outbound.Config (*wgcommon.DeviceConfig)(nil), // 2: v2ray.core.proxy.wireguard.wgcommon.DeviceConfig (*gvisorstack.Config)(nil), // 3: v2ray.core.common.packetswitch.gvisorstack.Config } var file_proxy_wireguard_outbound_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.proxy.wireguard.outbound.Config.wg_device:type_name -> v2ray.core.proxy.wireguard.wgcommon.DeviceConfig 3, // 1: v2ray.core.proxy.wireguard.outbound.Config.stack:type_name -> v2ray.core.common.packetswitch.gvisorstack.Config 0, // 2: v2ray.core.proxy.wireguard.outbound.Config.domain_strategy:type_name -> v2ray.core.proxy.wireguard.outbound.Config.DomainStrategy 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_proxy_wireguard_outbound_config_proto_init() } func file_proxy_wireguard_outbound_config_proto_init() { if File_proxy_wireguard_outbound_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_wireguard_outbound_config_proto_rawDesc), len(file_proxy_wireguard_outbound_config_proto_rawDesc)), NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_wireguard_outbound_config_proto_goTypes, DependencyIndexes: file_proxy_wireguard_outbound_config_proto_depIdxs, EnumInfos: file_proxy_wireguard_outbound_config_proto_enumTypes, MessageInfos: file_proxy_wireguard_outbound_config_proto_msgTypes, }.Build() File_proxy_wireguard_outbound_config_proto = out.File file_proxy_wireguard_outbound_config_proto_goTypes = nil file_proxy_wireguard_outbound_config_proto_depIdxs = nil } ================================================ FILE: proxy/wireguard/outbound/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.wireguard.outbound; option csharp_namespace = "V2Ray.Core.Proxy.Wireguard.Outbound"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/wireguard/outbound"; option java_package = "com.v2ray.core.proxy.wireguard.outbound"; option java_multiple_files = true; import "proxy/wireguard/wgcommon/config.proto"; import "common/packetswitch/gvisorstack/config.proto"; import "common/net/packetaddr/config.proto"; import "common/protoext/extensions.proto"; message Config{ option (v2ray.core.common.protoext.message_opt).type = "outbound"; option (v2ray.core.common.protoext.message_opt).short_name = "wireguard"; v2ray.core.proxy.wireguard.wgcommon.DeviceConfig wg_device = 1; v2ray.core.common.packetswitch.gvisorstack.Config stack = 2; //v2ray.core.net.packetaddr.PacketAddrType outbound_packet_encoding = 3; bool listen_on_system_network = 4; enum DomainStrategy { AS_IS = 0; USE_IP = 1; USE_IP4 = 2; USE_IP6 = 3; } DomainStrategy domain_strategy = 5; } ================================================ FILE: proxy/wireguard/outbound/errors.generated.go ================================================ package outbound import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/wireguard/outbound/outbound.go ================================================ package outbound import ( "context" gonet "net" "sync" "time" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/dualStack/happyEyeball" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" cnet "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/packetswitch/gvisorstack" "github.com/v2fly/v2ray-core/v5/common/packetswitch/interconnect" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/proxy/wireguard/wgcommon" "github.com/v2fly/v2ray-core/v5/transport" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func NewWireguardOutbound(ctx context.Context, config *Config) (*WireguardOutbound, error) { w := &WireguardOutbound{ ctx: ctx, config: config, } // Acquire dns client feature if available if err := core.RequireFeatures(ctx, func(d dns.Client) error { w.dnsClient = d return nil }); err != nil { return nil, newError("failed to require dns client feature").Base(err) } storage := envctx.EnvironmentFromContext(ctx).(environment.ProxyEnvironment).TransientStorage() udpState, err := NewClientConnState() if err != nil { return nil, newError("failed to create UDP connection state").Base(err) } if err := storage.Put(ctx, ConnectionState, udpState); err != nil { return nil, newError("failed to put connection state").Base(err) } return w, nil } type WireguardOutbound struct { ctx context.Context config *Config dnsClient dns.Client } type WireguardOutboundSession struct { ctx context.Context config *Config stack *gvisorstack.WrappedStack wireguardDevice *wgcommon.WrappedWireguardDevice interconnect *interconnect.NetworkLayerCable // system packet conn used when ListenOnSystemNetwork is true systemPacketConn internet.PacketConn dnsClient dns.Client } func (s *WireguardOutboundSession) initFromConfig(ctx context.Context, config *Config) error { if config == nil { return newError("nil config") } // create interconnect cable cable, err := interconnect.NewNetworkLayerCable(ctx) if err != nil { return newError("failed to create interconnect cable").Base(err) } s.interconnect = cable // create wireguard device wrapper wd, err := wgcommon.NewWrappedWireguardDevice(ctx, config.GetWgDevice()) if err != nil { return newError("failed to create wireguard device").Base(err) } s.wireguardDevice = wd // attach device tunnel to left side of cable s.wireguardDevice.SetTunnel(cable.GetLSideDevice()) // create gvisor stack wrapper if stack config is provided if config.GetStack() != nil { st, err := gvisorstack.NewStack(ctx, config.GetStack()) if err != nil { return newError("failed to create gvisor stack").Base(err) } s.stack = st if err := s.stack.CreateStackFromNetworkLayerDevice(cable.GetRSideDevice()); err != nil { return newError("failed to create stack from network layer device").Base(err) } } return nil } const ConnectionState = "ConnectionState" type ClientConnState struct { session *WireguardOutboundSession initOnce *sync.Once mu sync.Mutex } func (c *ClientConnState) GetOrCreateSession(create func() (*WireguardOutboundSession, error)) (*WireguardOutboundSession, error) { var errOuter error c.initOnce.Do(func() { sess, err := create() if err != nil { errOuter = err return } c.mu.Lock() c.session = sess c.mu.Unlock() }) if errOuter != nil { return nil, newError("failed to initialize UDP State").Base(errOuter) } return c.session, nil } func (c *ClientConnState) IsTransientStorageLifecycleReceiver() {} func (c *ClientConnState) Close() error { c.mu.Lock() defer c.mu.Unlock() if c.session == nil { return nil } sess := c.session c.session = nil // close interconnect devices first to stop any further packet injections if sess.interconnect != nil { _ = sess.interconnect.GetLSideDevice().Close() _ = sess.interconnect.GetRSideDevice().Close() sess.interconnect = nil } // close system packet conn if sess.systemPacketConn != nil { _ = sess.systemPacketConn.Close() sess.systemPacketConn = nil } // close wireguard device if sess.wireguardDevice != nil { _ = sess.wireguardDevice.Close() sess.wireguardDevice = nil } // Close stack last to quiesce any gVisor internal goroutines that may // hold references to PacketBuffers (prevents dec-ref races). if sess.stack != nil { _ = sess.stack.Close() sess.stack = nil } return nil } func (w *WireguardOutbound) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { // keep dialer for address family preference when resolving domain _ = dialer storage := envctx.EnvironmentFromContext(w.ctx).(environment.ProxyEnvironment).TransientStorage() stateIfc, err := storage.Get(ctx, ConnectionState) if err != nil { return newError("failed to get connection state").Base(err) } clientState, ok := stateIfc.(*ClientConnState) if !ok { return newError("bad connection state") } // create session if needed sess, err := clientState.GetOrCreateSession(func() (*WireguardOutboundSession, error) { s := &WireguardOutboundSession{ctx: ctx, config: w.config} s.dnsClient = w.dnsClient if err := s.initFromConfig(ctx, w.config); err != nil { return nil, err } if !w.config.ListenOnSystemNetwork { // SORRRRRY, I tried but it was v2ray's udp support was too difficult to work with return nil, newError("unimplemented: listenOnSystemNetwork=false is not implemented yet") } packetConn, err := internet.ListenSystemPacket(w.ctx, &gonet.UDPAddr{IP: cnet.AnyIP.IP(), Port: 0}, nil) if err != nil { return nil, newError("failed to listen on system network").Base(err) } s.systemPacketConn = packetConn s.wireguardDevice.SetConn(packetConn) // initialize wireguard device now that conn present if err := s.wireguardDevice.InitDevice(); err != nil { return nil, newError("failed to init wireguard device").Base(err) } if err := s.wireguardDevice.SetupDeviceWithoutPeers(); err != nil { return nil, newError("failed to setup wireguard device").Base(err) } if err := s.wireguardDevice.AddOrReplacePeers(s.config.WgDevice.GetPeers()); err != nil { return nil, newError("failed to add peers").Base(err) } if err := s.wireguardDevice.Up(); err != nil { return nil, newError("failed to bring up wireguard device").Base(err) } return s, nil }) if err != nil { return newError("failed to create or fetch session").Base(err) } { debugData, err := sess.wireguardDevice.Debug() if err != nil { newError("failed to debug wireguard device").Base(err).WriteToLog(session.ExportIDToError(ctx)) } newError("wireguard device debug: \n", debugData).AtDebug().WriteToLog(session.ExportIDToError(ctx)) } outbound := session.OutboundFromContext(ctx) if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified") } destination := outbound.Target // require gVisor stack to process network-level connections if sess.stack == nil { return newError("gvisor stack is not configured for wireguard outbound") } ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, time.Second*300) defer cancel() if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil { defer func() { _ = packetConn.Close() }() pc, err := sess.stack.ListenUDP(ctx, cnet.UDPDestination(nil, 0)) if err != nil { return newError("failed to create udp session in stack").Base(err) } defer func() { _ = pc.Close() }() // Run copy loops and explicitly close resources afterwards to avoid leaks. err = nil func() { requestDone := func() error { protocolWriter := pc return udp.CopyPacketConn(protocolWriter, packetConn, udp.UpdateActivity(timer)) } responseDone := func() error { protocolReader := pc return udp.CopyPacketConn(packetConn, protocolReader, udp.UpdateActivity(timer)) } responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer)) err = task.Run(ctx, requestDone, responseDoneAndCloseWriter) }() if err != nil { return newError("connection ends").Base(err) } return nil } switch destination.Network { case cnet.Network_TCP: // Dial TCP inside the virtual stack ips := w.resolveDNSName(ctx, destination, sess) var dialedConn gonet.Conn if len(ips) == 0 { conn, err := sess.stack.DialTCP(ctx, destination) if err != nil { return newError("failed to dial tcp in stack").Base(err) } dialedConn = conn newError("dialed ", destination, " with no DNS resolution").AtDebug().WriteToLog(session.ExportIDToError(ctx)) } else { conn, err := happyEyeball.RacingDialer(ctx, destination, ips, func(ctx context.Context, domainDestination cnet.Destination, ips cnet.IP) (internet.Connection, error) { dest := cnet.Destination{Network: domainDestination.Network, Address: cnet.IPAddress(ips), Port: domainDestination.Port} return sess.stack.DialTCP(ctx, dest) }, true, time.Millisecond*300) if err != nil { return newError("failed to dial tcp in stack with racing dialer").Base(err) } dialedConn = conn } defer func() { _ = dialedConn.Close() }() requestDone := func() error { writer := buf.NewWriter(dialedConn) if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to copy request").Base(err) } return nil } responseDone := func() error { reader := buf.NewReader(dialedConn) if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to copy response").Base(err) } return nil } if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(link.Writer))); err != nil { return newError("connection ends").Base(err) } return nil case cnet.Network_UDP: // Create a packet conn on the stack and use mono-dest adapter pc, err := sess.stack.ListenUDP(ctx, cnet.UDPDestination(nil, 0)) if err != nil { return newError("failed to create udp session in stack").Base(err) } mono := udp.NewMonoDestUDPConn(pc, &gonet.UDPAddr{IP: destination.Address.IP(), Port: int(destination.Port)}) requestDone := func() error { return buf.Copy(link.Reader, mono, buf.UpdateActivity(timer)) } responseDone := func() error { return buf.Copy(mono, link.Writer, buf.UpdateActivity(timer)) } if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(link.Writer))); err != nil { _ = pc.Close() return newError("connection ends").Base(err) } return nil default: return newError("unsupported network: ", destination.Network) } } func (w *WireguardOutbound) resolveDNSName(ctx context.Context, destination cnet.Destination, sess *WireguardOutboundSession) []cnet.IP { // resolve domain names using dns client if necessary if destination.Address != nil && destination.Address.Family().IsDomain() && sess.dnsClient != nil { domain := destination.Address.Domain() opt := dns.IPOption{ IPv4Enable: sess.config.DomainStrategy == Config_USE_IP || sess.config.DomainStrategy == Config_USE_IP4, IPv6Enable: sess.config.DomainStrategy == Config_USE_IP || sess.config.DomainStrategy == Config_USE_IP6, FakeEnable: false, } ips, err := dns.LookupIPWithOption(sess.dnsClient, domain, opt) if err != nil { newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) } return ips } return nil } func (w *WireguardOutbound) Close() error { storage := envctx.EnvironmentFromContext(w.ctx).(environment.ProxyEnvironment).TransientStorage() stateIfc, err := storage.Get(context.Background(), ConnectionState) if err != nil || stateIfc == nil { return nil } clientState, ok := stateIfc.(*ClientConnState) if !ok || clientState.session == nil { return nil } _ = clientState.Close() return nil } func NewClientConnState() (*ClientConnState, error) { return &ClientConnState{initOnce: &sync.Once{}}, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewWireguardOutbound(ctx, config.(*Config)) })) } ================================================ FILE: proxy/wireguard/wgcommon/config.pb.go ================================================ package wgcommon import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type PeerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` PresharedKey []byte `protobuf:"bytes,2,opt,name=preshared_key,json=presharedKey,proto3" json:"preshared_key,omitempty"` AllowedIps []string `protobuf:"bytes,3,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"` Endpoint string `protobuf:"bytes,4,opt,name=endpoint,proto3" json:"endpoint,omitempty"` PersistentKeepaliveInterval int64 `protobuf:"varint,5,opt,name=persistent_keepalive_interval,json=persistentKeepaliveInterval,proto3" json:"persistent_keepalive_interval,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PeerConfig) Reset() { *x = PeerConfig{} mi := &file_proxy_wireguard_wgcommon_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PeerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerConfig) ProtoMessage() {} func (x *PeerConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_wireguard_wgcommon_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerConfig.ProtoReflect.Descriptor instead. func (*PeerConfig) Descriptor() ([]byte, []int) { return file_proxy_wireguard_wgcommon_config_proto_rawDescGZIP(), []int{0} } func (x *PeerConfig) GetPublicKey() []byte { if x != nil { return x.PublicKey } return nil } func (x *PeerConfig) GetPresharedKey() []byte { if x != nil { return x.PresharedKey } return nil } func (x *PeerConfig) GetAllowedIps() []string { if x != nil { return x.AllowedIps } return nil } func (x *PeerConfig) GetEndpoint() string { if x != nil { return x.Endpoint } return "" } func (x *PeerConfig) GetPersistentKeepaliveInterval() int64 { if x != nil { return x.PersistentKeepaliveInterval } return 0 } type DeviceConfig struct { state protoimpl.MessageState `protogen:"open.v1"` PrivateKey []byte `protobuf:"bytes,1,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` ListenPort uint32 `protobuf:"varint,3,opt,name=listen_port,json=listenPort,proto3" json:"listen_port,omitempty"` Peers []*PeerConfig `protobuf:"bytes,4,rep,name=peers,proto3" json:"peers,omitempty"` Mtu uint32 `protobuf:"varint,5,opt,name=mtu,proto3" json:"mtu,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DeviceConfig) Reset() { *x = DeviceConfig{} mi := &file_proxy_wireguard_wgcommon_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DeviceConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeviceConfig) ProtoMessage() {} func (x *DeviceConfig) ProtoReflect() protoreflect.Message { mi := &file_proxy_wireguard_wgcommon_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeviceConfig.ProtoReflect.Descriptor instead. func (*DeviceConfig) Descriptor() ([]byte, []int) { return file_proxy_wireguard_wgcommon_config_proto_rawDescGZIP(), []int{1} } func (x *DeviceConfig) GetPrivateKey() []byte { if x != nil { return x.PrivateKey } return nil } func (x *DeviceConfig) GetListenPort() uint32 { if x != nil { return x.ListenPort } return 0 } func (x *DeviceConfig) GetPeers() []*PeerConfig { if x != nil { return x.Peers } return nil } func (x *DeviceConfig) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } var File_proxy_wireguard_wgcommon_config_proto protoreflect.FileDescriptor const file_proxy_wireguard_wgcommon_config_proto_rawDesc = "" + "\n" + "%proxy/wireguard/wgcommon/config.proto\x12#v2ray.core.proxy.wireguard.wgcommon\"\xd1\x01\n" + "\n" + "PeerConfig\x12\x1d\n" + "\n" + "public_key\x18\x01 \x01(\fR\tpublicKey\x12#\n" + "\rpreshared_key\x18\x02 \x01(\fR\fpresharedKey\x12\x1f\n" + "\vallowed_ips\x18\x03 \x03(\tR\n" + "allowedIps\x12\x1a\n" + "\bendpoint\x18\x04 \x01(\tR\bendpoint\x12B\n" + "\x1dpersistent_keepalive_interval\x18\x05 \x01(\x03R\x1bpersistentKeepaliveInterval\"\xa9\x01\n" + "\fDeviceConfig\x12\x1f\n" + "\vprivate_key\x18\x01 \x01(\fR\n" + "privateKey\x12\x1f\n" + "\vlisten_port\x18\x03 \x01(\rR\n" + "listenPort\x12E\n" + "\x05peers\x18\x04 \x03(\v2/.v2ray.core.proxy.wireguard.wgcommon.PeerConfigR\x05peers\x12\x10\n" + "\x03mtu\x18\x05 \x01(\rR\x03mtuB\x8a\x01\n" + "'com.v2ray.core.proxy.wireguard.wgcommonP\x01Z7github.com/v2fly/v2ray-core/v5/proxy/wireguard/wgcommon\xaa\x02#V2Ray.Core.Proxy.Wireguard.Wgcommonb\x06proto3" var ( file_proxy_wireguard_wgcommon_config_proto_rawDescOnce sync.Once file_proxy_wireguard_wgcommon_config_proto_rawDescData []byte ) func file_proxy_wireguard_wgcommon_config_proto_rawDescGZIP() []byte { file_proxy_wireguard_wgcommon_config_proto_rawDescOnce.Do(func() { file_proxy_wireguard_wgcommon_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_wireguard_wgcommon_config_proto_rawDesc), len(file_proxy_wireguard_wgcommon_config_proto_rawDesc))) }) return file_proxy_wireguard_wgcommon_config_proto_rawDescData } var file_proxy_wireguard_wgcommon_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_wireguard_wgcommon_config_proto_goTypes = []any{ (*PeerConfig)(nil), // 0: v2ray.core.proxy.wireguard.wgcommon.PeerConfig (*DeviceConfig)(nil), // 1: v2ray.core.proxy.wireguard.wgcommon.DeviceConfig } var file_proxy_wireguard_wgcommon_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.proxy.wireguard.wgcommon.DeviceConfig.peers:type_name -> v2ray.core.proxy.wireguard.wgcommon.PeerConfig 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_proxy_wireguard_wgcommon_config_proto_init() } func file_proxy_wireguard_wgcommon_config_proto_init() { if File_proxy_wireguard_wgcommon_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_wireguard_wgcommon_config_proto_rawDesc), len(file_proxy_wireguard_wgcommon_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_wireguard_wgcommon_config_proto_goTypes, DependencyIndexes: file_proxy_wireguard_wgcommon_config_proto_depIdxs, MessageInfos: file_proxy_wireguard_wgcommon_config_proto_msgTypes, }.Build() File_proxy_wireguard_wgcommon_config_proto = out.File file_proxy_wireguard_wgcommon_config_proto_goTypes = nil file_proxy_wireguard_wgcommon_config_proto_depIdxs = nil } ================================================ FILE: proxy/wireguard/wgcommon/config.proto ================================================ syntax = "proto3"; package v2ray.core.proxy.wireguard.wgcommon; option csharp_namespace = "V2Ray.Core.Proxy.Wireguard.Wgcommon"; option go_package = "github.com/v2fly/v2ray-core/v5/proxy/wireguard/wgcommon"; option java_package = "com.v2ray.core.proxy.wireguard.wgcommon"; option java_multiple_files = true; message PeerConfig { bytes public_key = 1; bytes preshared_key = 2; repeated string allowed_ips = 3; string endpoint = 4; int64 persistent_keepalive_interval = 5; } message DeviceConfig { bytes private_key = 1; uint32 listen_port = 3; repeated PeerConfig peers = 4; uint32 mtu = 5; } ================================================ FILE: proxy/wireguard/wgcommon/errors.generated.go ================================================ package wgcommon import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: proxy/wireguard/wgcommon/filterDebug.go ================================================ package wgcommon import ( "encoding/base64" "encoding/hex" "strings" ) func filterDebugData(in string) string { lines := strings.Split(in, "\n") outLines := make([]string, 0, len(lines)) for _, line := range lines { trimmedLeft := strings.TrimLeft(line, " \t") switch { case strings.HasPrefix(trimmedLeft, "private_key="): continue case strings.HasPrefix(trimmedLeft, "preshared_key="): continue case strings.HasPrefix(trimmedLeft, "public_key="): leading := line[:len(line)-len(trimmedLeft)] value := strings.TrimSpace(trimmedLeft[len("public_key="):]) decoded, err := hex.DecodeString(value) if err != nil { outLines = append(outLines, line) continue } encoded := base64.StdEncoding.EncodeToString(decoded) outLines = append(outLines, leading+"public_key="+encoded) continue default: outLines = append(outLines, line) } } return strings.Join(outLines, "\n") } ================================================ FILE: proxy/wireguard/wgcommon/setup.go ================================================ package wgcommon import ( "errors" "fmt" "strings" "golang.zx2c4.com/wireguard/device" ) func (w *WrappedWireguardDevice) InitDevice() error { if w == nil || w.config == nil { return errors.New("wireguard: missing config") } if w.device != nil { return errors.New("wireguard: device already initialized") } // Create a tun device adaptor from the packetswitch network layer device. // Use a reasonable default MTU and batch sizes. These can be tuned later. tunDev, err := NewNetworkLayerDeviceToWireguardTunDeviceAdaptor(int(w.config.Mtu), w.tunnel, 1, 1024) if err != nil { return err } // Create wireguard bind adapter from provided PacketConn bind := NewNetPacketConnToWg(w.conn) // Create the wireguard device with our logger adapter. dev := device.NewDevice(tunDev, bind, NewDeviceLoggerAdapter()) if dev == nil { return errors.New("wireguard: failed to initialize device") } w.device = dev return nil } func (w *WrappedWireguardDevice) SetupDeviceWithoutPeers() error { if w == nil || w.config == nil { return errors.New("wireguard: missing config") } if w.device == nil { return errors.New("wireguard: device not initialized") } var sb strings.Builder if len(w.config.PrivateKey) > 0 { _, _ = fmt.Fprintf(&sb, "private_key=%x\n", w.config.PrivateKey) } if w.config.ListenPort != 0 { _, _ = fmt.Fprintf(&sb, "listen_port=%d\n", w.config.ListenPort) } // Terminate operation with a blank line. sb.WriteString("\n") return w.device.IpcSet(sb.String()) } func (w *WrappedWireguardDevice) AddOrReplacePeers(peers []*PeerConfig) error { if w == nil || w.config == nil { return errors.New("wireguard: missing config") } if w.device == nil { return errors.New("wireguard: device not initialized") } var sb strings.Builder // Replace existing peers with the provided list sb.WriteString("replace_peers=true\n") for _, p := range peers { if p == nil || len(p.PublicKey) == 0 { // skip empty entries continue } // start peer block _, _ = fmt.Fprintf(&sb, "public_key=%x\n", p.PublicKey) if len(p.PresharedKey) > 0 { _, _ = fmt.Fprintf(&sb, "preshared_key=%x\n", p.PresharedKey) } if p.Endpoint != "" { _, _ = fmt.Fprintf(&sb, "endpoint=%s\n", p.Endpoint) } if p.PersistentKeepaliveInterval != 0 { _, _ = fmt.Fprintf(&sb, "persistent_keepalive_interval=%d\n", p.PersistentKeepaliveInterval) } // replace allowed IPs for this peer sb.WriteString("replace_allowed_ips=true\n") for _, aip := range p.AllowedIps { if aip == "" { continue } _, _ = fmt.Fprintf(&sb, "allowed_ip=%s\n", aip) } } // terminate sb.WriteString("\n") return w.device.IpcSet(sb.String()) } func (w *WrappedWireguardDevice) RemovePeer(publicKey []byte) error { if w == nil { return errors.New("wireguard: nil receiver") } if w.device == nil { return errors.New("wireguard: device not initialized") } if len(publicKey) == 0 { return errors.New("wireguard: empty public key") } var sb strings.Builder _, _ = fmt.Fprintf(&sb, "public_key=%x\n", publicKey) sb.WriteString("remove=true\n") sb.WriteString("\n") return w.device.IpcSet(sb.String()) } ================================================ FILE: proxy/wireguard/wgcommon/wgConnAdaptor.go ================================================ package wgcommon import ( gonet "net" "net/netip" "sync" "time" "golang.zx2c4.com/wireguard/conn" "github.com/v2fly/v2ray-core/v5/common/net" ) // netPacketConnToWg is machine generated type netPacketConnToWg struct { mu sync.Mutex conn net.PacketConn actualPort uint16 closed bool // tracks whether the bind is logically closed (not the conn) } // NewNetPacketConnToWg constructs a wireguard conn.Bind adapter from a // common/net.PacketConn. It returns a Bind implementation that delegates // reads/writes to the provided PacketConn. // // Important: the Bind does NOT own the PacketConn lifecycle. WireGuard calls // Close() + Open() internally during BindUpdate(); Close() here only marks // the bind as logically closed without closing the underlying conn, so that // Open() can re-use it. func NewNetPacketConnToWg(c net.PacketConn) conn.Bind { if c == nil { return &netPacketConnToWg{} } n := &netPacketConnToWg{conn: c, closed: true} if la := c.LocalAddr(); la != nil { if ua, ok := la.(*gonet.UDPAddr); ok { n.actualPort = uint16(ua.Port) } } return n } // wgEndpoint is a minimal implementation of conn.Endpoint backed by netip.AddrPort. type wgEndpoint struct { ap netip.AddrPort hasSrc bool srcIP netip.Addr } func (e *wgEndpoint) ClearSrc() { e.hasSrc = false } func (e *wgEndpoint) SrcToString() string { if !e.hasSrc { return "" } // return just IP (no port) if src port is unknown return e.srcIP.String() } func (e *wgEndpoint) DstToString() string { return e.ap.String() } func (e *wgEndpoint) DstToBytes() []byte { b, _ := e.ap.MarshalBinary() return b } func (e *wgEndpoint) DstIP() netip.Addr { return e.ap.Addr() } func (e *wgEndpoint) SrcIP() netip.Addr { if e.hasSrc { return e.srcIP } return netip.Addr{} } func (n *netPacketConnToWg) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) { if n.conn == nil { return nil, 0, nil } n.mu.Lock() n.closed = false n.mu.Unlock() // Clear the read deadline that Close() may have set so reads can proceed. _ = n.conn.SetReadDeadline(time.Time{}) // determine actualPort from LocalAddr if possible if la := n.conn.LocalAddr(); la != nil { if ua, ok := la.(*gonet.UDPAddr); ok { n.actualPort = uint16(ua.Port) } } actualPort = n.actualPort fn := func(packets [][]byte, sizes []int, eps []conn.Endpoint) (int, error) { var i int for i = 0; i < len(packets); i++ { nRead, addr, err := n.conn.ReadFrom(packets[i]) if err != nil { if i == 0 { return 0, err } return i, nil } sizes[i] = nRead // build endpoint from addr if udpAddr, ok := addr.(*gonet.UDPAddr); ok { ip, _ := netip.AddrFromSlice(udpAddr.IP) ap := netip.AddrPortFrom(ip, uint16(udpAddr.Port)) eps[i] = &wgEndpoint{ap: ap} } else { // fallback: parse string s := addr.String() if ap, perr := netip.ParseAddrPort(s); perr == nil { eps[i] = &wgEndpoint{ap: ap} } else { eps[i] = &wgEndpoint{} } } } return i, nil } return []conn.ReceiveFunc{fn}, actualPort, nil } func (n *netPacketConnToWg) Close() error { n.mu.Lock() defer n.mu.Unlock() n.closed = true // Do NOT close the underlying conn here. WireGuard calls Close()+Open() // internally during BindUpdate(). The actual PacketConn lifecycle is // managed externally by the session that created it. // // Set a past read deadline to unblock any pending ReadFrom calls in // receive goroutines so that WireGuard's stopping.Wait() can complete. if n.conn != nil { _ = n.conn.SetReadDeadline(time.Unix(1, 0)) } return nil } func (n *netPacketConnToWg) SetMark(mark uint32) error { // best-effort: underlying PacketConn may not support setting fwmark; ignore. return nil } func (n *netPacketConnToWg) Send(bufs [][]byte, ep conn.Endpoint) error { if n.conn == nil { return nil } // Use DstToString to obtain "ip:port" and resolve to UDPAddr addrStr := ep.DstToString() udpAddr, err := gonet.ResolveUDPAddr("udp", addrStr) if err != nil { return err } for _, b := range bufs { if _, werr := n.conn.WriteTo(b, udpAddr); werr != nil { return werr } } return nil } func (n *netPacketConnToWg) ParseEndpoint(s string) (conn.Endpoint, error) { ap, err := netip.ParseAddrPort(s) if err != nil { return nil, err } return &wgEndpoint{ap: ap}, nil } func (n *netPacketConnToWg) BatchSize() int { // underlying common/net.PacketConn may not support batch; report 1. return 1 } ================================================ FILE: proxy/wireguard/wgcommon/wgConnAdaptor_test.go ================================================ package wgcommon import ( "net" "testing" "time" "golang.zx2c4.com/wireguard/conn" ) func TestNetPacketConnToWg_OpenReceive_Send(t *testing.T) { // setup a UDP listener (server) svAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") if err != nil { t.Fatal(err) } svConn, err := net.ListenUDP("udp", svAddr) if err != nil { t.Fatal(err) } defer func() { _ = svConn.Close() }() // client clConn, err := net.DialUDP("udp", nil, svConn.LocalAddr().(*net.UDPAddr)) if err != nil { t.Fatal(err) } defer func() { _ = clConn.Close() }() // Wrap server conn as Bind bind := NewNetPacketConnToWg(svConn) fns, port, err := bind.Open(0) if err != nil { t.Fatal(err) } if port == 0 { // LocalAddr should have set actualPort, otherwise use svConn if la := svConn.LocalAddr(); la != nil { if ua, ok := la.(*net.UDPAddr); ok && ua.Port != 0 { port = uint16(ua.Port) _ = port } } } if len(fns) == 0 { t.Fatal("no receive functions returned") } recvFn := fns[0] // run receiver in a goroutine recvBuf := make([]byte, 1500) sizes := make([]int, 1) eps := make([]conn.Endpoint, 1) ch := make(chan error, 1) go func() { _, err := recvFn([][]byte{recvBuf}, sizes, eps) ch <- err }() // send a message from client to server msg := []byte("hello-wg") if _, err := clConn.Write(msg); err != nil { t.Fatal(err) } // wait for receive select { case err := <-ch: if err != nil { t.Fatal(err) } case <-time.After(time.Second): t.Fatal("timeout waiting for receive") } // verify sizes and endpoint if sizes[0] != len(msg) { t.Fatalf("unexpected size: got %d want %d", sizes[0], len(msg)) } if eps[0] == nil { t.Fatal("nil endpoint returned") } // endpoint DstToString should be the client's address if eps[0].DstToString() != clConn.LocalAddr().String() { t.Fatalf("unexpected endpoint dst: %s", eps[0].DstToString()) } // Test Send: use a separate client conn to receive rcv2, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) if err != nil { t.Fatal(err) } defer func() { _ = rcv2.Close() }() // create a bind adapter from a separate unconnected "sender" socket senderConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) if err != nil { t.Fatal(err) } defer func() { _ = senderConn.Close() }() senderBind := NewNetPacketConnToWg(senderConn) // parse endpoint for rcv2 ep, err := senderBind.ParseEndpoint(rcv2.LocalAddr().String()) if err != nil { t.Fatal(err) } // send from senderBind to rcv2 via Send p := [][]byte{[]byte("ping")} if err := senderBind.Send(p, ep); err != nil { t.Fatal(err) } // read on rcv2 buf := make([]byte, 1500) if err := rcv2.SetReadDeadline(time.Now().Add(time.Second)); err != nil { t.Fatal(err) } n, _, err := rcv2.ReadFromUDP(buf) if err != nil { t.Fatal(err) } if n != len(p[0]) { t.Fatalf("unexpected recv len: %d", n) } _ = buf } func TestParseEndpointAndBatchSizeAndNilConstructor(t *testing.T) { // Parse valid IPv4 endpoint ep, err := NewNetPacketConnToWg(nil).ParseEndpoint("127.0.0.1:12345") if err != nil { t.Fatalf("ParseEndpoint failed: %v", err) } if ep == nil { t.Fatal("expected endpoint, got nil") } if ep.DstToString() != "127.0.0.1:12345" { t.Fatalf("unexpected DstToString: %s", ep.DstToString()) } // Parse IPv6 endpoint ipv6ep, err := NewNetPacketConnToWg(nil).ParseEndpoint("[::1]:54321") if err != nil { t.Fatalf("ParseEndpoint v6 failed: %v", err) } if ipv6ep == nil { t.Fatal("expected ipv6 endpoint, got nil") } // BatchSize and Close/SetMark for nil-constructed adapter bind := NewNetPacketConnToWg(nil) if bind.BatchSize() != 1 { t.Fatalf("unexpected batch size: %d", bind.BatchSize()) } // Close and SetMark should be no-ops and not panic if err := bind.Close(); err != nil { t.Fatalf("Close on nil adapter returned error: %v", err) } if err := bind.SetMark(123); err != nil { t.Fatalf("SetMark on nil adapter returned error: %v", err) } } func TestSendWithConnectedSocketProducesError(t *testing.T) { // create a server to target target, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) if err != nil { t.Fatal(err) } defer func() { _ = target.Close() }() // create a connected client socket (DialUDP) cl, err := net.DialUDP("udp", nil, target.LocalAddr().(*net.UDPAddr)) if err != nil { t.Fatal(err) } defer func() { _ = cl.Close() }() bind := NewNetPacketConnToWg(cl) ep, err := bind.ParseEndpoint("127.0.0.1:1") if err != nil { t.Fatal(err) } // Attempting to Send with a connected socket uses WriteTo and is expected // to return an error about using WriteTo on a pre-connected connection. p := [][]byte{[]byte("x")} err = bind.Send(p, ep) if err == nil { t.Fatalf("expected error when calling Send on adapter wrapping connected socket, got nil") } } func TestReceiveMultiplePackets(t *testing.T) { // server sv, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) if err != nil { t.Fatal(err) } defer func() { _ = sv.Close() }() // client c, err := net.DialUDP("udp", nil, sv.LocalAddr().(*net.UDPAddr)) if err != nil { t.Fatal(err) } defer func() { _ = c.Close() }() bind := NewNetPacketConnToWg(sv) fns, _, err := bind.Open(0) if err != nil { t.Fatal(err) } if len(fns) == 0 { t.Fatal("no receive functions") } recv := fns[0] // prepare two buffers b1 := make([]byte, 64) b2 := make([]byte, 64) sizes := make([]int, 2) eps := make([]conn.Endpoint, 2) ch := make(chan error, 1) go func() { _, err := recv([][]byte{b1, b2}, sizes, eps) ch <- err }() // send two packets quickly if _, err := c.Write([]byte("one")); err != nil { t.Fatal(err) } if _, err := c.Write([]byte("two")); err != nil { t.Fatal(err) } select { case err := <-ch: if err != nil { t.Fatal(err) } case <-time.After(time.Second): t.Fatal("timeout waiting for batched receive") } if sizes[0] == 0 && sizes[1] == 0 { t.Fatalf("expected at least one packet size to be non-zero") } } ================================================ FILE: proxy/wireguard/wgcommon/wgDeviceAdaptor.go ================================================ package wgcommon import ( "errors" "os" "sync" "golang.zx2c4.com/wireguard/tun" "github.com/v2fly/v2ray-core/v5/common/packetswitch" ) func NewNetworkLayerDeviceToWireguardTunDeviceAdaptor(mtu int, networkLayerSwitch packetswitch.NetworkLayerDevice, batchSize int, inboundChannelSize int) (*NetworkLayerDeviceToWireguardTunDeviceAdaptor, error) { if batchSize <= 0 { batchSize = 1 } if inboundChannelSize <= 0 { inboundChannelSize = 1024 } n := &NetworkLayerDeviceToWireguardTunDeviceAdaptor{ mtu: mtu, networkLayerSwitch: networkLayerSwitch, in: make(chan []byte, inboundChannelSize), events: make(chan tun.Event, 4), batchSize: batchSize, } // Attach writer to the network layer switch so incoming packets are delivered to this adaptor. if networkLayerSwitch != nil { if err := networkLayerSwitch.OnAttach(&networkLayerWriter{parent: n}); err != nil { return nil, err } // If the underlying device exposes real link events, forward them. if src, ok := networkLayerSwitch.(interface{ Events() <-chan tun.Event }); ok { go func() { for ev := range src.Events() { // Acquire lock to synchronize with Close (which closes the events channel). n.mu.Lock() closed := n.closed if !closed { // best-effort: do not block if events channel is full select { case n.events <- ev: default: } } n.mu.Unlock() if closed { return } } }() } } return n, nil } // NetworkLayerDeviceToWireguardTunDeviceAdaptor is primarily machine generated. type NetworkLayerDeviceToWireguardTunDeviceAdaptor struct { mtu int networkLayerSwitch packetswitch.NetworkLayerDevice mu sync.RWMutex closed bool in chan []byte events chan tun.Event batchSize int } // networkLayerWriter adapts packetswitch writes into the adaptor's incoming channel. type networkLayerWriter struct { parent *NetworkLayerDeviceToWireguardTunDeviceAdaptor } func (w *networkLayerWriter) Write(packet []byte) (int, error) { p := make([]byte, len(packet)) copy(p, packet) w.parent.mu.RLock() closed := w.parent.closed w.parent.mu.RUnlock() if closed { return 0, errors.New("device closed") } select { case w.parent.in <- p: return len(packet), nil default: // Channel full, drop packet. return 0, errors.New("no buffer space") } } func (n *NetworkLayerDeviceToWireguardTunDeviceAdaptor) File() *os.File { // No underlying OS file for this adaptor. return nil } func (n *NetworkLayerDeviceToWireguardTunDeviceAdaptor) Read(bufs [][]byte, sizes []int, offset int) (ret int, err error) { // Read up to BatchSize packets or until bufs exhausted. // NOTE: 'offset' is a byte offset within each buffer (to leave room for transport headers), // NOT an index into the bufs slice. WireGuard expects packet data to be written starting at // bufs[i][offset:]. maxCount := n.BatchSize() if maxCount <= 0 { maxCount = 1 } for i := 0; i < maxCount && i < len(bufs); i++ { var b []byte var ok bool if ret == 0 { // first read: block until a packet arrives or channel closes b, ok = <-n.in } else { // subsequent reads: do not block — if no packet available, return what we've got select { case b, ok = <-n.in: // got one default: return ret, nil } } if !ok { // channel closed if ret == 0 { return 0, os.ErrClosed } return ret, nil } to := bufs[i] if to == nil { // packet consumed but no destination buffer provided, skip copying ret++ continue } copied := copy(to[offset:], b) if sizes != nil && i < len(sizes) { sizes[i] = copied } ret++ } return ret, nil } func (n *NetworkLayerDeviceToWireguardTunDeviceAdaptor) Write(bufs [][]byte, offset int) (int, error) { written := 0 if n.networkLayerSwitch == nil { return 0, errors.New("no network layer writer attached") } for i := 0; i < len(bufs); i++ { b := bufs[i] if b == nil || len(b) <= offset { continue } // The offset is a byte offset within each buffer where the actual // packet payload starts (after transport headers). Extract only the // payload portion. Copy because caller may reuse buffer. payload := b[offset:] cp := make([]byte, len(payload)) copy(cp, payload) _, err := n.networkLayerSwitch.Write(cp) if err != nil { return written, err } written++ } return written, nil } func (n *NetworkLayerDeviceToWireguardTunDeviceAdaptor) MTU() (int, error) { return n.mtu, nil } func (n *NetworkLayerDeviceToWireguardTunDeviceAdaptor) Name() (string, error) { // No specific name available for this virtual adaptor. return "", nil } func (n *NetworkLayerDeviceToWireguardTunDeviceAdaptor) Events() <-chan tun.Event { return n.events } func (n *NetworkLayerDeviceToWireguardTunDeviceAdaptor) Close() error { n.mu.Lock() if n.closed { n.mu.Unlock() return nil } n.closed = true close(n.in) // Close events channel to signal no more events. close(n.events) dev := n.networkLayerSwitch n.networkLayerSwitch = nil n.mu.Unlock() if dev != nil { _ = dev.Close() } return nil } func (n *NetworkLayerDeviceToWireguardTunDeviceAdaptor) BatchSize() int { return n.batchSize } ================================================ FILE: proxy/wireguard/wgcommon/wgDeviceAdaptor_test.go ================================================ package wgcommon import ( "reflect" "sync" "testing" "time" "golang.zx2c4.com/wireguard/tun" "github.com/v2fly/v2ray-core/v5/common/packetswitch" ) // fakeNetDevice implements packetswitch.NetworkLayerDevice and optionally exposes Events(). type fakeNetDevice struct { mu sync.Mutex writer packetswitch.NetworkLayerPacketWriter writes [][]byte closed bool events chan tun.Event } func (f *fakeNetDevice) OnAttach(w packetswitch.NetworkLayerPacketWriter) error { f.mu.Lock() defer f.mu.Unlock() f.writer = w return nil } func (f *fakeNetDevice) Write(packet []byte) (int, error) { f.mu.Lock() defer f.mu.Unlock() if f.closed { return 0, errClosed } cp := make([]byte, len(packet)) copy(cp, packet) f.writes = append(f.writes, cp) return len(packet), nil } func (f *fakeNetDevice) Close() error { f.mu.Lock() f.closed = true f.mu.Unlock() return nil } func (f *fakeNetDevice) getWriter() packetswitch.NetworkLayerPacketWriter { f.mu.Lock() w := f.writer f.mu.Unlock() return w } func (f *fakeNetDevice) lastWrite() []byte { f.mu.Lock() defer f.mu.Unlock() if len(f.writes) == 0 { return nil } return f.writes[len(f.writes)-1] } // Provide Events() so adaptor can forward events when present. func (f *fakeNetDevice) Events() <-chan tun.Event { return f.events } var errClosed = &fakeError{"closed"} type fakeError struct{ s string } func (e *fakeError) Error() string { return e.s } func TestNewAdaptor_ReadWrite_Basic(t *testing.T) { fd := &fakeNetDevice{events: make(chan tun.Event, 4)} // batchSize 2, inboundChannelSize 4 a, err := NewNetworkLayerDeviceToWireguardTunDeviceAdaptor(1500, fd, 2, 4) if err != nil { t.Fatalf("constructor failed: %v", err) } w := fd.getWriter() if w == nil { t.Fatal("expected writer to be attached to fake device") } p1 := []byte{0x01, 0x02, 0x03} if _, err := w.Write(p1); err != nil { t.Fatalf("writer.Write failed: %v", err) } bufs := make([][]byte, 2) bufs[0] = make([]byte, 64) bufs[1] = make([]byte, 64) sizes := make([]int, 2) ret, err := a.Read(bufs, sizes, 0) if err != nil { t.Fatalf("Read returned error: %v", err) } if ret != 1 { t.Fatalf("expected 1 packet read, got %d", ret) } if sizes[0] != len(p1) { t.Fatalf("expected sizes[0]=%d, got %d", len(p1), sizes[0]) } if !reflect.DeepEqual(bufs[0][:sizes[0]], p1) { t.Fatalf("payload mismatch: got %v want %v", bufs[0][:sizes[0]], p1) } // Now write two packets and read both p2 := []byte{0x0a, 0x0b} p3 := []byte{0x0c} if _, err := w.Write(p2); err != nil { t.Fatalf("writer.Write p2 failed: %v", err) } if _, err := w.Write(p3); err != nil { t.Fatalf("writer.Write p3 failed: %v", err) } bufs2 := make([][]byte, 2) bufs2[0] = make([]byte, 16) bufs2[1] = make([]byte, 16) sizes2 := make([]int, 2) ret2, err := a.Read(bufs2, sizes2, 0) if err != nil { t.Fatalf("Read returned error: %v", err) } if ret2 != 2 { t.Fatalf("expected 2 packets read, got %d", ret2) } if sizes2[0] != len(p2) || sizes2[1] != len(p3) { t.Fatalf("unexpected sizes: %v", sizes2) } } func TestNewAdaptor_WriteToNetwork(t *testing.T) { fd := &fakeNetDevice{events: make(chan tun.Event, 4)} a, err := NewNetworkLayerDeviceToWireguardTunDeviceAdaptor(1500, fd, 1, 4) if err != nil { t.Fatalf("constructor failed: %v", err) } payload := []byte{0xaa, 0xbb, 0xcc} bufs := make([][]byte, 1) bufs[0] = payload written, err := a.Write(bufs, 0) if err != nil { t.Fatalf("Write returned error: %v", err) } if written != 1 { t.Fatalf("expected 1 written, got %d", written) } lw := fd.lastWrite() if !reflect.DeepEqual(lw, payload) { t.Fatalf("device write mismatch: got %v want %v", lw, payload) } } func TestNewAdaptor_InboundBufferFullDrops(t *testing.T) { fd := &fakeNetDevice{events: make(chan tun.Event, 4)} // inboundChannelSize 1 so second write should fail a, err := NewNetworkLayerDeviceToWireguardTunDeviceAdaptor(1500, fd, 2, 1) if err != nil { t.Fatalf("constructor failed: %v", err) } w := fd.getWriter() if w == nil { t.Fatal("expected writer to be attached to fake device") } p1 := []byte{0x01} p2 := []byte{0x02} if _, err := w.Write(p1); err != nil { t.Fatalf("first write failed: %v", err) } // second write should return error due to buffer full if _, err := w.Write(p2); err == nil { t.Fatalf("expected second write to fail due to full buffer") } bufs := make([][]byte, 1) bufs[0] = make([]byte, 8) sizes := make([]int, 1) ret, err := a.Read(bufs, sizes, 0) if err != nil { t.Fatalf("Read returned error: %v", err) } if ret != 1 { t.Fatalf("expected 1 packet read, got %d", ret) } } func TestNewAdaptor_ReadWithNonZeroOffset(t *testing.T) { // This test reproduces the 100% CPU bug where offset was misinterpreted // as a buffer index rather than a byte offset within each buffer. fd := &fakeNetDevice{events: make(chan tun.Event, 4)} a, err := NewNetworkLayerDeviceToWireguardTunDeviceAdaptor(1500, fd, 1, 4) if err != nil { t.Fatalf("constructor failed: %v", err) } w := fd.getWriter() if w == nil { t.Fatal("expected writer to be attached") } p1 := []byte{0xDE, 0xAD, 0xBE, 0xEF} if _, err := w.Write(p1); err != nil { t.Fatalf("writer.Write failed: %v", err) } // Use offset=16 to mimic WireGuard's MessageTransportHeaderSize const offset = 16 bufs := make([][]byte, 1) bufs[0] = make([]byte, 64) sizes := make([]int, 1) ret, err := a.Read(bufs, sizes, offset) if err != nil { t.Fatalf("Read returned error: %v", err) } if ret != 1 { t.Fatalf("expected 1 packet read, got %d", ret) } if sizes[0] != len(p1) { t.Fatalf("expected sizes[0]=%d, got %d", len(p1), sizes[0]) } // Data should be at bufs[0][offset:offset+sizes[0]], not bufs[0][0:sizes[0]] got := bufs[0][offset : offset+sizes[0]] if !reflect.DeepEqual(got, p1) { t.Fatalf("payload mismatch: got %v want %v", got, p1) } // The header area before offset should be untouched (all zeros) for i := 0; i < offset; i++ { if bufs[0][i] != 0 { t.Fatalf("byte at position %d was modified: %x", i, bufs[0][i]) } } } func TestNewAdaptor_EventForwardingAndClose(t *testing.T) { fd := &fakeNetDevice{events: make(chan tun.Event, 4)} a, err := NewNetworkLayerDeviceToWireguardTunDeviceAdaptor(1500, fd, 1, 4) if err != nil { t.Fatalf("constructor failed: %v", err) } // send event from underlying device fd.events <- tun.EventUp select { case ev := <-a.Events(): if ev != tun.EventUp { t.Fatalf("expected EventUp, got %v", ev) } case <-time.After(time.Second): t.Fatal("timeout waiting for forwarded event") } // Close adaptor and ensure events channel is closed if err := a.Close(); err != nil { t.Fatalf("Close returned error: %v", err) } // reading from closed events channel should return immediately with ok==false select { case _, ok := <-a.Events(): if ok { t.Fatal("expected events channel to be closed") } case <-time.After(time.Second): t.Fatal("timeout waiting for events channel close") } } ================================================ FILE: proxy/wireguard/wgcommon/wgLogAdaptor.go ================================================ package wgcommon import ( "fmt" "golang.zx2c4.com/wireguard/device" "github.com/v2fly/v2ray-core/v5/common/errors" ) // NewDeviceLoggerAdapter returns a wireguard device.Logger that forwards // verbose and error logs into the project's error logger using errors.New(...). // Verbosef logs are recorded as Debug, Errorf logs are recorded as Error. // machine generated func NewDeviceLoggerAdapter() *device.Logger { l := &device.Logger{} l.Verbosef = func(format string, args ...any) { msg := fmt.Sprintf(format, args...) err := errors.New(msg) err.AtDebug().WriteToLog() } l.Errorf = func(format string, args ...any) { msg := fmt.Sprintf(format, args...) err := errors.New(msg) err.AtError().WriteToLog() } return l } ================================================ FILE: proxy/wireguard/wgcommon/wgcommon.go ================================================ package wgcommon //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: proxy/wireguard/wgcommon/wgdevice.go ================================================ package wgcommon import ( "context" "golang.zx2c4.com/wireguard/device" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/packetswitch" ) func NewWrappedWireguardDevice(ctx context.Context, config *DeviceConfig) (*WrappedWireguardDevice, error) { return &WrappedWireguardDevice{ config: config, ctx: ctx, }, nil } type WrappedWireguardDevice struct { config *DeviceConfig ctx context.Context device *device.Device tunnel packetswitch.NetworkLayerDevice conn net.PacketConn } func (w *WrappedWireguardDevice) Up() error { if w.device != nil { return w.device.Up() } return newError("wireguard device do not exist").AtError() } // SetTunnel sets the network layer tunnel device for the wrapped WireGuard device. func (w *WrappedWireguardDevice) SetTunnel(t packetswitch.NetworkLayerDevice) { w.tunnel = t } // SetConn sets the underlying packet connection used by the wrapped WireGuard device. func (w *WrappedWireguardDevice) SetConn(c net.PacketConn) { w.conn = c } func (w *WrappedWireguardDevice) Close() error { if w == nil { return nil } // Bring device down if initialized if w.device != nil { _ = w.device.Down() w.device = nil } // Close tunnel if present if w.tunnel != nil { _ = w.tunnel.Close() w.tunnel = nil } // Close underlying packet conn if present if w.conn != nil { _ = w.conn.Close() w.conn = nil } return nil } func (w *WrappedWireguardDevice) Debug() (string, error) { if w.device != nil { result, err := w.device.IpcGet() if err != nil { return "", err } return filterDebugData(result), nil } return "", nil } ================================================ FILE: release/config/config.json ================================================ // Config file of V2Ray. This file follows standard JSON format, with comments support. // Uncomment entries below to satisfy your needs. Also read our manual for more detail at // https://www.v2fly.org/ { "log": { // By default, V2Ray writes access log to stdout. // "access": "/path/to/access/log/file", // By default, V2Ray write error log to stdout. // "error": "/path/to/error/log/file", // Log level, one of "debug", "info", "warning", "error", "none" "loglevel": "warning" }, // List of inbound proxy configurations. "inbounds": [{ // Port to listen on. You may need root access if the value is less than 1024. "port": 1080, // IP address to listen on. Change to "0.0.0.0" to listen on all network interfaces. "listen": "127.0.0.1", // Tag of the inbound proxy. May be used for routing. "tag": "socks-inbound", // Protocol name of inbound proxy. "protocol": "socks", // Settings of the protocol. Varies based on protocol. "settings": { "auth": "noauth", "udp": false, "ip": "127.0.0.1" }, // Enable sniffing on TCP connection. "sniffing": { "enabled": true, // Target domain will be overriden to the one carried by the connection, if the connection is HTTP or HTTPS. "destOverride": ["http", "tls"] } }], // List of outbound proxy configurations. "outbounds": [{ // Protocol name of the outbound proxy. "protocol": "freedom", // Settings of the protocol. Varies based on protocol. "settings": {}, // Tag of the outbound. May be used for routing. "tag": "direct" },{ "protocol": "blackhole", "settings": {}, "tag": "blocked" }], // Transport is for global transport settings. If you have multiple transports with same settings // (say mKCP), you may put it here, instead of in each individual inbound/outbounds. //"transport": {}, // Routing controls how traffic from inbounds are sent to outbounds. "routing": { "domainStrategy": "IPOnDemand", "rules":[ { // Blocks access to private IPs. Remove this if you want to access your router. "type": "field", "ip": ["geoip:private"], "outboundTag": "blocked" }, { // Blocks major ads. "type": "field", "domain": ["geosite:category-ads"], "outboundTag": "blocked" } ] }, // Dns settings for domain resolution. "dns": { // Static hosts, similar to hosts file. "hosts": { // Match v2fly.org to another domain on CloudFlare. This domain will be used when querying IPs for v2fly.org. "domain:v2fly.org": "www.vicemc.net", // The following settings help to eliminate DNS poisoning in mainland China. // It is safe to comment these out if this is not the case for you. "domain:github.io": "pages.github.com", "domain:wikipedia.org": "www.wikimedia.org", "domain:shadowsocks.org": "electronicsrealm.com" }, "servers": [ "1.1.1.1", { "address": "114.114.114.114", "port": 53, // List of domains that use this DNS first. "domains": [ "geosite:cn" ] }, "8.8.8.8", "localhost" ] }, // Policy controls some internal behavior of how V2Ray handles connections. // It may be on connection level by user levels in 'levels', or global settings in 'system.' "policy": { // Connection policys by user levels "levels": { "0": { "uplinkOnly": 0, "downlinkOnly": 0 } }, "system": { "statsInboundUplink": false, "statsInboundDownlink": false, "statsOutboundUplink": false, "statsOutboundDownlink": false } }, // Stats enables internal stats counter. // This setting can be used together with Policy and Api. //"stats":{}, // Api enables gRPC APIs for external programs to communicate with V2Ray instance. //"api": { //"tag": "api", //"services": [ // "HandlerService", // "LoggerService", // "StatsService" //] //}, // You may add other entries to the configuration, but they will not be recognized by V2Ray. "other": {} } ================================================ FILE: release/config/systemd/system/v2ray.service ================================================ [Unit] Description=V2Ray Service Documentation=https://www.v2fly.org/ After=network.target nss-lookup.target [Service] User=nobody CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE NoNewPrivileges=true ExecStart=/usr/local/bin/v2ray run -config /usr/local/etc/v2ray/config.json Restart=on-failure RestartPreventExitStatus=23 [Install] WantedBy=multi-user.target ================================================ FILE: release/config/systemd/system/v2ray@.service ================================================ [Unit] Description=V2Ray Service Documentation=https://www.v2fly.org/ After=network.target nss-lookup.target [Service] User=nobody CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE NoNewPrivileges=true ExecStart=/usr/local/bin/v2ray run -config /usr/local/etc/v2ray/%i.json Restart=on-failure RestartPreventExitStatus=23 [Install] WantedBy=multi-user.target ================================================ FILE: release/config/vpoint_socks_vmess.json ================================================ { "log": { "loglevel": "warning" }, "inbounds": [{ "port": 1080, "listen": "127.0.0.1", "protocol": "socks", "settings": { "auth": "noauth", "udp": false, "ip": "127.0.0.1" } }], "outbounds": [{ "protocol": "freedom", "settings": {}, "tag": "direct" }], "policy": { "levels": { "0": {"uplinkOnly": 0} } } } ================================================ FILE: release/config/vpoint_vmess_freedom.json ================================================ { "inbounds": [{ "port": 10086, "protocol": "vmess", "settings": { "clients": [ { "id": "23ad6b10-8d1a-40f7-8ad0-e3e35cd38297", "level": 1, "alterId": 64 } ] } }], "outbounds": [{ "protocol": "freedom", "settings": {} },{ "protocol": "blackhole", "settings": {}, "tag": "blocked" }], "routing": { "rules": [ { "type": "field", "ip": ["geoip:private"], "outboundTag": "blocked" } ] } } ================================================ FILE: release/container/Containerfile ================================================ # golang:1.21.4 linux/amd64 FROM docker.io/library/golang@sha256:337543447173c2238c78d4851456760dcc57c1dfa8c3bcd94cbee8b0f7b32ad0 AS builder FROM scratch COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /etc/passwd /etc/passwd COPY --from=builder /etc/group /etc/group COPY --from=builder /tmp /tmp COPY --from=builder /dev /dev ENV v2ray.location.asset=/opt/v2ray/share COPY ./ /opt/v2ray/ ENTRYPOINT [ "/opt/v2ray/bin/v2ray" ] CMD [ "run", "-config", "/etc/v2ray/config.json" ] ================================================ FILE: release/container/downloadAssets.sh ================================================ #!/bin/bash set -x -e download() { curl -L "https://github.com/${RELEASE_REPO}/releases/download/$1/$2" >"$2" } downloadAndUnzip() { download "$1" "$2" unzip -n -d "${2%\.zip}" "$2" } mkdir -p assets pushd assets downloadAndUnzip "$1" "v2ray-linux-$2.zip" downloadAndUnzip "$1" "v2ray-extra.zip" popd placeFile() { mkdir -p "context/$2" cp -R "assets/$1/$3" "context/$2/$3" } function generateStandardVersion() { placeFile "$1" "$2/bin" "v2ray" } function generateExtraVersion() { generateStandardVersion "$1" "$2" placeFile "$1" "$2/share" "geosite.dat" placeFile "$1" "$2/share" "geoip.dat" placeFile "$1" "$2/etc" "config.json" placeFile "v2ray-extra" "$2/share" "browserforwarder" } if [ "$4" = "std" ]; then generateStandardVersion "v2ray-linux-$2" "linux/$3/std" fi if [ "$4" = "extra" ]; then generateExtraVersion "v2ray-linux-$2" "linux/$3/extra" fi ================================================ FILE: release/debian/changelog ================================================ v2ray-core (4.42.2-2) unstable; urgency=medium * Support Windows ARM64 * TLS: support client certificate authentication * GeoIP asset: add trimmed GeoIP file `geoip-only-cn-private.dat` to zip package for ROM/RAM insufficient devices * Socks: support 4/4a version of the socks protocol * DNS: add option `disableFallbackIfMatch` for DNS * More details in https://github.com/v2fly/v2ray-core/releases/tag/v4.42.2 -- V2Fly Mon, 20 Sep 2021 11:00:00 +0800 v2ray-core (4.41.1-1) unstable; urgency=medium * VMess: added two VMess experiments: AuthenticatedLength & NoTerminationSignal * Draining connection at client side when receiving invalid data * Observatory: support custom probe interval and probe URL * Fix: connection stability issue in HTTP/2 & gRPC transport * More details in https://github.com/v2fly/v2ray-core/releases/tag/v4.41.1 -- V2Fly Wed, 18 Aug 2021 10:00:00 +0800 v2ray-core (4.40.1-1) unstable; urgency=medium * DNS: support DNS over TCP * Fix: new certification issuing incorrectly delayed * More details in https://github.com/v2fly/v2ray-core/releases/tag/v4.40.1 -- V2Fly Tue, 22 Jun 2021 22:00:00 +0800 v2ray-core (4.39.2-1) unstable; urgency=medium * Websocket: support header based Websocket early data & its partial browser forwarder support * GeoData: add a memory efficient geodata decoder called `memconservative` for memory-limited devices * HTTP/2 Transport: support to set method and headers for outgoing connections * TCP Socket Option: support to set keepalive interval on Linux operating system * Fixed BrowserForwarder panics with empty config * Fixed FakeDNS prints error with empty config * Fixed dual stack FakeDNS Close method * Fixed Observatory starts with empty config & fails to close * Fixed null check on alternative system dialer * Fixed the chain proxy support for gRPC and HTTP/2 transport * Fixed leastping logic * Fixed v2ctl unable to create geodata loaders * More details in https://github.com/v2fly/v2ray-core/releases/tag/v4.39.2 -- V2Fly Wed, 26 May 2021 08:00:00 +0800 v2ray-core (4.38.3-1) unstable; urgency=medium * FakeDNS: add fakedns+others sniffer * FakeDNS: support dual stack IP pool by default * Observatory: A component that measures the connectivity of selected outbounds * Multi-JSON support for Observatory & BrowserForwarder * Routing: add leastPing balancing strategy * Fix: FakeDNS crash * Fix: FakeDNS return ErrEmptyResponse when no FakeIP found * Fix: UDP DNS connection crash * More details in https://github.com/v2fly/v2ray-core/releases/tag/v4.38.3 -- V2Fly Wed, 5 May 2021 10:00:00 +0800 v2ray-core (4.37.3-1) unstable; urgency=medium * DNS: hosts support multiple addresses (#884 #886 #888) * Fix: cannot load geoip & geosite (#889) * Chore: use Go v1.16 to build Debian package (#890) -- V2Fly Mon, 12 Apr 2021 23:05:51 +0800 v2ray-core (4.37.2-1) unstable; urgency=medium * Websocket: support browser forwarder (#818) * Websocket: support Websocket 0-RTT early data (#818) * Shadowsocks: add replay protection for Shadowsocks proxy (#777) * DNS: add queryStrategy option for DNS (#794) * DNS: add disableFallback & skipFallback option for DNS client (#864) * GeoIP: add inversed GeoIP matching (#860) -- V2Fly Sun, 11 Apr 2021 22:00:51 +0800 v2ray-core (4.34.0-1) unstable; urgency=medium * TLS Session Resumption is now disabled by default (#569). See #557 for more information. * Support for the legacy Shadowsocks protocol with stream ciphers has been removed (#566). If you are still using the unsecure stream ciphers, migrate to Shadowsocks AEAD (ChaCha20Poly1305 and AES-GCM) immediately. * We have added preliminary support for DNS over QUIC (#534). Currently only non-proxied lookup is supported. * No longer Release s390x, ppc64, ppc64le, and MIPS SoftFloat -- ymshenyu Mon, 04 Jan 2021 22:00:51 +0800 v2ray-core (4.33.0-1) unstable; urgency=medium * Remove XTLS * Add support for Debian package * API: Reflection Service Support * Update to IETF QUIC draft-32 (draft-29 is still supported) * Use Go 1.15.5 -- kslr Mon, 04 Jan 2021 22:00:07 +0800 v2ray-core (4.32.1-1) unstable; urgency=medium * Initial release -- ymshenyu Sun, 08 Nov 2020 08:59:07 +0800 ================================================ FILE: release/debian/control ================================================ Source: v2ray-core Section: net Priority: optional Maintainer: ymshenyu Build-Depends: debhelper-compat (= 12), dh-golang, Standards-Version: 4.5.0 Homepage: https://github.com/v2fly/v2ray-core #Vcs-Browser: https://salsa.debian.org/debian/v2ray-core #Vcs-Git: https://salsa.debian.org/debian/v2ray-core.git Rules-Requires-Root: no XS-Go-Import-Path: github.com/v2fly/v2ray-core/v5 Package: v2ray Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Recommends: v2ray-domain-list-community, v2ray-geoip, v2ray-geoip-only-cn-private Description: Library platform for building proxies in golang Project V2Ray is a set of network tools that help you to build your own computer network. It secures your network connections and thus protects your privacy. Package: v2ray-domain-list-community Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Library platform for building proxies in golang (routing file) Project V2Ray is a set of network tools that help you to build your own computer network. It secures your network connections and thus protects your privacy. Package: v2ray-geoip Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Library platform for building proxies in golang (routing file) Project V2Ray is a set of network tools that help you to build your own computer network. It secures your network connections and thus protects your privacy. Package: v2ray-geoip-only-cn-private Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Library platform for building proxies in golang (routing file) Project V2Ray is a set of network tools that help you to build your own computer network. It secures your network connections and thus protects your privacy. ================================================ FILE: release/debian/copyright ================================================ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: v2ray-core Upstream-Contact: https://github.com/v2fly/v2ray-core Source: https://github.com/v2fly/v2ray-core Files: * Copyright: 2015-2020 V2Fly Community License: Expat Files: release/config/geoip.dat Copyright: 2015-2020 V2Fly Community License: CC-BY-SA-4.0 Files: release/config/geoip-only-cn-private.dat Copyright: 2015-2020 V2Fly Community License: CC-BY-SA-4.0 Files: release/debian/* Copyright: 2020 ymshenyu License: Expat License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: CC-BY-SA-4.0 Attribution-ShareAlike 4.0 International . ======================================================================= . Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. . Using Creative Commons Public Licenses . Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. . Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors . Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees . ======================================================================= . Creative Commons Attribution-ShareAlike 4.0 International Public License . By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. . . Section 1 -- Definitions. . a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. . b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. . c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. . d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. . e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. . f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. . g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. . h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. . i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. . j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. . k. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. . l. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. . m. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. . . Section 2 -- Scope. . a. License grant. . 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: . a. reproduce and Share the Licensed Material, in whole or in part; and . b. produce, reproduce, and Share Adapted Material. . 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. . 3. Term. The term of this Public License is specified in Section 6(a). . 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. . 5. Downstream recipients. . a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. . b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. . c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. . 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). . b. Other rights. . 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. . 2. Patent and trademark rights are not licensed under this Public License. . 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. . . Section 3 -- License Conditions. . Your exercise of the Licensed Rights is expressly made subject to the following conditions. . a. Attribution. . 1. If You Share the Licensed Material (including in modified form), You must: . a. retain the following if it is supplied by the Licensor with the Licensed Material: . i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); . ii. a copyright notice; . iii. a notice that refers to this Public License; . iv. a notice that refers to the disclaimer of warranties; . v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; . b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and . c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. . 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. . 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. . b. ShareAlike. . In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. . 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. . 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. . 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. . . Section 4 -- Sui Generis Database Rights. . Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: . a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; . b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, . including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. . For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. . . Section 5 -- Disclaimer of Warranties and Limitation of Liability. . a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. . b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. . c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. . . Section 6 -- Term and Termination. . a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. . b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: . 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or . 2. upon express reinstatement by the Licensor. . For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. . c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. . d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. . . Section 7 -- Other Terms and Conditions. . a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. . b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. . . Section 8 -- Interpretation. . a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. . b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. . c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. . d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. . . ======================================================================= . Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the "Licensor." Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. . Creative Commons may be contacted at creativecommons.org. # Please also look if there are files or directories which have a # different copyright/license attached and list them here. # Please avoid picking licenses with terms that are more restrictive than the # packaged work, as it may make Debian's contributions unacceptable upstream. # # If you need, there are some extra license texts available in two places: # /usr/share/debhelper/dh_make/licenses/ # /usr/share/common-licenses/ ================================================ FILE: release/debian/rules ================================================ #!/usr/bin/make -f include /usr/share/dpkg/default.mk BUILDDIR=_build %: dh $@ --builddirectory=$(BUILDDIR) --buildsystem=golang execute_after_dh_auto_configure: go mod vendor cp -r vendor/* _build/src override_dh_auto_build: DH_GOPKG="github.com/v2fly/v2ray-core/v5/main" dh_auto_build -- -ldflags "-s -w" cd $(BUILDDIR); mv bin/main bin/v2ray override_dh_auto_install: dh_auto_install -- --no-source override_dh_auto_test: ================================================ FILE: release/debian/source/format ================================================ 3.0 (quilt) ================================================ FILE: release/debian/v2ray-docs.docs ================================================ README.md ================================================ FILE: release/debian/v2ray-domain-list-community.install ================================================ release/config/geosite.dat usr/share/v2ray ================================================ FILE: release/debian/v2ray-geoip-only-cn-private.install ================================================ release/config/geoip-only-cn-private.dat usr/share/v2ray ================================================ FILE: release/debian/v2ray-geoip.install ================================================ release/config/geoip.dat usr/share/v2ray ================================================ FILE: release/debian/v2ray.install ================================================ usr/bin release/config/config.json etc/v2ray ================================================ FILE: release/debian/v2ray.service ================================================ [Unit] Description=V2Ray Service Documentation=https://www.v2fly.org/ After=network.target nss-lookup.target [Service] User=nobody CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE NoNewPrivileges=true ExecStart=/usr/bin/v2ray -config /etc/v2ray/config.json Restart=on-failure RestartPreventExitStatus=23 [Install] WantedBy=multi-user.target ================================================ FILE: release/debian/v2ray@.service ================================================ [Unit] Description=V2Ray Service Documentation=https://www.v2fly.org/ After=network.target nss-lookup.target [Service] User=nobody CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE NoNewPrivileges=true ExecStart=/usr/bin/v2ray -config /etc/v2ray/%i.json Restart=on-failure RestartPreventExitStatus=23 [Install] WantedBy=multi-user.target ================================================ FILE: release/extra/browserforwarder/index.html ================================================ Bridge See debug console for detail ================================================ FILE: release/extra/browserforwarder/index.js ================================================ "use strict"; (function() { var $global,$module;if(Error.stackTraceLimit=1/0,"undefined"!=typeof window?$global=window:"undefined"!=typeof self?$global=self:"undefined"!=typeof global?($global=global).require=require:$global=this,void 0===$global||void 0===$global.Array)throw new Error("no global object found");"undefined"!=typeof module&&($module=module);var $throwRuntimeError,$packages={},$idCounter=0,$keys=function(e){return e?Object.keys(e):[]},$flushConsole=function(){},$throwNilPointerError=function(){$throwRuntimeError("invalid memory address or nil pointer dereference")},$call=function(e,n,r){return e.apply(n,r)},$makeFunc=function(e){return function(){return $externalize(e(this,new($sliceType($jsObjectPtr))($global.Array.prototype.slice.call(arguments,[]))),$emptyInterface)}},$unused=function(e){},$mapArray=function(e,n){for(var r=new e.constructor(e.length),t=0;te.$capacity||t>e.$capacity)&&$throwRuntimeError("slice bounds out of range"),e===e.constructor.nil)return e;var i=new e.constructor(e.$array);return i.$offset=e.$offset+n,i.$length=r-n,i.$capacity=t-n,i},$substring=function(e,n,r){return(n<0||re.length)&&$throwRuntimeError("slice bounds out of range"),e.substring(n,r)},$sliceToArray=function(e){return e.$array.constructor!==Array?e.$array.subarray(e.$offset,e.$offset+e.$length):e.$array.slice(e.$offset,e.$offset+e.$length)},$decodeRune=function(e,n){var r=e.charCodeAt(n);if(r<128)return[r,1];if(r!=r||r<192)return[65533,1];var t=e.charCodeAt(n+1);if(t!=t||t<128||192<=t)return[65533,1];if(r<224)return(a=(31&r)<<6|63&t)<=127?[65533,1]:[a,2];var i=e.charCodeAt(n+2);if(i!=i||i<128||192<=i)return[65533,1];if(r<240)return(a=(15&r)<<12|(63&t)<<6|63&i)<=2047?[65533,1]:55296<=a&&a<=57343?[65533,1]:[a,3];var a,o=e.charCodeAt(n+3);return o!=o||o<128||192<=o?[65533,1]:r<248?(a=(7&r)<<18|(63&t)<<12|(63&i)<<6|63&o)<=65535||11141111114111||55296<=e&&e<=57343)&&(e=65533),e<=127?String.fromCharCode(e):e<=2047?String.fromCharCode(192|e>>6,128|63&e):e<=65535?String.fromCharCode(224|e>>12,128|e>>6&63,128|63&e):String.fromCharCode(240|e>>18,128|e>>12&63,128|e>>6&63,128|63&e)},$stringToBytes=function(e){for(var n=new Uint8Array(e.length),r=0;rt){for(var o=i-1;o>=0;o--)a.copy(e[r+o],n[t+o]);return}for(o=0;ot)for(o=i-1;o>=0;o--)e[r+o]=n[t+o];else for(o=0;o$)if(a=0,$=Math.max(o,e.$capacity<1024?2*e.$capacity:Math.floor(5*e.$capacity/4)),e.$array.constructor===Array){(i=e.$array.slice(e.$offset,e.$offset+e.$length)).length=$;for(var c=e.constructor.elem.zero,u=e.$length;u<$;u++)i[u]=c()}else(i=new e.$array.constructor($)).set(e.$array.subarray(e.$offset,e.$offset+e.$length));$copyArray(i,n,a+e.$length,r,t,e.constructor.elem);var l=new e.constructor(i);return l.$offset=a,l.$length=o,l.$capacity=$,l},$equal=function(e,n,r){if(r===$jsObjectPtr)return e===n;switch(r.kind){case $kindComplex64:case $kindComplex128:return e.$real===n.$real&&e.$imag===n.$imag;case $kindInt64:case $kindUint64:return e.$high===n.$high&&e.$low===n.$low;case $kindArray:if(e.length!==n.length)return!1;for(var t=0;t>>16&65535)*t+r*(n>>>16&65535)<<16>>>0)>>0},$floatKey=function(e){return e!=e?"NaN$"+ ++$idCounter:String(e)},$flatten64=function(e){return 4294967296*e.$high+e.$low},$shiftLeft64=function(e,n){return 0===n?e:n<32?new e.constructor(e.$high<>>32-n,e.$low<>>0):n<64?new e.constructor(e.$low<>n,(e.$low>>>n|e.$high<<32-n)>>>0):n<64?new e.constructor(e.$high>>31,e.$high>>n-32>>>0):e.$high<0?new e.constructor(-1,4294967295):new e.constructor(0,0)},$shiftRightUint64=function(e,n){return 0===n?e:n<32?new e.constructor(e.$high>>>n,(e.$low>>>n|e.$high<<32-n)>>>0):n<64?new e.constructor(0,e.$high>>>n-32):new e.constructor(0,0)},$mul64=function(e,n){var r=0,t=0;0!=(1&n.$low)&&(r=e.$high,t=e.$low);for(var i=1;i<32;i++)0!=(n.$low&1<>>32-i,t+=e.$low<>>0);for(i=0;i<32;i++)0!=(n.$high&1<$||a===$&&o>c);)$=($<<1|c>>>31)>>>0,c=c<<1>>>0,s++;for(var f=0;f<=s;f++)u=u<<1|l>>>31,l=l<<1>>>0,(a>$||a===$&&o>=c)&&(a-=$,(o-=c)<0&&(a--,o+=4294967296),4294967296===++l&&(u++,l=0)),c=(c>>>1|$<<31)>>>0,$>>>=1;return r?new e.constructor(a*i,o*i):new e.constructor(u*t,l*t)},$divComplex=function(e,n){var r=e.$real===1/0||e.$real===-1/0||e.$imag===1/0||e.$imag===-1/0,t=n.$real===1/0||n.$real===-1/0||n.$imag===1/0||n.$imag===-1/0,i=!r&&(e.$real!=e.$real||e.$imag!=e.$imag),a=!t&&(n.$real!=n.$real||n.$imag!=n.$imag);if(i||a)return new e.constructor(NaN,NaN);if(r&&!t)return new e.constructor(1/0,1/0);if(!r&&t)return new e.constructor(0,0);if(0===n.$real&&0===n.$imag)return 0===e.$real&&0===e.$imag?new e.constructor(NaN,NaN):new e.constructor(1/0,1/0);if(Math.abs(n.$real)<=Math.abs(n.$imag)){var o=n.$real/n.$imag,$=n.$real*o+n.$imag;return new e.constructor((e.$real*o+e.$imag)/$,(e.$imag*o-e.$real)/$)}o=n.$imag/n.$real,$=n.$imag*o+n.$real;return new e.constructor((e.$imag*o+e.$real)/$,(e.$imag-e.$real*o)/$)},$kindBool=1,$kindInt=2,$kindInt8=3,$kindInt16=4,$kindInt32=5,$kindInt64=6,$kindUint=7,$kindUint8=8,$kindUint16=9,$kindUint32=10,$kindUint64=11,$kindUintptr=12,$kindFloat32=13,$kindFloat64=14,$kindComplex64=15,$kindComplex128=16,$kindArray=17,$kindChan=18,$kindFunc=19,$kindInterface=20,$kindMap=21,$kindPtr=22,$kindSlice=23,$kindString=24,$kindStruct=25,$kindUnsafePointer=26,$methodSynthesizers=[],$addMethodSynthesizer=function(e){null!==$methodSynthesizers?$methodSynthesizers.push(e):e()},$synthesizeMethods=function(){$methodSynthesizers.forEach(function(e){e()}),$methodSynthesizers=null},$ifaceKeyFor=function(e){if(e===$ifaceNil)return"nil";var n=e.constructor;return n.string+"$"+n.keyFor(e.$val)},$identity=function(e){return e},$typeIDCounter=0,$idKey=function(e){return void 0===e.$id&&($idCounter++,e.$id=$idCounter),String(e.$id)},$newType=function(e,n,r,t,i,a,o){var $;switch(n){case $kindBool:case $kindInt:case $kindInt8:case $kindInt16:case $kindInt32:case $kindUint:case $kindUint8:case $kindUint16:case $kindUint32:case $kindUintptr:case $kindUnsafePointer:($=function(e){this.$val=e}).wrapped=!0,$.keyFor=$identity;break;case $kindString:($=function(e){this.$val=e}).wrapped=!0,$.keyFor=function(e){return"$"+e};break;case $kindFloat32:case $kindFloat64:($=function(e){this.$val=e}).wrapped=!0,$.keyFor=function(e){return $floatKey(e)};break;case $kindInt64:($=function(e,n){this.$high=e+Math.floor(Math.ceil(n)/4294967296)>>0,this.$low=n>>>0,this.$val=this}).keyFor=function(e){return e.$high+"$"+e.$low};break;case $kindUint64:($=function(e,n){this.$high=e+Math.floor(Math.ceil(n)/4294967296)>>>0,this.$low=n>>>0,this.$val=this}).keyFor=function(e){return e.$high+"$"+e.$low};break;case $kindComplex64:($=function(e,n){this.$real=$fround(e),this.$imag=$fround(n),this.$val=this}).keyFor=function(e){return e.$real+"$"+e.$imag};break;case $kindComplex128:($=function(e,n){this.$real=e,this.$imag=n,this.$val=this}).keyFor=function(e){return e.$real+"$"+e.$imag};break;case $kindArray:($=function(e){this.$val=e}).wrapped=!0,$.ptr=$newType(4,$kindPtr,"*"+r,!1,"",!1,function(e){this.$get=function(){return e},this.$set=function(e){$.copy(this,e)},this.$val=e}),$.init=function(e,n){$.elem=e,$.len=n,$.comparable=e.comparable,$.keyFor=function(n){return Array.prototype.join.call($mapArray(n,function(n){return String(e.keyFor(n)).replace(/\\/g,"\\\\").replace(/\$/g,"\\$")}),"$")},$.copy=function(n,r){$copyArray(n,r,0,0,r.length,e)},$.ptr.init($),Object.defineProperty($.ptr.nil,"nilCheck",{get:$throwNilPointerError})};break;case $kindChan:($=function(e){this.$val=e}).wrapped=!0,$.keyFor=$idKey,$.init=function(e,n,r){$.elem=e,$.sendOnly=n,$.recvOnly=r};break;case $kindFunc:($=function(e){this.$val=e}).wrapped=!0,$.init=function(e,n,r){$.params=e,$.results=n,$.variadic=r,$.comparable=!1};break;case $kindInterface:($={implementedBy:{},missingMethodFor:{}}).keyFor=$ifaceKeyFor,$.init=function(e){$.methods=e,e.forEach(function(e){$ifaceNil[e.prop]=$throwNilPointerError})};break;case $kindMap:($=function(e){this.$val=e}).wrapped=!0,$.init=function(e,n){$.key=e,$.elem=n,$.comparable=!1};break;case $kindPtr:($=o||function(e,n,r){this.$get=e,this.$set=n,this.$target=r,this.$val=this}).keyFor=$idKey,$.init=function(e){$.elem=e,$.wrapped=e.kind===$kindArray,$.nil=new $($throwNilPointerError,$throwNilPointerError)};break;case $kindSlice:($=function(e){e.constructor!==$.nativeArray&&(e=new $.nativeArray(e)),this.$array=e,this.$offset=0,this.$length=e.length,this.$capacity=e.length,this.$val=this}).init=function(e){$.elem=e,$.comparable=!1,$.nativeArray=$nativeArray(e.kind),$.nil=new $([])};break;case $kindStruct:($=function(e){this.$val=e}).wrapped=!0,$.ptr=$newType(4,$kindPtr,"*"+r,!1,i,a,o),$.ptr.elem=$,$.ptr.prototype.$get=function(){return this},$.ptr.prototype.$set=function(e){$.copy(this,e)},$.init=function(e,n){$.pkgPath=e,$.fields=n,n.forEach(function(e){e.typ.comparable||($.comparable=!1)}),$.keyFor=function(e){var r=e.$val;return $mapArray(n,function(e){return String(e.typ.keyFor(r[e.prop])).replace(/\\/g,"\\\\").replace(/\$/g,"\\$")}).join("$")},$.copy=function(e,r){for(var t=0;t0;){var a=[],o=[];t.forEach(function(e){if(!i[e.typ.string])switch(i[e.typ.string]=!0,e.typ.named&&(o=o.concat(e.typ.methods),e.indirect&&(o=o.concat($ptrType(e.typ).methods))),e.typ.kind){case $kindStruct:e.typ.fields.forEach(function(n){if(n.embedded){var r=n.typ,t=r.kind===$kindPtr;a.push({typ:t?r.elem:r,indirect:e.indirect||t})}});break;case $kindInterface:o=o.concat(e.typ.methods)}}),o.forEach(function(e){void 0===n[e.name]&&(n[e.name]=e)}),t=a}return e.methodSetCache=[],Object.keys(n).sort().forEach(function(r){e.methodSetCache.push(n[r])}),e.methodSetCache},$Bool=$newType(1,$kindBool,"bool",!0,"",!1,null),$Int=$newType(4,$kindInt,"int",!0,"",!1,null),$Int8=$newType(1,$kindInt8,"int8",!0,"",!1,null),$Int16=$newType(2,$kindInt16,"int16",!0,"",!1,null),$Int32=$newType(4,$kindInt32,"int32",!0,"",!1,null),$Int64=$newType(8,$kindInt64,"int64",!0,"",!1,null),$Uint=$newType(4,$kindUint,"uint",!0,"",!1,null),$Uint8=$newType(1,$kindUint8,"uint8",!0,"",!1,null),$Uint16=$newType(2,$kindUint16,"uint16",!0,"",!1,null),$Uint32=$newType(4,$kindUint32,"uint32",!0,"",!1,null),$Uint64=$newType(8,$kindUint64,"uint64",!0,"",!1,null),$Uintptr=$newType(4,$kindUintptr,"uintptr",!0,"",!1,null),$Float32=$newType(4,$kindFloat32,"float32",!0,"",!1,null),$Float64=$newType(8,$kindFloat64,"float64",!0,"",!1,null),$Complex64=$newType(8,$kindComplex64,"complex64",!0,"",!1,null),$Complex128=$newType(16,$kindComplex128,"complex128",!0,"",!1,null),$String=$newType(8,$kindString,"string",!0,"",!1,null),$UnsafePointer=$newType(4,$kindUnsafePointer,"unsafe.Pointer",!0,"",!1,null),$nativeArray=function(e){switch(e){case $kindInt:return Int32Array;case $kindInt8:return Int8Array;case $kindInt16:return Int16Array;case $kindInt32:return Int32Array;case $kindUint:return Uint32Array;case $kindUint8:return Uint8Array;case $kindUint16:return Uint16Array;case $kindUint32:case $kindUintptr:return Uint32Array;case $kindFloat32:return Float32Array;case $kindFloat64:return Float64Array;default:return Array}},$toNativeArray=function(e,n){var r=$nativeArray(e);return r===Array?n:new r(n)},$arrayTypes={},$arrayType=function(e,n){var r=e.id+"$"+n,t=$arrayTypes[r];return void 0===t&&(t=$newType(12,$kindArray,"["+n+"]"+e.string,!1,"",!1,null),$arrayTypes[r]=t,t.init(e,n)),t},$chanType=function(e,n,r){var t=(r?"<-":"")+"chan"+(n?"<- ":" ")+e.string,i=n?"SendChan":r?"RecvChan":"Chan",a=e[i];return void 0===a&&(a=$newType(4,$kindChan,t,!1,"",!1,null),e[i]=a,a.init(e,n,r)),a},$Chan=function(e,n){(n<0||n>2147483647)&&$throwRuntimeError("makechan: size out of range"),this.$elem=e,this.$capacity=n,this.$buffer=[],this.$sendQueue=[],this.$recvQueue=[],this.$closed=!1},$chanNil=new $Chan(null,0);$chanNil.$sendQueue=$chanNil.$recvQueue={length:0,push:function(){},shift:function(){},indexOf:function(){return-1}};var $funcTypes={},$funcType=function(e,n,r){var t=$mapArray(e,function(e){return e.id}).join(",")+"$"+$mapArray(n,function(e){return e.id}).join(",")+"$"+r,i=$funcTypes[t];if(void 0===i){var a=$mapArray(e,function(e){return e.string});r&&(a[a.length-1]="..."+a[a.length-1].substr(2));var o="func("+a.join(", ")+")";1===n.length?o+=" "+n[0].string:n.length>1&&(o+=" ("+$mapArray(n,function(e){return e.string}).join(", ")+")"),i=$newType(4,$kindFunc,o,!1,"",!1,null),$funcTypes[t]=i,i.init(e,n,r)}return i},$interfaceTypes={},$interfaceType=function(e){var n=$mapArray(e,function(e){return e.pkg+","+e.name+","+e.typ.id}).join("$"),r=$interfaceTypes[n];if(void 0===r){var t="interface {}";0!==e.length&&(t="interface { "+$mapArray(e,function(e){return(""!==e.pkg?e.pkg+".":"")+e.name+e.typ.string.substr(4)}).join("; ")+" }"),r=$newType(8,$kindInterface,t,!1,"",!1,null),$interfaceTypes[n]=r,r.init(e)}return r},$emptyInterface=$interfaceType([]),$ifaceNil={},$error=$newType(8,$kindInterface,"error",!0,"",!1,null);$error.init([{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],!1)}]);var $panicValue,$jsObjectPtr,$jsErrorPtr,$mapTypes={},$mapType=function(e,n){var r=e.id+"$"+n.id,t=$mapTypes[r];return void 0===t&&(t=$newType(4,$kindMap,"map["+e.string+"]"+n.string,!1,"",!1,null),$mapTypes[r]=t,t.init(e,n)),t},$makeMap=function(e,n){for(var r={},t=0;t2147483647)&&$throwRuntimeError("makeslice: len out of range"),(r<0||r2147483647)&&$throwRuntimeError("makeslice: cap out of range");var t=new e.nativeArray(r);if(e.nativeArray===Array)for(var i=0;i=$curGoroutine.deferStack.length)throw n;if(null!==n){var t=null;try{$curGoroutine.deferStack.push(e),$panic(new $jsErrorPtr(n))}catch(e){t=e}return $curGoroutine.deferStack.pop(),void $callDeferred(e,t)}if(!$curGoroutine.asleep){$stackDepthOffset--;var i=$panicStackDepth,a=$panicValue,o=$curGoroutine.panicStack.pop();void 0!==o&&($panicStackDepth=$getStackDepth(),$panicValue=o);try{for(;;){if(null===e&&void 0===(e=$curGoroutine.deferStack[$curGoroutine.deferStack.length-1])){if($panicStackDepth=null,o.Object instanceof Error)throw o.Object;var $;throw $=o.constructor===$String?o.$val:void 0!==o.Error?o.Error():void 0!==o.String?o.String():o,new Error($)}var c=e.pop();if(void 0===c){if($curGoroutine.deferStack.pop(),void 0!==o){e=null;continue}return}var u=c[0].apply(c[2],c[1]);if(u&&void 0!==u.$blk){if(e.push([u.$blk,[],u]),r)throw null;return}if(void 0!==o&&null===$panicStackDepth)throw null}}finally{void 0!==o&&(null!==$panicStackDepth&&$curGoroutine.panicStack.push(o),$panicStackDepth=i,$panicValue=a),$stackDepthOffset++}}},$panic=function(e){$curGoroutine.panicStack.push(e),$callDeferred(null,null,!0)},$recover=function(){return null===$panicStackDepth||void 0!==$panicStackDepth&&$panicStackDepth!==$getStackDepth()-2?$ifaceNil:($panicStackDepth=null,$panicValue)},$throw=function(e){throw e},$noGoroutine={asleep:!1,exit:!1,deferStack:[],panicStack:[]},$curGoroutine=$noGoroutine,$totalGoroutines=0,$awakeGoroutines=0,$checkForDeadlock=!0,$mainFinished=!1,$go=function(e,n){$totalGoroutines++,$awakeGoroutines++;var r=function(){try{$curGoroutine=r;var t=e.apply(void 0,n);if(t&&void 0!==t.$blk)return e=function(){return t.$blk()},void(n=[]);r.exit=!0}catch(e){if(!r.exit)throw e}finally{$curGoroutine=$noGoroutine,r.exit&&($totalGoroutines--,r.asleep=!0),r.asleep&&($awakeGoroutines--,!$mainFinished&&0===$awakeGoroutines&&$checkForDeadlock&&(console.error("fatal error: all goroutines are asleep - deadlock!"),void 0!==$global.process&&$global.process.exit(2)))}};r.asleep=!1,r.exit=!1,r.deferStack=[],r.panicStack=[],$schedule(r)},$scheduled=[],$runScheduled=function(){try{for(var e;void 0!==(e=$scheduled.shift());)e()}finally{$scheduled.length>0&&setTimeout($runScheduled,0)}},$schedule=function(e){e.asleep&&(e.asleep=!1,$awakeGoroutines++),$scheduled.push(e),$curGoroutine===$noGoroutine&&$runScheduled()},$setTimeout=function(e,n){return $awakeGoroutines++,setTimeout(function(){$awakeGoroutines--,e()},n)},$block=function(){$curGoroutine===$noGoroutine&&$throwRuntimeError("cannot block in JavaScript callback, fix by wrapping code in goroutine"),$curGoroutine.asleep=!0},$send=function(e,n){e.$closed&&$throwRuntimeError("send on closed channel");var r=e.$recvQueue.shift();if(void 0===r){if(!(e.$buffer.length65535){var u=Math.floor((c-65536)/1024)+55296,l=(c-65536)%1024+56320;$+=String.fromCharCode(u,l)}else $+=String.fromCharCode(c)}return $;case $kindStruct:var s=$packages.time;if(void 0!==s&&e.constructor===s.Time.ptr){var f=$div64(e.UnixNano(),new $Int64(0,1e6));return new Date($flatten64(f))}var d={},p=function(e,n){if(n===$jsObjectPtr)return e;switch(n.kind){case $kindPtr:return e===n.nil?d:p(e.$get(),n.elem);case $kindStruct:var r=n.fields[0];return p(e[r.prop],r.typ);case $kindInterface:return p(e.$val,e.constructor);default:return d}},h=p(e,n);if(h!==d)return h;h={};for(i=0;i>24;case $kindInt16:return parseInt(e)<<16>>16;case $kindInt32:return parseInt(e)>>0;case $kindUint:return parseInt(e);case $kindUint8:return parseInt(e)<<24>>>24;case $kindUint16:return parseInt(e)<<16>>>16;case $kindUint32:case $kindUintptr:return parseInt(e)>>>0;case $kindInt64:case $kindUint64:return new n(0,e);case $kindFloat32:case $kindFloat64:return parseFloat(e);case $kindArray:return e.length!==n.len&&$throwRuntimeError("got array with wrong size from JavaScript native"),$mapArray(e,function(e){return $internalize(e,n.elem)});case $kindFunc:return function(){for(var t=[],i=0;i=128)return!1;return!0}; $packages["github.com/gopherjs/gopherjs/js"]=(function(){var $pkg={},$init,A,B,L,N,Q,E,K;A=$pkg.Object=$newType(0,$kindStruct,"js.Object",true,"github.com/gopherjs/gopherjs/js",true,function(object_){this.$val=this;if(arguments.length===0){this.object=null;return;}this.object=object_;});B=$pkg.Error=$newType(0,$kindStruct,"js.Error",true,"github.com/gopherjs/gopherjs/js",true,function(Object_){this.$val=this;if(arguments.length===0){this.Object=null;return;}this.Object=Object_;});L=$sliceType($emptyInterface);N=$ptrType(A);Q=$ptrType(B);A.ptr.prototype.Get=function(a){var a,b;b=this;return b.object[$externalize(a,$String)];};A.prototype.Get=function(a){return this.$val.Get(a);};A.ptr.prototype.Set=function(a,b){var a,b,c;c=this;c.object[$externalize(a,$String)]=$externalize(b,$emptyInterface);};A.prototype.Set=function(a,b){return this.$val.Set(a,b);};A.ptr.prototype.Delete=function(a){var a,b;b=this;delete b.object[$externalize(a,$String)];};A.prototype.Delete=function(a){return this.$val.Delete(a);};A.ptr.prototype.Length=function(){var a;a=this;return $parseInt(a.object.length);};A.prototype.Length=function(){return this.$val.Length();};A.ptr.prototype.Index=function(a){var a,b;b=this;return b.object[a];};A.prototype.Index=function(a){return this.$val.Index(a);};A.ptr.prototype.SetIndex=function(a,b){var a,b,c;c=this;c.object[a]=$externalize(b,$emptyInterface);};A.prototype.SetIndex=function(a,b){return this.$val.SetIndex(a,b);};A.ptr.prototype.Call=function(a,b){var a,b,c,d;c=this;return(d=c.object,d[$externalize(a,$String)].apply(d,$externalize(b,L)));};A.prototype.Call=function(a,b){return this.$val.Call(a,b);};A.ptr.prototype.Invoke=function(a){var a,b;b=this;return b.object.apply(undefined,$externalize(a,L));};A.prototype.Invoke=function(a){return this.$val.Invoke(a);};A.ptr.prototype.New=function(a){var a,b;b=this;return new($global.Function.prototype.bind.apply(b.object,[undefined].concat($externalize(a,L))));};A.prototype.New=function(a){return this.$val.New(a);};A.ptr.prototype.Bool=function(){var a;a=this;return!!(a.object);};A.prototype.Bool=function(){return this.$val.Bool();};A.ptr.prototype.String=function(){var a;a=this;return $internalize(a.object,$String);};A.prototype.String=function(){return this.$val.String();};A.ptr.prototype.Int=function(){var a;a=this;return $parseInt(a.object)>>0;};A.prototype.Int=function(){return this.$val.Int();};A.ptr.prototype.Int64=function(){var a;a=this;return $internalize(a.object,$Int64);};A.prototype.Int64=function(){return this.$val.Int64();};A.ptr.prototype.Uint64=function(){var a;a=this;return $internalize(a.object,$Uint64);};A.prototype.Uint64=function(){return this.$val.Uint64();};A.ptr.prototype.Float=function(){var a;a=this;return $parseFloat(a.object);};A.prototype.Float=function(){return this.$val.Float();};A.ptr.prototype.Interface=function(){var a;a=this;return $internalize(a.object,$emptyInterface);};A.prototype.Interface=function(){return this.$val.Interface();};A.ptr.prototype.Unsafe=function(){var a;a=this;return a.object;};A.prototype.Unsafe=function(){return this.$val.Unsafe();};B.ptr.prototype.Error=function(){var a;a=this;return"JavaScript error: "+$internalize(a.Object.message,$String);};B.prototype.Error=function(){return this.$val.Error();};B.ptr.prototype.Stack=function(){var a;a=this;return $internalize(a.Object.stack,$String);};B.prototype.Stack=function(){return this.$val.Stack();};E=function(a){var a;return $makeFunc(a);};$pkg.MakeFunc=E;K=function(){var a;a=new B.ptr(null);$unused(a);};N.methods=[{prop:"Get",name:"Get",pkg:"",typ:$funcType([$String],[N],false)},{prop:"Set",name:"Set",pkg:"",typ:$funcType([$String,$emptyInterface],[],false)},{prop:"Delete",name:"Delete",pkg:"",typ:$funcType([$String],[],false)},{prop:"Length",name:"Length",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Index",name:"Index",pkg:"",typ:$funcType([$Int],[N],false)},{prop:"SetIndex",name:"SetIndex",pkg:"",typ:$funcType([$Int,$emptyInterface],[],false)},{prop:"Call",name:"Call",pkg:"",typ:$funcType([$String,L],[N],true)},{prop:"Invoke",name:"Invoke",pkg:"",typ:$funcType([L],[N],true)},{prop:"New",name:"New",pkg:"",typ:$funcType([L],[N],true)},{prop:"Bool",name:"Bool",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"Int",name:"Int",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Int64",name:"Int64",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Uint64",name:"Uint64",pkg:"",typ:$funcType([],[$Uint64],false)},{prop:"Float",name:"Float",pkg:"",typ:$funcType([],[$Float64],false)},{prop:"Interface",name:"Interface",pkg:"",typ:$funcType([],[$emptyInterface],false)},{prop:"Unsafe",name:"Unsafe",pkg:"",typ:$funcType([],[$Uintptr],false)}];Q.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Stack",name:"Stack",pkg:"",typ:$funcType([],[$String],false)}];A.init("github.com/gopherjs/gopherjs/js",[{prop:"object",name:"object",embedded:false,exported:false,typ:N,tag:""}]);B.init("",[{prop:"Object",name:"Object",embedded:true,exported:true,typ:N,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:K();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["internal/cpu"]=(function(){var $pkg={},$init;$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["internal/bytealg"]=(function(){var $pkg={},$init,A,B,I,K,M;A=$packages["internal/cpu"];B=function(b,c){var b,c,d,e,f,g;if(!((b.$length===c.$length))){return false;}d=b;e=0;while(true){if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);if(!((g===((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f])))){return false;}e++;}return true;};$pkg.Equal=B;I=function(b,c){var b,c;$panic(new $String("unimplemented"));};$pkg.Index=I;K=function(b){var b;$panic(new $String("unimplemented"));};$pkg.Cutover=K;M=function(b,c){var b,c,d;d=0;while(true){if(!(d>0;}return-1;};$pkg.IndexByteString=M;$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$pkg.MaxLen=0;}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["runtime/internal/sys"]=(function(){var $pkg={},$init;$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["runtime"]=(function(){var $pkg={},$init,B,C,A,E,AQ,AR,AX,BF,F,G,I,O,V,AJ,AK;B=$packages["github.com/gopherjs/gopherjs/js"];C=$packages["internal/bytealg"];A=$packages["runtime/internal/sys"];E=$pkg._type=$newType(0,$kindStruct,"runtime._type",true,"runtime",false,function(str_){this.$val=this;if(arguments.length===0){this.str="";return;}this.str=str_;});AQ=$pkg.TypeAssertionError=$newType(0,$kindStruct,"runtime.TypeAssertionError",true,"runtime",true,function(_interface_,concrete_,asserted_,missingMethod_){this.$val=this;if(arguments.length===0){this._interface=AX.nil;this.concrete=AX.nil;this.asserted=AX.nil;this.missingMethod="";return;}this._interface=_interface_;this.concrete=concrete_;this.asserted=asserted_;this.missingMethod=missingMethod_;});AR=$pkg.errorString=$newType(8,$kindString,"runtime.errorString",true,"runtime",false,null);AX=$ptrType(E);BF=$ptrType(AQ);E.ptr.prototype.string=function(){var a;a=this;return a.str;};E.prototype.string=function(){return this.$val.string();};E.ptr.prototype.pkgpath=function(){var a;a=this;return"";};E.prototype.pkgpath=function(){return this.$val.pkgpath();};F=function(){var a,b;a=$packages[$externalize("github.com/gopherjs/gopherjs/js",$String)];$jsObjectPtr=a.Object.ptr;$jsErrorPtr=a.Error.ptr;$throwRuntimeError=AK;b=$ifaceNil;b=new AQ.ptr(AX.nil,AX.nil,AX.nil,"");$unused(b);};G=function(){var a,b,c;a=$global.process;if(a===undefined){return"/";}b=a.env.GOPHERJS_GOROOT;if(!(b===undefined)){return $internalize(b,$String);}else{c=a.env.GOROOT;if(!(c===undefined)){return $internalize(c,$String);}}return"/usr/local/go";};$pkg.GOROOT=G;I=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;b=0;c="";d=0;e=false;f=new($global.Error)().stack.split($externalize("\n",$String))[(a+2>>0)];if(f===undefined){g=0;h="";i=0;j=false;b=g;c=h;d=i;e=j;return[b,c,d,e];}k=f.substring(($parseInt(f.indexOf($externalize("(",$String)))>>0)+1>>0,$parseInt(f.indexOf($externalize(")",$String)))>>0).split($externalize(":",$String));l=0;m=$internalize(k[0],$String);n=$parseInt(k[1])>>0;o=true;b=l;c=m;d=n;e=o;return[b,c,d,e];};$pkg.Caller=I;O=function(){$curGoroutine.exit=$externalize(true,$Bool);$throw(null);};$pkg.Goexit=O;V=function(a,b){var a,b;};$pkg.SetFinalizer=V;AJ=function(a){var a;};$pkg.KeepAlive=AJ;AK=function(a){var a;$panic(new AR((a)));};AQ.ptr.prototype.RuntimeError=function(){};AQ.prototype.RuntimeError=function(){return this.$val.RuntimeError();};AQ.ptr.prototype.Error=function(){var a,b,c,d,e;a=this;b="interface";if(!(a._interface===AX.nil)){b=a._interface.string();}c=a.asserted.string();if(a.concrete===AX.nil){return"interface conversion: "+b+" is nil, not "+c;}d=a.concrete.string();if(a.missingMethod===""){e="interface conversion: "+b+" is "+d+", not "+c;if(d===c){if(!(a.concrete.pkgpath()===a.asserted.pkgpath())){e=e+(" (types from different packages)");}else{e=e+(" (types from different scopes)");}}return e;}return"interface conversion: "+d+" is not "+c+": missing method "+a.missingMethod;};AQ.prototype.Error=function(){return this.$val.Error();};AR.prototype.RuntimeError=function(){var a;a=this.$val;};$ptrType(AR).prototype.RuntimeError=function(){return new AR(this.$get()).RuntimeError();};AR.prototype.Error=function(){var a;a=this.$val;return"runtime error: "+(a);};$ptrType(AR).prototype.Error=function(){return new AR(this.$get()).Error();};AX.methods=[{prop:"string",name:"string",pkg:"runtime",typ:$funcType([],[$String],false)},{prop:"pkgpath",name:"pkgpath",pkg:"runtime",typ:$funcType([],[$String],false)}];BF.methods=[{prop:"RuntimeError",name:"RuntimeError",pkg:"",typ:$funcType([],[],false)},{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];AR.methods=[{prop:"RuntimeError",name:"RuntimeError",pkg:"",typ:$funcType([],[],false)},{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];E.init("runtime",[{prop:"str",name:"str",embedded:false,exported:false,typ:$String,tag:""}]);AQ.init("runtime",[{prop:"_interface",name:"_interface",embedded:false,exported:false,typ:AX,tag:""},{prop:"concrete",name:"concrete",embedded:false,exported:false,typ:AX,tag:""},{prop:"asserted",name:"asserted",embedded:false,exported:false,typ:AX,tag:""},{prop:"missingMethod",name:"missingMethod",embedded:false,exported:false,typ:$String,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=B.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}F();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["errors"]=(function(){var $pkg={},$init,B,C,A;B=$pkg.errorString=$newType(0,$kindStruct,"errors.errorString",true,"errors",false,function(s_){this.$val=this;if(arguments.length===0){this.s="";return;}this.s=s_;});C=$ptrType(B);A=function(a){var a;return new B.ptr(a);};$pkg.New=A;B.ptr.prototype.Error=function(){var a;a=this;return a.s;};B.prototype.Error=function(){return this.$val.Error();};C.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];B.init("errors",[{prop:"s",name:"s",embedded:false,exported:false,typ:$String,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["math/bits"]=(function(){var $pkg={},$init,I,J,BC,H,K,N,O,AM;H=function(a){var a;return 64-AM(a)>>0;};$pkg.LeadingZeros64=H;K=function(a){var a;if(true){return N(((a>>>0)));}return O((new $Uint64(0,a)));};$pkg.TrailingZeros=K;N=function(a){var a,b;if(a===0){return 32;}return(((b=($imul((((a&(-a>>>0))>>>0)),125613361)>>>0)>>>27>>>0,((b<0||b>=I.length)?($throwRuntimeError("index out of range"),undefined):I[b]))>>0));};$pkg.TrailingZeros32=N;O=function(a){var a,b,c;if((a.$high===0&&a.$low===0)){return 64;}return(((b=$shiftRightUint64($mul64(((c=new $Uint64(-a.$high,-a.$low),new $Uint64(a.$high&c.$high,(a.$low&c.$low)>>>0))),new $Uint64(66559345,3033172745)),58),(($flatten64(b)<0||$flatten64(b)>=J.length)?($throwRuntimeError("index out of range"),undefined):J[$flatten64(b)]))>>0));};$pkg.TrailingZeros64=O;AM=function(a){var a,b;b=0;if((a.$high>1||(a.$high===1&&a.$low>=0))){a=$shiftRightUint64(a,(32));b=32;}if((a.$high>0||(a.$high===0&&a.$low>=65536))){a=$shiftRightUint64(a,(16));b=b+(16)>>0;}if((a.$high>0||(a.$high===0&&a.$low>=256))){a=$shiftRightUint64(a,(8));b=b+(8)>>0;}b=b+(((($flatten64(a)<0||$flatten64(a)>=BC.length)?($throwRuntimeError("index out of range"),undefined):BC[$flatten64(a)])>>0))>>0;return b;};$pkg.Len64=AM;$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:I=$toNativeArray($kindUint8,[0,1,28,2,29,14,24,3,30,22,20,15,25,17,4,8,31,27,13,23,21,19,16,7,26,12,18,6,11,5,10,9]);J=$toNativeArray($kindUint8,[0,1,56,2,57,49,28,3,61,58,42,50,38,29,17,4,62,47,59,36,45,43,51,22,53,39,33,30,24,18,12,5,63,55,48,27,60,41,37,16,46,35,44,21,52,32,23,11,54,26,40,15,34,20,31,10,25,14,19,9,13,8,7,6]);BC=$toNativeArray($kindUint8,[0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8]);}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["math"]=(function(){var $pkg={},$init,A,B,FV,FW,FX,FY,C,D,G,AY,V,AF,AZ,BA,BB,BC,BD;A=$packages["github.com/gopherjs/gopherjs/js"];B=$packages["math/bits"];FV=$arrayType($Uint32,2);FW=$arrayType($Float32,2);FX=$arrayType($Float64,1);FY=$structType("math",[{prop:"uint32array",name:"uint32array",embedded:false,exported:false,typ:FV,tag:""},{prop:"float32array",name:"float32array",embedded:false,exported:false,typ:FW,tag:""},{prop:"float64array",name:"float64array",embedded:false,exported:false,typ:FX,tag:""}]);V=function(av){var av;return $parseFloat(C.exp(av));};$pkg.Exp=V;AF=function(av){var av;if(!((av===av))){return G;}return $parseFloat(C.log(av));};$pkg.Log=AF;AZ=function(){var av;av=new($global.ArrayBuffer)(8);AY.uint32array=new($global.Uint32Array)(av);AY.float32array=new($global.Float32Array)(av);AY.float64array=new($global.Float64Array)(av);};BA=function(av){var av;AY.float32array[0]=av;return AY.uint32array[0];};$pkg.Float32bits=BA;BB=function(av){var av;AY.uint32array[0]=av;return AY.float32array[0];};$pkg.Float32frombits=BB;BC=function(av){var av,aw,ax;AY.float64array[0]=av;return(aw=$shiftLeft64((new $Uint64(0,AY.uint32array[1])),32),ax=(new $Uint64(0,AY.uint32array[0])),new $Uint64(aw.$high+ax.$high,aw.$low+ax.$low));};$pkg.Float64bits=BC;BD=function(av){var av;AY.uint32array[0]=((av.$low>>>0));AY.uint32array[1]=(($shiftRightUint64(av,32).$low>>>0));return AY.float64array[0];};$pkg.Float64frombits=BD;$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}AY=new FY.ptr(FV.zero(),FW.zero(),FX.zero());C=$global.Math;D=0;G=0/D;AZ();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["unicode/utf8"]=(function(){var $pkg={},$init,B,A,C,F,G,H,I,J,K,L,M,N,P,Q;B=$pkg.acceptRange=$newType(0,$kindStruct,"utf8.acceptRange",true,"unicode/utf8",false,function(lo_,hi_){this.$val=this;if(arguments.length===0){this.lo=0;this.hi=0;return;}this.lo=lo_;this.hi=hi_;});F=function(a){var a,aa,ab,ac,ad,ae,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;b=0;c=0;d=a.$length;if(d<1){e=65533;f=0;b=e;c=f;return[b,c];}g=(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]);h=((g<0||g>=A.length)?($throwRuntimeError("index out of range"),undefined):A[g]);if(h>=240){i=(((h>>0))<<31>>0)>>31>>0;j=(((((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0])>>0))&~i)>>0)|(65533&i);k=1;b=j;c=k;return[b,c];}l=(h&7)>>>0;n=$clone((m=h>>>4<<24>>>24,((m<0||m>=C.length)?($throwRuntimeError("index out of range"),undefined):C[m])),B);if(d<((l>>0))){o=65533;p=1;b=o;c=p;return[b,c];}q=(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]);if(q>>0)>>0))<<6>>0)|((((q&63)>>>0)>>0));u=2;b=t;c=u;return[b,c];}v=(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]);if(v<128||191>>0)>>0))<<12>>0)|(((((q&63)>>>0)>>0))<<6>>0))|((((v&63)>>>0)>>0));z=3;b=y;c=z;return[b,c];}aa=(3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]);if(aa<128||191>>0)>>0))<<18>>0)|(((((q&63)>>>0)>>0))<<12>>0))|(((((v&63)>>>0)>>0))<<6>>0))|((((aa&63)>>>0)>>0));ae=4;b=ad;c=ae;return[b,c];};$pkg.DecodeRune=F;G=function(a){var a,aa,ab,ac,ad,ae,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;b=0;c=0;d=a.length;if(d<1){e=65533;f=0;b=e;c=f;return[b,c];}g=a.charCodeAt(0);h=((g<0||g>=A.length)?($throwRuntimeError("index out of range"),undefined):A[g]);if(h>=240){i=(((h>>0))<<31>>0)>>31>>0;j=((((a.charCodeAt(0)>>0))&~i)>>0)|(65533&i);k=1;b=j;c=k;return[b,c];}l=(h&7)>>>0;n=$clone((m=h>>>4<<24>>>24,((m<0||m>=C.length)?($throwRuntimeError("index out of range"),undefined):C[m])),B);if(d<((l>>0))){o=65533;p=1;b=o;c=p;return[b,c];}q=a.charCodeAt(1);if(q>>0)>>0))<<6>>0)|((((q&63)>>>0)>>0));u=2;b=t;c=u;return[b,c];}v=a.charCodeAt(2);if(v<128||191>>0)>>0))<<12>>0)|(((((q&63)>>>0)>>0))<<6>>0))|((((v&63)>>>0)>>0));z=3;b=y;c=z;return[b,c];}aa=a.charCodeAt(3);if(aa<128||191>>0)>>0))<<18>>0)|(((((q&63)>>>0)>>0))<<12>>0))|(((((v&63)>>>0)>>0))<<6>>0))|((((aa&63)>>>0)>>0));ae=4;b=ad;c=ae;return[b,c];};$pkg.DecodeRuneInString=G;H=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;b=0;c=0;d=a.$length;if(d===0){e=65533;f=0;b=e;c=f;return[b,c];}g=d-1>>0;b=((((g<0||g>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+g])>>0));if(b<128){h=b;i=1;b=h;c=i;return[b,c];}j=d-4>>0;if(j<0){j=0;}g=g-(1)>>0;while(true){if(!(g>=j)){break;}if(N(((g<0||g>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+g]))){break;}g=g-(1)>>0;}if(g<0){g=0;}k=F($subslice(a,g,d));b=k[0];c=k[1];if(!(((g+c>>0)===d))){l=65533;m=1;b=l;c=m;return[b,c];}n=b;o=c;b=n;c=o;return[b,c];};$pkg.DecodeLastRune=H;I=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;b=0;c=0;d=a.length;if(d===0){e=65533;f=0;b=e;c=f;return[b,c];}g=d-1>>0;b=((a.charCodeAt(g)>>0));if(b<128){h=b;i=1;b=h;c=i;return[b,c];}j=d-4>>0;if(j<0){j=0;}g=g-(1)>>0;while(true){if(!(g>=j)){break;}if(N(a.charCodeAt(g))){break;}g=g-(1)>>0;}if(g<0){g=0;}k=G($substring(a,g,d));b=k[0];c=k[1];if(!(((g+c>>0)===d))){l=65533;m=1;b=l;c=m;return[b,c];}n=b;o=c;b=n;c=o;return[b,c];};$pkg.DecodeLastRuneInString=I;J=function(a){var a;if(a<0){return-1;}else if(a<=127){return 1;}else if(a<=2047){return 2;}else if(55296<=a&&a<=57343){return-1;}else if(a<=65535){return 3;}else if(a<=1114111){return 4;}return-1;};$pkg.RuneLen=J;K=function(a,b){var a,b,c;c=((b>>>0));if(c<=127){(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=((b<<24>>>24)));return 1;}else if(c<=2047){$unused((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=((192|(((b>>6>>0)<<24>>>24)))>>>0));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=((128|((((b<<24>>>24))&63)>>>0))>>>0));return 2;}else if((c>1114111)||(55296<=c&&c<=57343)){b=65533;$unused((2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=((224|(((b>>12>>0)<<24>>>24)))>>>0));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=((128|(((((b>>6>>0)<<24>>>24))&63)>>>0))>>>0));(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]=((128|((((b<<24>>>24))&63)>>>0))>>>0));return 3;}else if(c<=65535){$unused((2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=((224|(((b>>12>>0)<<24>>>24)))>>>0));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=((128|(((((b>>6>>0)<<24>>>24))&63)>>>0))>>>0));(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]=((128|((((b<<24>>>24))&63)>>>0))>>>0));return 3;}else{$unused((3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=((240|(((b>>18>>0)<<24>>>24)))>>>0));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=((128|(((((b>>12>>0)<<24>>>24))&63)>>>0))>>>0));(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]=((128|(((((b>>6>>0)<<24>>>24))&63)>>>0))>>>0));(3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]=((128|((((b<<24>>>24))&63)>>>0))>>>0));return 4;}};$pkg.EncodeRune=K;L=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;b=a.$length;c=0;d=0;while(true){if(!(d>0;e=((d<0||d>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+d]);if(e<128){d=d+(1)>>0;continue;}f=((e<0||e>=A.length)?($throwRuntimeError("index out of range"),undefined):A[e]);if(f===241){d=d+(1)>>0;continue;}g=((((f&7)>>>0)>>0));if((d+g>>0)>b){d=d+(1)>>0;continue;}i=$clone((h=f>>>4<<24>>>24,((h<0||h>=C.length)?($throwRuntimeError("index out of range"),undefined):C[h])),B);k=(j=d+1>>0,((j<0||j>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+j]));if(k>0,((l<0||l>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+l]));if(m<128||191>0,((n<0||n>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+n]));if(o<128||191>0;}return c;};$pkg.RuneCount=L;M=function(a){var a,b,c,d,e,f,g,h,i,j,k,l;b=0;c=a.length;d=0;while(true){if(!(d>0;b=b+(1)>>0;continue;}f=((e<0||e>=A.length)?($throwRuntimeError("index out of range"),undefined):A[e]);if(f===241){d=d+(1)>>0;b=b+(1)>>0;continue;}g=((((f&7)>>>0)>>0));if((d+g>>0)>c){d=d+(1)>>0;b=b+(1)>>0;continue;}i=$clone((h=f>>>4<<24>>>24,((h<0||h>=C.length)?($throwRuntimeError("index out of range"),undefined):C[h])),B);j=a.charCodeAt((d+1>>0));if(j>0));if(k<128||191>0));if(l<128||191>0;b=b+(1)>>0;}b=b;return b;};$pkg.RuneCountInString=M;N=function(a){var a;return!((((a&192)>>>0)===128));};$pkg.RuneStart=N;P=function(a){var a,b,c,d,e,f,g,h,i,j,k;b=a.length;c=0;while(true){if(!(c>0;continue;}e=((d<0||d>=A.length)?($throwRuntimeError("index out of range"),undefined):A[d]);if(e===241){return false;}f=((((e&7)>>>0)>>0));if((c+f>>0)>b){return false;}h=$clone((g=e>>>4<<24>>>24,((g<0||g>=C.length)?($throwRuntimeError("index out of range"),undefined):C[g])),B);i=a.charCodeAt((c+1>>0));if(i>0));if(j<128||191>0));if(k<128||191>0;}return true;};$pkg.ValidString=P;Q=function(a){var a;if(0<=a&&a<55296){return true;}else if(573431&&((a.charCodeAt(1)===120)||(a.charCodeAt(1)===88))){if(a.length<3){return[new $Uint64(0,0),V("ParseUint",d)];}b=16;a=$substring(a,2);}else if((a.charCodeAt(0)===48)){b=8;a=$substring(a,1);}else{b=10;}}else{return[new $Uint64(0,0),X("ParseUint",d,b)];}if(c===0){c=32;}else if(c<0||c>64){return[new $Uint64(0,0),Y("ParseUint",d,c)];}e=new $Uint64(0,0);f=b;if(f===(10)){e=new $Uint64(429496729,2576980378);}else if(f===(16)){e=new $Uint64(268435456,0);}else{e=(g=$div64(new $Uint64(4294967295,4294967295),(new $Uint64(0,b)),false),new $Uint64(g.$high+0,g.$low+1));}i=(h=$shiftLeft64(new $Uint64(0,1),((c>>>0))),new $Uint64(h.$high-0,h.$low-1));j=new $Uint64(0,0);k=(new DE($stringToBytes(a)));l=0;while(true){if(!(l=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+l]);n=0;if(48<=m&&m<=57){n=m-48<<24>>>24;}else if(97<=m&&m<=122){n=(m-97<<24>>>24)+10<<24>>>24;}else if(65<=m&&m<=90){n=(m-65<<24>>>24)+10<<24>>>24;}else{return[new $Uint64(0,0),V("ParseUint",d)];}if(n>=((b<<24>>>24))){return[new $Uint64(0,0),V("ParseUint",d)];}if((j.$high>e.$high||(j.$high===e.$high&&j.$low>=e.$low))){return[i,W("ParseUint",d)];}j=$mul64(j,((new $Uint64(0,b))));p=(o=(new $Uint64(0,n)),new $Uint64(j.$high+o.$high,j.$low+o.$low));if((p.$highi.$high||(p.$high===i.$high&&p.$low>i.$low))){return[i,W("ParseUint",d)];}j=p;l++;}return[j,$ifaceNil];};$pkg.ParseUint=Z;AA=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w;d=new $Int64(0,0);e=$ifaceNil;if(a.length===0){f=new $Int64(0,0);g=V("ParseInt",a);d=f;e=g;return[d,e];}h=a;i=false;if(a.charCodeAt(0)===43){a=$substring(a,1);}else if(a.charCodeAt(0)===45){i=true;a=$substring(a,1);}j=new $Uint64(0,0);k=Z(a,b,c);j=k[0];e=k[1];if(!($interfaceIsEqual(e,$ifaceNil))&&!($interfaceIsEqual($assertType(e,DF).Err,$pkg.ErrRange))){$assertType(e,DF).Func="ParseInt";$assertType(e,DF).Num=h;l=new $Int64(0,0);m=e;d=l;e=m;return[d,e];}if(c===0){c=32;}n=($shiftLeft64(new $Uint64(0,1),(((c-1>>0)>>>0))));if(!i&&(j.$high>n.$high||(j.$high===n.$high&&j.$low>=n.$low))){o=((p=new $Uint64(n.$high-0,n.$low-1),new $Int64(p.$high,p.$low)));q=W("ParseInt",h);d=o;e=q;return[d,e];}if(i&&(j.$high>n.$high||(j.$high===n.$high&&j.$low>n.$low))){r=(s=(new $Int64(n.$high,n.$low)),new $Int64(-s.$high,-s.$low));t=W("ParseInt",h);d=r;e=t;return[d,e];}u=(new $Int64(j.$high,j.$low));if(i){u=new $Int64(-u.$high,-u.$low);}v=u;w=$ifaceNil;d=v;e=w;return[d,e];};$pkg.ParseInt=AA;AB=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m;b=a.length;if(true&&(0=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]);g=g-(48)<<24>>>24;if(g>9){return[0,new U.ptr("Atoi",c,$pkg.ErrSyntax)];}d=($imul(d,10))+((g>>0))>>0;f++;}if(c.charCodeAt(0)===45){d=-d;}return[d,$ifaceNil];}h=AA(a,10,0);i=h[0];j=h[1];k=$assertType(j,DF,true);l=k[0];m=k[1];if(m){l.Func="Atoi";}return[(((i.$low+((i.$high>>31)*4294967296))>>0)),j];};$pkg.Atoi=AB;AC.ptr.prototype.String=function(){var a,b,c,d;a=this;b=10+a.nd>>0;if(a.dp>0){b=b+(a.dp)>>0;}if(a.dp<0){b=b+(-a.dp)>>0;}c=$makeSlice(DE,b);d=0;if((a.nd===0)){return"0";}else if(a.dp<=0){((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]=48);d=d+(1)>>0;((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]=46);d=d+(1)>>0;d=d+(AD($subslice(c,d,(d+-a.dp>>0))))>>0;d=d+($copySlice($subslice(c,d),$subslice(new DE(a.d),0,a.nd)))>>0;}else if(a.dp>0;((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]=46);d=d+(1)>>0;d=d+($copySlice($subslice(c,d),$subslice(new DE(a.d),a.dp,a.nd)))>>0;}else{d=d+($copySlice($subslice(c,d),$subslice(new DE(a.d),0,a.nd)))>>0;d=d+(AD($subslice(c,d,((d+a.dp>>0)-a.nd>>0))))>>0;}return($bytesToString($subslice(c,0,d)));};AC.prototype.String=function(){return this.$val.String();};AD=function(a){var a,b,c,d;b=a;c=0;while(true){if(!(c=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+d]=48);c++;}return a.$length;};AE=function(a){var a,b,c;while(true){if(!(a.nd>0&&((b=a.d,c=a.nd-1>>0,((c<0||c>=b.length)?($throwRuntimeError("index out of range"),undefined):b[c]))===48))){break;}a.nd=a.nd-(1)>>0;}if(a.nd===0){a.dp=0;}};AC.ptr.prototype.Assign=function(a){var a,b,c,d,e,f,g,h;b=this;c=DG.zero();d=0;while(true){if(!((a.$high>0||(a.$high===0&&a.$low>0)))){break;}e=$div64(a,new $Uint64(0,10),false);a=(f=$mul64(new $Uint64(0,10),e),new $Uint64(a.$high-f.$high,a.$low-f.$low));((d<0||d>=c.length)?($throwRuntimeError("index out of range"),undefined):c[d]=((new $Uint64(a.$high+0,a.$low+48).$low<<24>>>24)));d=d+(1)>>0;a=e;}b.nd=0;d=d-(1)>>0;while(true){if(!(d>=0)){break;}(g=b.d,h=b.nd,((h<0||h>=g.length)?($throwRuntimeError("index out of range"),undefined):g[h]=((d<0||d>=c.length)?($throwRuntimeError("index out of range"),undefined):c[d])));b.nd=b.nd+(1)>>0;d=d-(1)>>0;}b.dp=b.nd;AE(b);};AC.prototype.Assign=function(a){return this.$val.Assign(a);};AF=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;c=0;d=0;e=0;while(true){if(!(((f=b,f<32?(e>>>f):0)>>>0)===0)){break;}if(c>=a.nd){if(e===0){a.nd=0;return;}while(true){if(!(((g=b,g<32?(e>>>g):0)>>>0)===0)){break;}e=e*10>>>0;c=c+(1)>>0;}break;}i=(((h=a.d,((c<0||c>=h.length)?($throwRuntimeError("index out of range"),undefined):h[c]))>>>0));e=((e*10>>>0)+i>>>0)-48>>>0;c=c+(1)>>0;}a.dp=a.dp-((c-1>>0))>>0;k=(((j=b,j<32?(1<>>0))-1>>>0;while(true){if(!(c=l.length)?($throwRuntimeError("index out of range"),undefined):l[c]))>>>0));o=(n=b,n<32?(e>>>n):0)>>>0;e=(e&(k))>>>0;(p=a.d,((d<0||d>=p.length)?($throwRuntimeError("index out of range"),undefined):p[d]=(((o+48>>>0)<<24>>>24))));d=d+(1)>>0;e=((e*10>>>0)+m>>>0)-48>>>0;c=c+(1)>>0;}while(true){if(!(e>0)){break;}r=(q=b,q<32?(e>>>q):0)>>>0;e=(e&(k))>>>0;if(d<800){(s=a.d,((d<0||d>=s.length)?($throwRuntimeError("index out of range"),undefined):s[d]=(((r+48>>>0)<<24>>>24))));d=d+(1)>>0;}else if(r>0){a.trunc=true;}e=e*10>>>0;}a.nd=d;AE(a);};AI=function(a,b){var a,b,c;c=0;while(true){if(!(c=a.$length){return true;}if(!((((c<0||c>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+c])===b.charCodeAt(c)))){return((c<0||c>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+c])>0;}return false;};AJ=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;c=((b<0||b>=AH.$length)?($throwRuntimeError("index out of range"),undefined):AH.$array[AH.$offset+b]).delta;if(AI($subslice(new DE(a.d),0,a.nd),((b<0||b>=AH.$length)?($throwRuntimeError("index out of range"),undefined):AH.$array[AH.$offset+b]).cutoff)){c=c-(1)>>0;}d=a.nd;e=a.nd+c>>0;f=0;d=d-(1)>>0;while(true){if(!(d>=0)){break;}f=f+(((g=b,g<32?((((((h=a.d,((d<0||d>=h.length)?($throwRuntimeError("index out of range"),undefined):h[d]))>>>0))-48>>>0))<>>0))>>>0;j=(i=f/10,(i===i&&i!==1/0&&i!==-1/0)?i>>>0:$throwRuntimeError("integer divide by zero"));k=f-(10*j>>>0)>>>0;e=e-(1)>>0;if(e<800){(l=a.d,((e<0||e>=l.length)?($throwRuntimeError("index out of range"),undefined):l[e]=(((k+48>>>0)<<24>>>24))));}else if(!((k===0))){a.trunc=true;}f=j;d=d-(1)>>0;}while(true){if(!(f>0)){break;}n=(m=f/10,(m===m&&m!==1/0&&m!==-1/0)?m>>>0:$throwRuntimeError("integer divide by zero"));o=f-(10*n>>>0)>>>0;e=e-(1)>>0;if(e<800){(p=a.d,((e<0||e>=p.length)?($throwRuntimeError("index out of range"),undefined):p[e]=(((o+48>>>0)<<24>>>24))));}else if(!((o===0))){a.trunc=true;}f=n;}a.nd=a.nd+(c)>>0;if(a.nd>=800){a.nd=800;}a.dp=a.dp+(c)>>0;AE(a);};AC.ptr.prototype.Shift=function(a){var a,b;b=this;if((b.nd===0)){}else if(a>0){while(true){if(!(a>28)){break;}AJ(b,28);a=a-(28)>>0;}AJ(b,((a>>>0)));}else if(a<0){while(true){if(!(a<-28)){break;}AF(b,28);a=a+(28)>>0;}AF(b,((-a>>>0)));}};AC.prototype.Shift=function(a){return this.$val.Shift(a);};AK=function(a,b){var a,b,c,d,e,f,g;if(b<0||b>=a.nd){return false;}if(((c=a.d,((b<0||b>=c.length)?($throwRuntimeError("index out of range"),undefined):c[b]))===53)&&((b+1>>0)===a.nd)){if(a.trunc){return true;}return b>0&&!(((d=(((e=a.d,f=b-1>>0,((f<0||f>=e.length)?($throwRuntimeError("index out of range"),undefined):e[f]))-48<<24>>>24))%2,d===d?d:$throwRuntimeError("integer divide by zero"))===0));}return(g=a.d,((b<0||b>=g.length)?($throwRuntimeError("index out of range"),undefined):g[b]))>=53;};AC.ptr.prototype.Round=function(a){var a,b;b=this;if(a<0||a>=b.nd){return;}if(AK(b,a)){b.RoundUp(a);}else{b.RoundDown(a);}};AC.prototype.Round=function(a){return this.$val.Round(a);};AC.ptr.prototype.RoundDown=function(a){var a,b;b=this;if(a<0||a>=b.nd){return;}b.nd=a;AE(b);};AC.prototype.RoundDown=function(a){return this.$val.RoundDown(a);};AC.ptr.prototype.RoundUp=function(a){var a,b,c,d,e,f,g;b=this;if(a<0||a>=b.nd){return;}c=a-1>>0;while(true){if(!(c>=0)){break;}e=(d=b.d,((c<0||c>=d.length)?($throwRuntimeError("index out of range"),undefined):d[c]));if(e<57){(g=b.d,((c<0||c>=g.length)?($throwRuntimeError("index out of range"),undefined):g[c]=((f=b.d,((c<0||c>=f.length)?($throwRuntimeError("index out of range"),undefined):f[c]))+(1)<<24>>>24)));b.nd=c+1>>0;return;}c=c-(1)>>0;}b.d[0]=49;b.nd=1;b.dp=b.dp+(1)>>0;};AC.prototype.RoundUp=function(a){return this.$val.RoundUp(a);};AC.ptr.prototype.RoundedInteger=function(){var a,b,c,d,e,f,g;a=this;if(a.dp>20){return new $Uint64(4294967295,4294967295);}b=0;c=new $Uint64(0,0);b=0;while(true){if(!(b=f.length)?($throwRuntimeError("index out of range"),undefined):f[b]))-48<<24>>>24))),new $Uint64(d.$high+e.$high,d.$low+e.$low));b=b+(1)>>0;}while(true){if(!(b>0;}if(AK(a,a.dp)){c=(g=new $Uint64(0,1),new $Uint64(c.$high+g.$high,c.$low+g.$low));}return c;};AC.prototype.RoundedInteger=function(){return this.$val.RoundedInteger();};AL.ptr.prototype.AssignComputeBounds=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;e=new AL.ptr(new $Uint64(0,0),0,false);f=new AL.ptr(new $Uint64(0,0),0,false);g=this;g.mant=a;g.exp=b-((d.mantbits>>0))>>0;g.neg=c;if(g.exp<=0&&(h=$shiftLeft64(($shiftRightUint64(a,((-g.exp>>>0)))),((-g.exp>>>0))),(a.$high===h.$high&&a.$low===h.$low))){g.mant=$shiftRightUint64(g.mant,(((-g.exp>>>0))));g.exp=0;i=$clone(g,AL);j=$clone(g,AL);AL.copy(e,i);AL.copy(f,j);return[e,f];}k=b-d.bias>>0;AL.copy(f,new AL.ptr((l=$mul64(new $Uint64(0,2),g.mant),new $Uint64(l.$high+0,l.$low+1)),g.exp-1>>0,g.neg));if(!((m=$shiftLeft64(new $Uint64(0,1),d.mantbits),(a.$high===m.$high&&a.$low===m.$low)))||(k===1)){AL.copy(e,new AL.ptr((n=$mul64(new $Uint64(0,2),g.mant),new $Uint64(n.$high-0,n.$low-1)),g.exp-1>>0,g.neg));}else{AL.copy(e,new AL.ptr((o=$mul64(new $Uint64(0,4),g.mant),new $Uint64(o.$high-0,o.$low-1)),g.exp-2>>0,g.neg));}return[e,f];};AL.prototype.AssignComputeBounds=function(a,b,c,d){return this.$val.AssignComputeBounds(a,b,c,d);};AL.ptr.prototype.Normalize=function(){var a,b,c;a=this;if((b=a.mant,(b.$high===0&&b.$low===0))){return 0;}c=C.LeadingZeros64(a.mant);a.mant=$shiftLeft64(a.mant,(((c>>>0))));a.exp=a.exp-(c)>>0;return((c>>>0));};AL.prototype.Normalize=function(){return this.$val.Normalize();};AL.ptr.prototype.Multiply=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x;b=this;c=$shiftRightUint64(b.mant,32);d=(new $Uint64(0,((b.mant.$low>>>0))));e=c;f=d;g=$shiftRightUint64(a.mant,32);h=(new $Uint64(0,((a.mant.$low>>>0))));i=g;j=h;k=$mul64(e,j);l=$mul64(f,i);b.mant=(m=(n=$mul64(e,i),o=$shiftRightUint64(k,32),new $Uint64(n.$high+o.$high,n.$low+o.$low)),p=$shiftRightUint64(l,32),new $Uint64(m.$high+p.$high,m.$low+p.$low));u=(q=(r=(new $Uint64(0,((k.$low>>>0)))),s=(new $Uint64(0,((l.$low>>>0)))),new $Uint64(r.$high+s.$high,r.$low+s.$low)),t=$shiftRightUint64(($mul64(f,j)),32),new $Uint64(q.$high+t.$high,q.$low+t.$low));u=(v=new $Uint64(0,2147483648),new $Uint64(u.$high+v.$high,u.$low+v.$low));b.mant=(w=b.mant,x=($shiftRightUint64(u,32)),new $Uint64(w.$high+x.$high,w.$low+x.$low));b.exp=(b.exp+a.exp>>0)+64>>0;};AL.prototype.Multiply=function(a){return this.$val.Multiply(a);};AL.ptr.prototype.AssignDecimal=function(a,b,c,d,e){var a,aa,ab,ac,ad,ae,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;f=false;g=this;h=0;if(d){h=h+(4)>>0;}g.mant=a;g.exp=0;g.neg=c;j=(i=((b- -348>>0))/8,(i===i&&i!==1/0&&i!==-1/0)?i>>0:$throwRuntimeError("integer divide by zero"));if(b<-348||j>=87){f=false;return f;}l=(k=((b- -348>>0))%8,k===k?k:$throwRuntimeError("integer divide by zero"));if(l<19&&(m=(n=19-l>>0,((n<0||n>=AO.length)?($throwRuntimeError("index out of range"),undefined):AO[n])),(a.$high=AO.length)?($throwRuntimeError("index out of range"),undefined):AO[l])));g.Normalize();}else{g.Normalize();g.Multiply($clone(((l<0||l>=AM.length)?($throwRuntimeError("index out of range"),undefined):AM[l]),AL));h=h+(4)>>0;}g.Multiply($clone(((j<0||j>=AN.length)?($throwRuntimeError("index out of range"),undefined):AN[j]),AL));if(h>0){h=h+(1)>>0;}h=h+(4)>>0;o=g.Normalize();h=(p=(o),p<32?(h<>0;q=e.bias-63>>0;r=0;if(g.exp<=q){r=((63-e.mantbits>>>0)+1>>>0)+(((q-g.exp>>0)>>>0))>>>0;}else{r=63-e.mantbits>>>0;}s=$shiftLeft64(new $Uint64(0,1),((r-1>>>0)));w=(t=g.mant,u=(v=$shiftLeft64(new $Uint64(0,1),r),new $Uint64(v.$high-0,v.$low-1)),new $Uint64(t.$high&u.$high,(t.$low&u.$low)>>>0));if((x=(y=(new $Int64(s.$high,s.$low)),z=(new $Int64(0,h)),new $Int64(y.$high-z.$high,y.$low-z.$low)),aa=(new $Int64(w.$high,w.$low)),(x.$high>0)),28))/93,(d===d&&d!==1/0&&d!==-1/0)?d>>0:$throwRuntimeError("integer divide by zero"));g=(f=((e- -348>>0))/8,(f===f&&f!==1/0&&f!==-1/0)?f>>0:$throwRuntimeError("integer divide by zero"));Loop:while(true){h=(c.exp+((g<0||g>=AN.length)?($throwRuntimeError("index out of range"),undefined):AN[g]).exp>>0)+64>>0;if(h<-60){g=g+(1)>>0;}else if(h>-32){g=g-(1)>>0;}else{break Loop;}}c.Multiply($clone(((g<0||g>=AN.length)?($throwRuntimeError("index out of range"),undefined):AN[g]),AL));i=-((-348+($imul(g,8))>>0));j=g;a=i;b=j;return[a,b];};AL.prototype.frexp10=function(){return this.$val.frexp10();};AP=function(a,b,c){var a,b,c,d,e,f;d=0;e=c.frexp10();d=e[0];f=e[1];a.Multiply($clone(((f<0||f>=AN.length)?($throwRuntimeError("index out of range"),undefined):AN[f]),AL));b.Multiply($clone(((f<0||f>=AN.length)?($throwRuntimeError("index out of range"),undefined):AN[f]),AL));return d;};AL.ptr.prototype.FixedDecimal=function(a,b){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;c=this;if((d=c.mant,(d.$high===0&&d.$low===0))){a.nd=0;a.dp=0;a.neg=c.neg;return true;}if(b===0){$panic(new $String("strconv: internal error: extFloat.FixedDecimal called with n == 0"));}c.Normalize();e=c.frexp10();f=e[0];g=((-c.exp>>>0));h=(($shiftRightUint64(c.mant,g).$low>>>0));k=(i=c.mant,j=$shiftLeft64((new $Uint64(0,h)),g),new $Uint64(i.$high-j.$high,i.$low-j.$low));l=new $Uint64(0,1);m=b;n=0;o=new $Uint64(0,1);p=0;q=new $Uint64(0,1);r=p;s=q;while(true){if(!(r<20)){break;}if((t=(new $Uint64(0,h)),(s.$high>t.$high||(s.$high===t.$high&&s.$low>t.$low)))){n=r;break;}s=$mul64(s,(new $Uint64(0,10)));r=r+(1)>>0;}u=h;if(n>m){o=(v=n-m>>0,((v<0||v>=AO.length)?($throwRuntimeError("index out of range"),undefined):AO[v]));h=(w=h/(((o.$low>>>0))),(w===w&&w!==1/0&&w!==-1/0)?w>>>0:$throwRuntimeError("integer divide by zero"));u=u-(($imul(h,((o.$low>>>0)))>>>0))>>>0;}else{u=0;}x=DH.zero();y=32;z=h;while(true){if(!(z>0)){break;}ab=(aa=z/10,(aa===aa&&aa!==1/0&&aa!==-1/0)?aa>>>0:$throwRuntimeError("integer divide by zero"));z=z-(($imul(10,ab)>>>0))>>>0;y=y-(1)>>0;((y<0||y>=x.length)?($throwRuntimeError("index out of range"),undefined):x[y]=(((z+48>>>0)<<24>>>24)));z=ab;}ac=y;while(true){if(!(ac<32)){break;}(ad=a.d,ae=ac-y>>0,((ae<0||ae>=ad.$length)?($throwRuntimeError("index out of range"),undefined):ad.$array[ad.$offset+ae]=((ac<0||ac>=x.length)?($throwRuntimeError("index out of range"),undefined):x[ac])));ac=ac+(1)>>0;}af=32-y>>0;a.nd=af;a.dp=n+f>>0;m=m-(af)>>0;if(m>0){if(!((u===0))||!((o.$high===0&&o.$low===1))){$panic(new $String("strconv: internal error, rest != 0 but needed > 0"));}while(true){if(!(m>0)){break;}k=$mul64(k,(new $Uint64(0,10)));l=$mul64(l,(new $Uint64(0,10)));if((ag=$mul64(new $Uint64(0,2),l),ah=$shiftLeft64(new $Uint64(0,1),g),(ag.$high>ah.$high||(ag.$high===ah.$high&&ag.$low>ah.$low)))){return false;}ai=$shiftRightUint64(k,g);(aj=a.d,((af<0||af>=aj.$length)?($throwRuntimeError("index out of range"),undefined):aj.$array[aj.$offset+af]=((new $Uint64(ai.$high+0,ai.$low+48).$low<<24>>>24))));k=(ak=$shiftLeft64(ai,g),new $Uint64(k.$high-ak.$high,k.$low-ak.$low));af=af+(1)>>0;m=m-(1)>>0;}a.nd=af;}am=AQ(a,(al=$shiftLeft64((new $Uint64(0,u)),g),new $Uint64(al.$high|k.$high,(al.$low|k.$low)>>>0)),o,g,l);if(!am){return false;}an=a.nd-1>>0;while(true){if(!(an>=0)){break;}if(!(((ao=a.d,((an<0||an>=ao.$length)?($throwRuntimeError("index out of range"),undefined):ao.$array[ao.$offset+an]))===48))){a.nd=an+1>>0;break;}an=an-(1)>>0;}return true;};AL.prototype.FixedDecimal=function(a,b){return this.$val.FixedDecimal(a,b);};AQ=function(a,b,c,d,e){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;if((f=$shiftLeft64(c,d),(b.$high>f.$high||(b.$high===f.$high&&b.$low>f.$low)))){$panic(new $String("strconv: num > den<h.$high||(g.$high===h.$high&&g.$low>h.$low)))){$panic(new $String("strconv: \xCE\xB5 > (den<l.$high||(k.$high===l.$high&&k.$low>l.$low)))){m=a.nd-1>>0;while(true){if(!(m>=0)){break;}if((n=a.d,((m<0||m>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+m]))===57){a.nd=a.nd-(1)>>0;}else{break;}m=m-(1)>>0;}if(m<0){(o=a.d,(0>=o.$length?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+0]=49));a.nd=1;a.dp=a.dp+(1)>>0;}else{(q=a.d,((m<0||m>=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+m]=((p=a.d,((m<0||m>=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+m]))+(1)<<24>>>24)));}return true;}return false;};AL.ptr.prototype.ShortestDecimal=function(a,b,c){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,ba,bb,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;d=this;if((e=d.mant,(e.$high===0&&e.$low===0))){a.nd=0;a.dp=0;a.neg=d.neg;return true;}if((d.exp===0)&&$equal(b,d,AL)&&$equal(b,c,AL)){f=DG.zero();g=23;h=d.mant;while(true){if(!((h.$high>0||(h.$high===0&&h.$low>0)))){break;}i=$div64(h,new $Uint64(0,10),false);h=(j=$mul64(new $Uint64(0,10),i),new $Uint64(h.$high-j.$high,h.$low-j.$low));((g<0||g>=f.length)?($throwRuntimeError("index out of range"),undefined):f[g]=((new $Uint64(h.$high+0,h.$low+48).$low<<24>>>24)));g=g-(1)>>0;h=i;}k=(24-g>>0)-1>>0;l=0;while(true){if(!(l=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+l]=(m=(g+1>>0)+l>>0,((m<0||m>=f.length)?($throwRuntimeError("index out of range"),undefined):f[m]))));l=l+(1)>>0;}o=k;p=k;a.nd=o;a.dp=p;while(true){if(!(a.nd>0&&((q=a.d,r=a.nd-1>>0,((r<0||r>=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+r]))===48))){break;}a.nd=a.nd-(1)>>0;}if(a.nd===0){a.dp=0;}a.neg=d.neg;return true;}c.Normalize();if(d.exp>c.exp){d.mant=$shiftLeft64(d.mant,((((d.exp-c.exp>>0)>>>0))));d.exp=c.exp;}if(b.exp>c.exp){b.mant=$shiftLeft64(b.mant,((((b.exp-c.exp>>0)>>>0))));b.exp=c.exp;}s=AP(b,d,c);c.mant=(t=c.mant,u=new $Uint64(0,1),new $Uint64(t.$high+u.$high,t.$low+u.$low));b.mant=(v=b.mant,w=new $Uint64(0,1),new $Uint64(v.$high-w.$high,v.$low-w.$low));x=((-c.exp>>>0));y=(($shiftRightUint64(c.mant,x).$low>>>0));ab=(z=c.mant,aa=$shiftLeft64((new $Uint64(0,y)),x),new $Uint64(z.$high-aa.$high,z.$low-aa.$low));ae=(ac=c.mant,ad=b.mant,new $Uint64(ac.$high-ad.$high,ac.$low-ad.$low));ah=(af=c.mant,ag=d.mant,new $Uint64(af.$high-ag.$high,af.$low-ag.$low));ai=0;aj=0;ak=new $Uint64(0,1);al=aj;am=ak;while(true){if(!(al<20)){break;}if((an=(new $Uint64(0,y)),(am.$high>an.$high||(am.$high===an.$high&&am.$low>an.$low)))){ai=al;break;}am=$mul64(am,(new $Uint64(0,10)));al=al+(1)>>0;}ao=0;while(true){if(!(ao>0)-1>>0,((ap<0||ap>=AO.length)?($throwRuntimeError("index out of range"),undefined):AO[ap]));as=(ar=y/((aq.$low>>>0)),(ar===ar&&ar!==1/0&&ar!==-1/0)?ar>>>0:$throwRuntimeError("integer divide by zero"));(at=a.d,((ao<0||ao>=at.$length)?($throwRuntimeError("index out of range"),undefined):at.$array[at.$offset+ao]=(((as+48>>>0)<<24>>>24))));y=y-(($imul(as,((aq.$low>>>0)))>>>0))>>>0;av=(au=$shiftLeft64((new $Uint64(0,y)),x),new $Uint64(au.$high+ab.$high,au.$low+ab.$low));if((av.$high>0;a.dp=ai+s>>0;a.neg=d.neg;return AR(a,av,ah,ae,$shiftLeft64(aq,x),new $Uint64(0,2));}ao=ao+(1)>>0;}a.nd=ai;a.dp=a.nd+s>>0;a.neg=d.neg;aw=0;ax=new $Uint64(0,1);while(true){ab=$mul64(ab,(new $Uint64(0,10)));ax=$mul64(ax,(new $Uint64(0,10)));aw=(($shiftRightUint64(ab,x).$low>>0));(ay=a.d,az=a.nd,((az<0||az>=ay.$length)?($throwRuntimeError("index out of range"),undefined):ay.$array[ay.$offset+az]=(((aw+48>>0)<<24>>>24))));a.nd=a.nd+(1)>>0;ab=(ba=$shiftLeft64((new $Uint64(0,aw)),x),new $Uint64(ab.$high-ba.$high,ab.$low-ba.$low));if((bb=$mul64(ae,ax),(ab.$high>0;(m=a.d,((k<0||k>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+k]=((l=a.d,((k<0||k>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+k]))-(1)<<24>>>24)));b=(n=e,new $Uint64(b.$high+n.$high,b.$low+n.$low));}if((o=new $Uint64(b.$high+e.$high,b.$low+e.$low),p=(q=(r=$div64(e,new $Uint64(0,2),false),new $Uint64(c.$high+r.$high,c.$low+r.$low)),new $Uint64(q.$high+f.$high,q.$low+f.$low)),(o.$highs.$high||(b.$high===s.$high&&b.$low>s.$low)))){return false;}if((a.nd===1)&&((t=a.d,(0>=t.$length?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+0]))===48)){a.nd=0;a.dp=0;}return true;};AW=function(a,b,c,d,e){var a,b,c,d,e;return AX(a,b,c,d,e);};$pkg.AppendFloat=AW;AX=function(a,b,c,d,e){var a,aa,ab,ac,ad,ae,af,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;f=new $Uint64(0,0);g=DI.nil;h=e;if(h===(32)){f=(new $Uint64(0,A.Float32bits(($fround(b)))));g=AT;}else if(h===(64)){f=A.Float64bits(b);g=AU;}else{$panic(new $String("strconv: illegal AppendFloat/FormatFloat bitSize"));}j=!((i=$shiftRightUint64(f,((g.expbits+g.mantbits>>>0))),(i.$high===0&&i.$low===0)));l=(($shiftRightUint64(f,g.mantbits).$low>>0))&((((k=g.expbits,k<32?(1<>0)-1>>0));o=(m=(n=$shiftLeft64(new $Uint64(0,1),g.mantbits),new $Uint64(n.$high-0,n.$low-1)),new $Uint64(f.$high&m.$high,(f.$low&m.$low)>>>0));p=l;if(p===((((q=g.expbits,q<32?(1<>0)-1>>0))){r="";if(!((o.$high===0&&o.$low===0))){r="NaN";}else if(j){r="-Inf";}else{r="+Inf";}return $appendSlice(a,r);}else if(p===(0)){l=l+(1)>>0;}else{o=(s=$shiftLeft64(new $Uint64(0,1),g.mantbits),new $Uint64(o.$high|s.$high,(o.$low|s.$low)>>>0));}l=l+(g.bias)>>0;if(c===98){return BE(a,j,o,l,g);}if(!I){return AY(a,d,c,j,o,l,g);}t=new BB.ptr(DE.nil,0,0,false);u=false;v=d<0;if(v){w=new AL.ptr(new $Uint64(0,0),0,false);x=w.AssignComputeBounds(o,l,j,g);y=$clone(x[0],AL);z=$clone(x[1],AL);aa=DH.zero();t.d=new DE(aa);u=w.ShortestDecimal(t,y,z);if(!u){return AY(a,d,c,j,o,l,g);}ab=c;if((ab===(101))||(ab===(69))){d=BG(t.nd-1>>0,0);}else if(ab===(102)){d=BG(t.nd-t.dp>>0,0);}else if((ab===(103))||(ab===(71))){d=t.nd;}}else if(!((c===102))){ac=d;ad=c;if((ad===(101))||(ad===(69))){ac=ac+(1)>>0;}else if((ad===(103))||(ad===(71))){if(d===0){d=1;}ac=d;}if(ac<=15){ae=DG.zero();t.d=new DE(ae);af=new AL.ptr(o,l-((g.mantbits>>0))>>0,j);u=af.FixedDecimal(t,ac);}}if(!u){return AY(a,d,c,j,o,l,g);}return AZ(a,v,j,$clone(t,BB),d,c);};AY=function(a,b,c,d,e,f,g){var a,b,c,d,e,f,g,h,i,j,k,l;h=new AC.ptr(DD.zero(),0,0,false,false);h.Assign(e);h.Shift(f-((g.mantbits>>0))>>0);i=new BB.ptr(DE.nil,0,0,false);j=b<0;if(j){BA(h,e,f,g);BB.copy(i,new BB.ptr(new DE(h.d),h.nd,h.dp,false));k=c;if((k===(101))||(k===(69))){b=i.nd-1>>0;}else if(k===(102)){b=BG(i.nd-i.dp>>0,0);}else if((k===(103))||(k===(71))){b=i.nd;}}else{l=c;if((l===(101))||(l===(69))){h.Round(b+1>>0);}else if(l===(102)){h.Round(h.dp+b>>0);}else if((l===(103))||(l===(71))){if(b===0){b=1;}h.Round(b);}BB.copy(i,new BB.ptr(new DE(h.d),h.nd,h.dp,false));}return AZ(a,j,d,$clone(i,BB),b,c);};AZ=function(a,b,c,d,e,f){var a,b,c,d,e,f,g,h,i;g=f;if((g===(101))||(g===(69))){return BC(a,c,$clone(d,BB),e,f);}else if(g===(102)){return BD(a,c,$clone(d,BB),e);}else if((g===(103))||(g===(71))){h=e;if(h>d.nd&&d.nd>=d.dp){h=d.nd;}if(b){h=6;}i=d.dp-1>>0;if(i<-4||i>=h){if(e>d.nd){e=d.nd;}return BC(a,c,$clone(d,BB),e-1>>0,(f+101<<24>>>24)-103<<24>>>24);}if(e>d.dp){e=d.nd;}return BD(a,c,$clone(d,BB),BG(e-d.dp>>0,0));}return $append(a,37,f);};BA=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x;if((b.$high===0&&b.$low===0)){a.nd=0;return;}e=d.bias+1>>0;if(c>e&&($imul(332,((a.dp-a.nd>>0))))>=($imul(100,((c-((d.mantbits>>0))>>0))))){return;}f=new AC.ptr(DD.zero(),0,0,false,false);f.Assign((g=$mul64(b,new $Uint64(0,2)),new $Uint64(g.$high+0,g.$low+1)));f.Shift((c-((d.mantbits>>0))>>0)-1>>0);h=new $Uint64(0,0);i=0;if((j=$shiftLeft64(new $Uint64(0,1),d.mantbits),(b.$high>j.$high||(b.$high===j.$high&&b.$low>j.$low)))||(c===e)){h=new $Uint64(b.$high-0,b.$low-1);i=c;}else{h=(k=$mul64(b,new $Uint64(0,2)),new $Uint64(k.$high-0,k.$low-1));i=c-1>>0;}l=new AC.ptr(DD.zero(),0,0,false,false);l.Assign((m=$mul64(h,new $Uint64(0,2)),new $Uint64(m.$high+0,m.$low+1)));l.Shift((i-((d.mantbits>>0))>>0)-1>>0);o=(n=$div64(b,new $Uint64(0,2),true),(n.$high===0&&n.$low===0));p=0;while(true){if(!(p=r.length)?($throwRuntimeError("index out of range"),undefined):r[p]));}t=(s=a.d,((p<0||p>=s.length)?($throwRuntimeError("index out of range"),undefined):s[p]));u=48;if(p=v.length)?($throwRuntimeError("index out of range"),undefined):v[p]));}w=!((q===t))||o&&((p+1>>0)===l.nd);x=!((t===u))&&(o||(t+1<<24>>>24)>0)>0);return;}else if(w){a.RoundDown(p+1>>0);return;}else if(x){a.RoundUp(p+1>>0);return;}p=p+(1)>>0;}};BC=function(a,b,c,d,e){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;if(b){a=$append(a,45);}f=48;if(!((c.nd===0))){f=(g=c.d,(0>=g.$length?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+0]));}a=$append(a,f);if(d>0){a=$append(a,46);h=1;i=BF(c.nd,d+1>>0);if(h>0;}}a=$append(a,e);j=c.dp-1>>0;if(c.nd===0){j=0;}if(j<0){f=45;j=-j;}else{f=43;}a=$append(a,f);if(j<10){a=$append(a,48,((j<<24>>>24))+48<<24>>>24);}else if(j<100){a=$append(a,(((k=j/10,(k===k&&k!==1/0&&k!==-1/0)?k>>0:$throwRuntimeError("integer divide by zero"))<<24>>>24))+48<<24>>>24,(((l=j%10,l===l?l:$throwRuntimeError("integer divide by zero"))<<24>>>24))+48<<24>>>24);}else{a=$append(a,(((m=j/100,(m===m&&m!==1/0&&m!==-1/0)?m>>0:$throwRuntimeError("integer divide by zero"))<<24>>>24))+48<<24>>>24,(n=(((o=j/10,(o===o&&o!==1/0&&o!==-1/0)?o>>0:$throwRuntimeError("integer divide by zero"))<<24>>>24))%10,n===n?n:$throwRuntimeError("integer divide by zero"))+48<<24>>>24,(((p=j%10,p===p?p:$throwRuntimeError("integer divide by zero"))<<24>>>24))+48<<24>>>24);}return a;};BD=function(a,b,c,d){var a,b,c,d,e,f,g,h,i;if(b){a=$append(a,45);}if(c.dp>0){e=BF(c.nd,c.dp);a=$appendSlice(a,$subslice(c.d,0,e));while(true){if(!(e>0;}}else{a=$append(a,48);}if(d>0){a=$append(a,46);f=0;while(true){if(!(f>0;if(0<=h&&h=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+h]));}a=$append(a,g);f=f+(1)>>0;}}return a;};BE=function(a,b,c,d,e){var a,b,c,d,e,f,g;if(b){a=$append(a,45);}f=BS(a,c,10,false,true);a=f[0];a=$append(a,112);d=d-(((e.mantbits>>0)))>>0;if(d>=0){a=$append(a,43);}g=BS(a,(new $Uint64(0,d)),10,d<0,true);a=g[0];return a;};BF=function(a,b){var a,b;if(ab){return a;}return b;};BM=function(a,b){var a,b,c,d;if(true&&(a.$high<0||(a.$high===0&&a.$low<100))&&(b===10)){return BR(((a.$low>>0)));}c=BS(DE.nil,a,b,false,false);d=c[1];return d;};$pkg.FormatUint=BM;BN=function(a,b){var a,b,c,d;if(true&&(0>31)*4294967296))>>0)));}c=BS(DE.nil,(new $Uint64(a.$high,a.$low)),b,(a.$high<0||(a.$high===0&&a.$low<0)),false);d=c[1];return d;};$pkg.FormatInt=BN;BO=function(a){var a;return BN((new $Int64(0,a)),10);};$pkg.Itoa=BO;BR=function(a){var a;if(a<10){return $substring("0123456789abcdefghijklmnopqrstuvwxyz",a,(a+1>>0));}return $substring("00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899",($imul(a,2)),(($imul(a,2))+2>>0));};BS=function(a,b,c,d,e){var a,aa,ab,ac,ad,ae,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;f=DE.nil;g="";if(c<2||c>36){$panic(new $String("strconv: illegal AppendInt/FormatInt base"));}h=DJ.zero();i=65;if(d){b=new $Uint64(-b.$high,-b.$low);}if(c===10){if(true){while(true){if(!((b.$high>0||(b.$high===0&&b.$low>=1000000000)))){break;}j=$div64(b,new $Uint64(0,1000000000),false);l=(((k=$mul64(j,new $Uint64(0,1000000000)),new $Uint64(b.$high-k.$high,b.$low-k.$low)).$low>>>0));m=4;while(true){if(!(m>0)){break;}o=(n=l%100,n===n?n:$throwRuntimeError("integer divide by zero"))*2>>>0;l=(p=l/(100),(p===p&&p!==1/0&&p!==-1/0)?p>>>0:$throwRuntimeError("integer divide by zero"));i=i-(2)>>0;(q=i+1>>0,((q<0||q>=h.length)?($throwRuntimeError("index out of range"),undefined):h[q]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899".charCodeAt((o+1>>>0))));(r=i+0>>0,((r<0||r>=h.length)?($throwRuntimeError("index out of range"),undefined):h[r]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899".charCodeAt((o+0>>>0))));m=m-(1)>>0;}i=i-(1)>>0;((i<0||i>=h.length)?($throwRuntimeError("index out of range"),undefined):h[i]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899".charCodeAt(((l*2>>>0)+1>>>0)));b=j;}}s=((b.$low>>>0));while(true){if(!(s>=100)){break;}u=(t=s%100,t===t?t:$throwRuntimeError("integer divide by zero"))*2>>>0;s=(v=s/(100),(v===v&&v!==1/0&&v!==-1/0)?v>>>0:$throwRuntimeError("integer divide by zero"));i=i-(2)>>0;(w=i+1>>0,((w<0||w>=h.length)?($throwRuntimeError("index out of range"),undefined):h[w]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899".charCodeAt((u+1>>>0))));(x=i+0>>0,((x<0||x>=h.length)?($throwRuntimeError("index out of range"),undefined):h[x]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899".charCodeAt((u+0>>>0))));}y=s*2>>>0;i=i-(1)>>0;((i<0||i>=h.length)?($throwRuntimeError("index out of range"),undefined):h[i]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899".charCodeAt((y+1>>>0)));if(s>=10){i=i-(1)>>0;((i<0||i>=h.length)?($throwRuntimeError("index out of range"),undefined):h[i]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899".charCodeAt(y));}}else if(BT(c)){z=(((C.TrailingZeros(((c>>>0)))>>>0))&7)>>>0;aa=(new $Uint64(0,c));ab=((c>>>0))-1>>>0;while(true){if(!((b.$high>aa.$high||(b.$high===aa.$high&&b.$low>=aa.$low)))){break;}i=i-(1)>>0;((i<0||i>=h.length)?($throwRuntimeError("index out of range"),undefined):h[i]="0123456789abcdefghijklmnopqrstuvwxyz".charCodeAt(((((b.$low>>>0))&ab)>>>0)));b=$shiftRightUint64(b,(z));}i=i-(1)>>0;((i<0||i>=h.length)?($throwRuntimeError("index out of range"),undefined):h[i]="0123456789abcdefghijklmnopqrstuvwxyz".charCodeAt(((b.$low>>>0))));}else{ac=(new $Uint64(0,c));while(true){if(!((b.$high>ac.$high||(b.$high===ac.$high&&b.$low>=ac.$low)))){break;}i=i-(1)>>0;ad=$div64(b,ac,false);((i<0||i>=h.length)?($throwRuntimeError("index out of range"),undefined):h[i]="0123456789abcdefghijklmnopqrstuvwxyz".charCodeAt((((ae=$mul64(ad,ac),new $Uint64(b.$high-ae.$high,b.$low-ae.$low)).$low>>>0))));b=ad;}i=i-(1)>>0;((i<0||i>=h.length)?($throwRuntimeError("index out of range"),undefined):h[i]="0123456789abcdefghijklmnopqrstuvwxyz".charCodeAt(((b.$low>>>0))));}if(d){i=i-(1)>>0;((i<0||i>=h.length)?($throwRuntimeError("index out of range"),undefined):h[i]=45);}if(e){f=$appendSlice(a,$subslice(new DE(h),i));return[f,g];}g=($bytesToString($subslice(new DE(h),i)));return[f,g];};BT=function(a){var a;return(a&((a-1>>0)))===0;};BU=function(a,b,c,d){var a,b,c,d,e;return($bytesToString(BW($makeSlice(DE,0,(e=($imul(3,a.length))/2,(e===e&&e!==1/0&&e!==-1/0)?e>>0:$throwRuntimeError("integer divide by zero"))),a,b,c,d)));};BW=function(a,b,c,d,e){var a,b,c,d,e,f,g,h;a=$append(a,c);f=0;while(true){if(!(b.length>0)){break;}g=((b.charCodeAt(0)>>0));f=1;if(g>=128){h=E.DecodeRuneInString(b);g=h[0];f=h[1];}if((f===1)&&(g===65533)){a=$appendSlice(a,"\\x");a=$append(a,"0123456789abcdef".charCodeAt((b.charCodeAt(0)>>>4<<24>>>24)));a=$append(a,"0123456789abcdef".charCodeAt(((b.charCodeAt(0)&15)>>>0)));b=$substring(b,f);continue;}a=BY(a,g,c,d,e);b=$substring(b,f);}a=$append(a,c);return a;};BX=function(a,b,c,d,e){var a,b,c,d,e;a=$append(a,c);if(!E.ValidRune(b)){b=65533;}a=BY(a,b,c,d,e);a=$append(a,c);return a;};BY=function(a,b,c,d,e){var a,b,c,d,e,f,g,h,i,j;f=DK.zero();if((b===((c>>0)))||(b===92)){a=$append(a,92);a=$append(a,((b<<24>>>24)));return a;}if(d){if(b<128&&CS(b)){a=$append(a,((b<<24>>>24)));return a;}}else if(CS(b)||e&&CU(b)){g=E.EncodeRune(new DE(f),b);a=$appendSlice(a,$subslice(new DE(f),0,g));return a;}h=b;if(h===(7)){a=$appendSlice(a,"\\a");}else if(h===(8)){a=$appendSlice(a,"\\b");}else if(h===(12)){a=$appendSlice(a,"\\f");}else if(h===(10)){a=$appendSlice(a,"\\n");}else if(h===(13)){a=$appendSlice(a,"\\r");}else if(h===(9)){a=$appendSlice(a,"\\t");}else if(h===(11)){a=$appendSlice(a,"\\v");}else{if(b<32){a=$appendSlice(a,"\\x");a=$append(a,"0123456789abcdef".charCodeAt((((b<<24>>>24))>>>4<<24>>>24)));a=$append(a,"0123456789abcdef".charCodeAt(((((b<<24>>>24))&15)>>>0)));}else if(b>1114111){b=65533;a=$appendSlice(a,"\\u");i=12;while(true){if(!(i>=0)){break;}a=$append(a,"0123456789abcdef".charCodeAt((((b>>$min(((i>>>0)),31))>>0)&15)));i=i-(4)>>0;}}else if(b<65536){a=$appendSlice(a,"\\u");i=12;while(true){if(!(i>=0)){break;}a=$append(a,"0123456789abcdef".charCodeAt((((b>>$min(((i>>>0)),31))>>0)&15)));i=i-(4)>>0;}}else{a=$appendSlice(a,"\\U");j=28;while(true){if(!(j>=0)){break;}a=$append(a,"0123456789abcdef".charCodeAt((((b>>$min(((j>>>0)),31))>>0)&15)));j=j-(4)>>0;}}}return a;};BZ=function(a){var a;return BU(a,34,false,false);};$pkg.Quote=BZ;CA=function(a,b){var a,b;return BW(a,b,34,false,false);};$pkg.AppendQuote=CA;CB=function(a){var a;return BU(a,34,true,false);};$pkg.QuoteToASCII=CB;CC=function(a,b){var a,b;return BW(a,b,34,true,false);};$pkg.AppendQuoteToASCII=CC;CG=function(a,b){var a,b;return BX(a,b,39,false,false);};$pkg.AppendQuoteRune=CG;CI=function(a,b){var a,b;return BX(a,b,39,true,false);};$pkg.AppendQuoteRuneToASCII=CI;CL=function(a){var a,b,c,d;while(true){if(!(a.length>0)){break;}b=E.DecodeRuneInString(a);c=b[0];d=b[1];a=$substring(a,d);if(d>1){if(c===65279){return false;}continue;}if(c===65533){return false;}if((c<32&&!((c===9)))||(c===96)||(c===127)){return false;}}return true;};$pkg.CanBackquote=CL;CM=function(a){var a,b,c,d,e,f,g,h,i,j;b=0;c=false;d=((a>>0));if(48<=d&&d<=57){e=d-48>>0;f=true;b=e;c=f;return[b,c];}else if(97<=d&&d<=102){g=(d-97>>0)+10>>0;h=true;b=g;c=h;return[b,c];}else if(65<=d&&d<=70){i=(d-65>>0)+10>>0;j=true;b=i;c=j;return[b,c];}return[b,c];};CN=function(a,b){var a,aa,ab,ac,ad,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;c=0;d=false;e="";f=$ifaceNil;if(a.length===0){f=$pkg.ErrSyntax;return[c,d,e,f];}g=a.charCodeAt(0);if((g===b)&&((b===39)||(b===34))){f=$pkg.ErrSyntax;return[c,d,e,f];}else if(g>=128){h=E.DecodeRuneInString(a);i=h[0];j=h[1];k=i;l=true;m=$substring(a,j);n=$ifaceNil;c=k;d=l;e=m;f=n;return[c,d,e,f];}else if(!((g===92))){o=((a.charCodeAt(0)>>0));p=false;q=$substring(a,1);r=$ifaceNil;c=o;d=p;e=q;f=r;return[c,d,e,f];}if(a.length<=1){f=$pkg.ErrSyntax;return[c,d,e,f];}s=a.charCodeAt(1);a=$substring(a,2);switch(0){default:t=s;if(t===(97)){c=7;}else if(t===(98)){c=8;}else if(t===(102)){c=12;}else if(t===(110)){c=10;}else if(t===(114)){c=13;}else if(t===(116)){c=9;}else if(t===(118)){c=11;}else if((t===(120))||(t===(117))||(t===(85))){u=0;v=s;if(v===(120)){u=2;}else if(v===(117)){u=4;}else if(v===(85)){u=8;}w=0;if(a.length>0)|z;x=x+(1)>>0;}a=$substring(a,u);if(s===120){c=w;break;}if(w>1114111){f=$pkg.ErrSyntax;return[c,d,e,f];}c=w;d=true;}else if((t===(48))||(t===(49))||(t===(50))||(t===(51))||(t===(52))||(t===(53))||(t===(54))||(t===(55))){ab=((s>>0))-48>>0;if(a.length<2){f=$pkg.ErrSyntax;return[c,d,e,f];}ac=0;while(true){if(!(ac<2)){break;}ad=((a.charCodeAt(ac)>>0))-48>>0;if(ad<0||ad>7){f=$pkg.ErrSyntax;return[c,d,e,f];}ab=((ab<<3>>0))|ad;ac=ac+(1)>>0;}a=$substring(a,2);if(ab>255){f=$pkg.ErrSyntax;return[c,d,e,f];}c=ab;}else if(t===(92)){c=92;}else if((t===(39))||(t===(34))){if(!((s===b))){f=$pkg.ErrSyntax;return[c,d,e,f];}c=((s>>0));}else{f=$pkg.ErrSyntax;return[c,d,e,f];}}e=a;return[c,d,e,f];};$pkg.UnquoteChar=CN;CO=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;b=a.length;if(b<2){return["",$pkg.ErrSyntax];}c=a.charCodeAt(0);if(!((c===a.charCodeAt((b-1>>0))))){return["",$pkg.ErrSyntax];}a=$substring(a,1,(b-1>>0));if(c===96){if(CP(a,96)){return["",$pkg.ErrSyntax];}if(CP(a,13)){d=$makeSlice(DE,0,(a.length-1>>0));e=0;while(true){if(!(e>0;}return[($bytesToString(d)),$ifaceNil];}return[a,$ifaceNil];}if(!((c===34))&&!((c===39))){return["",$pkg.ErrSyntax];}if(CP(a,10)){return["",$pkg.ErrSyntax];}if(!CP(a,92)&&!CP(a,c)){f=c;if(f===(34)){if(E.ValidString(a)){return[a,$ifaceNil];}}else if(f===(39)){g=E.DecodeRuneInString(a);h=g[0];i=g[1];if((i===a.length)&&(!((h===65533))||!((i===1)))){return[a,$ifaceNil];}}}j=DK.zero();l=$makeSlice(DE,0,(k=($imul(3,a.length))/2,(k===k&&k!==1/0&&k!==-1/0)?k>>0:$throwRuntimeError("integer divide by zero")));while(true){if(!(a.length>0)){break;}m=CN(a,c);n=m[0];o=m[1];p=m[2];q=m[3];if(!($interfaceIsEqual(q,$ifaceNil))){return["",q];}a=p;if(n<128||!o){l=$append(l,((n<<24>>>24)));}else{r=E.EncodeRune(new DE(j),n);l=$appendSlice(l,$subslice(new DE(j),0,r));}if((c===39)&&!((a.length===0))){return["",$pkg.ErrSyntax];}}return[($bytesToString(l)),$ifaceNil];};$pkg.Unquote=CO;CP=function(a,b){var a,b;return!((D.IndexByteString(a,b)===-1));};CQ=function(a,b){var a,b,c,d,e,f,g,h;c=0;d=a.$length;e=c;f=d;while(true){if(!(e>0))/2,(g===g&&g!==1/0&&g!==-1/0)?g>>0:$throwRuntimeError("integer divide by zero"))>>0;if(((h<0||h>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+h])>0;}else{f=h;}}return e;};CR=function(a,b){var a,b,c,d,e,f,g,h;c=0;d=a.$length;e=c;f=d;while(true){if(!(e>0))/2,(g===g&&g!==1/0&&g!==-1/0)?g>>0:$throwRuntimeError("integer divide by zero"))>>0;if(((h<0||h>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+h])>0;}else{f=h;}}return e;};CS=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;if(a<=255){if(32<=a&&a<=126){return true;}if(161<=a&&a<=255){return!((a===173));}return false;}if(0<=a&&a<65536){b=((a<<16>>>16));c=BH;d=BI;e=b;f=c;g=d;h=CQ(f,e);if(h>=f.$length||e<(i=(h&~1)>>0,((i<0||i>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+i]))||(j=h|1,((j<0||j>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+j]))=g.$length||!((((k<0||k>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+k])===e));}l=((a>>>0));m=BJ;n=BK;o=l;p=m;q=n;r=CR(p,o);if(r>=p.$length||o<(s=(r&~1)>>0,((s<0||s>=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+s]))||(t=r|1,((t<0||t>=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+t]))=131072){return true;}a=a-(65536)>>0;u=CQ(q,((a<<16>>>16)));return u>=q.$length||!((((u<0||u>=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+u])===((a<<16>>>16))));};$pkg.IsPrint=CS;CU=function(a){var a,b,c;if(a>65535){return false;}b=((a<<16>>>16));c=CQ(BL,b);return c=BL.$length)?($throwRuntimeError("index out of range"),undefined):BL.$array[BL.$offset+c]));};DF.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];DL.methods=[{prop:"set",name:"set",pkg:"strconv",typ:$funcType([$String],[$Bool],false)},{prop:"floatBits",name:"floatBits",pkg:"strconv",typ:$funcType([DI],[$Uint64,$Bool],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"Assign",name:"Assign",pkg:"",typ:$funcType([$Uint64],[],false)},{prop:"Shift",name:"Shift",pkg:"",typ:$funcType([$Int],[],false)},{prop:"Round",name:"Round",pkg:"",typ:$funcType([$Int],[],false)},{prop:"RoundDown",name:"RoundDown",pkg:"",typ:$funcType([$Int],[],false)},{prop:"RoundUp",name:"RoundUp",pkg:"",typ:$funcType([$Int],[],false)},{prop:"RoundedInteger",name:"RoundedInteger",pkg:"",typ:$funcType([],[$Uint64],false)}];DN.methods=[{prop:"floatBits",name:"floatBits",pkg:"strconv",typ:$funcType([DI],[$Uint64,$Bool],false)},{prop:"AssignComputeBounds",name:"AssignComputeBounds",pkg:"",typ:$funcType([$Uint64,$Int,$Bool,DI],[AL,AL],false)},{prop:"Normalize",name:"Normalize",pkg:"",typ:$funcType([],[$Uint],false)},{prop:"Multiply",name:"Multiply",pkg:"",typ:$funcType([AL],[],false)},{prop:"AssignDecimal",name:"AssignDecimal",pkg:"",typ:$funcType([$Uint64,$Int,$Bool,$Bool,DI],[$Bool],false)},{prop:"frexp10",name:"frexp10",pkg:"strconv",typ:$funcType([],[$Int,$Int],false)},{prop:"FixedDecimal",name:"FixedDecimal",pkg:"",typ:$funcType([DM,$Int],[$Bool],false)},{prop:"ShortestDecimal",name:"ShortestDecimal",pkg:"",typ:$funcType([DM,DN,DN],[$Bool],false)}];U.init("",[{prop:"Func",name:"Func",embedded:false,exported:true,typ:$String,tag:""},{prop:"Num",name:"Num",embedded:false,exported:true,typ:$String,tag:""},{prop:"Err",name:"Err",embedded:false,exported:true,typ:$error,tag:""}]);AC.init("strconv",[{prop:"d",name:"d",embedded:false,exported:false,typ:DD,tag:""},{prop:"nd",name:"nd",embedded:false,exported:false,typ:$Int,tag:""},{prop:"dp",name:"dp",embedded:false,exported:false,typ:$Int,tag:""},{prop:"neg",name:"neg",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"trunc",name:"trunc",embedded:false,exported:false,typ:$Bool,tag:""}]);AG.init("strconv",[{prop:"delta",name:"delta",embedded:false,exported:false,typ:$Int,tag:""},{prop:"cutoff",name:"cutoff",embedded:false,exported:false,typ:$String,tag:""}]);AL.init("strconv",[{prop:"mant",name:"mant",embedded:false,exported:false,typ:$Uint64,tag:""},{prop:"exp",name:"exp",embedded:false,exported:false,typ:$Int,tag:""},{prop:"neg",name:"neg",embedded:false,exported:false,typ:$Bool,tag:""}]);AS.init("strconv",[{prop:"mantbits",name:"mantbits",embedded:false,exported:false,typ:$Uint,tag:""},{prop:"expbits",name:"expbits",embedded:false,exported:false,typ:$Uint,tag:""},{prop:"bias",name:"bias",embedded:false,exported:false,typ:$Int,tag:""}]);BB.init("strconv",[{prop:"d",name:"d",embedded:false,exported:false,typ:DE,tag:""},{prop:"nd",name:"nd",embedded:false,exported:false,typ:$Int,tag:""},{prop:"dp",name:"dp",embedded:false,exported:false,typ:$Int,tag:""},{prop:"neg",name:"neg",embedded:false,exported:false,typ:$Bool,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=B.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}I=true;$pkg.ErrRange=B.New("value out of range");$pkg.ErrSyntax=B.New("invalid syntax");AH=new DA([new AG.ptr(0,""),new AG.ptr(1,"5"),new AG.ptr(1,"25"),new AG.ptr(1,"125"),new AG.ptr(2,"625"),new AG.ptr(2,"3125"),new AG.ptr(2,"15625"),new AG.ptr(3,"78125"),new AG.ptr(3,"390625"),new AG.ptr(3,"1953125"),new AG.ptr(4,"9765625"),new AG.ptr(4,"48828125"),new AG.ptr(4,"244140625"),new AG.ptr(4,"1220703125"),new AG.ptr(5,"6103515625"),new AG.ptr(5,"30517578125"),new AG.ptr(5,"152587890625"),new AG.ptr(6,"762939453125"),new AG.ptr(6,"3814697265625"),new AG.ptr(6,"19073486328125"),new AG.ptr(7,"95367431640625"),new AG.ptr(7,"476837158203125"),new AG.ptr(7,"2384185791015625"),new AG.ptr(7,"11920928955078125"),new AG.ptr(8,"59604644775390625"),new AG.ptr(8,"298023223876953125"),new AG.ptr(8,"1490116119384765625"),new AG.ptr(9,"7450580596923828125"),new AG.ptr(9,"37252902984619140625"),new AG.ptr(9,"186264514923095703125"),new AG.ptr(10,"931322574615478515625"),new AG.ptr(10,"4656612873077392578125"),new AG.ptr(10,"23283064365386962890625"),new AG.ptr(10,"116415321826934814453125"),new AG.ptr(11,"582076609134674072265625"),new AG.ptr(11,"2910383045673370361328125"),new AG.ptr(11,"14551915228366851806640625"),new AG.ptr(12,"72759576141834259033203125"),new AG.ptr(12,"363797880709171295166015625"),new AG.ptr(12,"1818989403545856475830078125"),new AG.ptr(13,"9094947017729282379150390625"),new AG.ptr(13,"45474735088646411895751953125"),new AG.ptr(13,"227373675443232059478759765625"),new AG.ptr(13,"1136868377216160297393798828125"),new AG.ptr(14,"5684341886080801486968994140625"),new AG.ptr(14,"28421709430404007434844970703125"),new AG.ptr(14,"142108547152020037174224853515625"),new AG.ptr(15,"710542735760100185871124267578125"),new AG.ptr(15,"3552713678800500929355621337890625"),new AG.ptr(15,"17763568394002504646778106689453125"),new AG.ptr(16,"88817841970012523233890533447265625"),new AG.ptr(16,"444089209850062616169452667236328125"),new AG.ptr(16,"2220446049250313080847263336181640625"),new AG.ptr(16,"11102230246251565404236316680908203125"),new AG.ptr(17,"55511151231257827021181583404541015625"),new AG.ptr(17,"277555756156289135105907917022705078125"),new AG.ptr(17,"1387778780781445675529539585113525390625"),new AG.ptr(18,"6938893903907228377647697925567626953125"),new AG.ptr(18,"34694469519536141888238489627838134765625"),new AG.ptr(18,"173472347597680709441192448139190673828125"),new AG.ptr(19,"867361737988403547205962240695953369140625")]);AM=$toNativeArray($kindStruct,[new AL.ptr(new $Uint64(2147483648,0),-63,false),new AL.ptr(new $Uint64(2684354560,0),-60,false),new AL.ptr(new $Uint64(3355443200,0),-57,false),new AL.ptr(new $Uint64(4194304000,0),-54,false),new AL.ptr(new $Uint64(2621440000,0),-50,false),new AL.ptr(new $Uint64(3276800000,0),-47,false),new AL.ptr(new $Uint64(4096000000,0),-44,false),new AL.ptr(new $Uint64(2560000000,0),-40,false)]);AN=$toNativeArray($kindStruct,[new AL.ptr(new $Uint64(4203730336,136053384),-1220,false),new AL.ptr(new $Uint64(3132023167,2722021238),-1193,false),new AL.ptr(new $Uint64(2333539104,810921078),-1166,false),new AL.ptr(new $Uint64(3477244234,1573795306),-1140,false),new AL.ptr(new $Uint64(2590748842,1432697645),-1113,false),new AL.ptr(new $Uint64(3860516611,1025131999),-1087,false),new AL.ptr(new $Uint64(2876309015,3348809418),-1060,false),new AL.ptr(new $Uint64(4286034428,3200048207),-1034,false),new AL.ptr(new $Uint64(3193344495,1097586188),-1007,false),new AL.ptr(new $Uint64(2379227053,2424306748),-980,false),new AL.ptr(new $Uint64(3545324584,827693699),-954,false),new AL.ptr(new $Uint64(2641472655,2913388981),-927,false),new AL.ptr(new $Uint64(3936100983,602835915),-901,false),new AL.ptr(new $Uint64(2932623761,1081627501),-874,false),new AL.ptr(new $Uint64(2184974969,1572261463),-847,false),new AL.ptr(new $Uint64(3255866422,1308317239),-821,false),new AL.ptr(new $Uint64(2425809519,944281679),-794,false),new AL.ptr(new $Uint64(3614737867,629291719),-768,false),new AL.ptr(new $Uint64(2693189581,2545915892),-741,false),new AL.ptr(new $Uint64(4013165208,388672741),-715,false),new AL.ptr(new $Uint64(2990041083,708162190),-688,false),new AL.ptr(new $Uint64(2227754207,3536207675),-661,false),new AL.ptr(new $Uint64(3319612455,450088378),-635,false),new AL.ptr(new $Uint64(2473304014,3139815830),-608,false),new AL.ptr(new $Uint64(3685510180,2103616900),-582,false),new AL.ptr(new $Uint64(2745919064,224385782),-555,false),new AL.ptr(new $Uint64(4091738259,3737383206),-529,false),new AL.ptr(new $Uint64(3048582568,2868871352),-502,false),new AL.ptr(new $Uint64(2271371013,1820084875),-475,false),new AL.ptr(new $Uint64(3384606560,885076051),-449,false),new AL.ptr(new $Uint64(2521728396,2444895829),-422,false),new AL.ptr(new $Uint64(3757668132,1881767613),-396,false),new AL.ptr(new $Uint64(2799680927,3102062735),-369,false),new AL.ptr(new $Uint64(4171849679,2289335700),-343,false),new AL.ptr(new $Uint64(3108270227,2410191823),-316,false),new AL.ptr(new $Uint64(2315841784,3205436779),-289,false),new AL.ptr(new $Uint64(3450873173,1697722806),-263,false),new AL.ptr(new $Uint64(2571100870,3497754540),-236,false),new AL.ptr(new $Uint64(3831238852,707476230),-210,false),new AL.ptr(new $Uint64(2854495385,1769181907),-183,false),new AL.ptr(new $Uint64(4253529586,2197867022),-157,false),new AL.ptr(new $Uint64(3169126500,2450594539),-130,false),new AL.ptr(new $Uint64(2361183241,1867548876),-103,false),new AL.ptr(new $Uint64(3518437208,3793315116),-77,false),new AL.ptr(new $Uint64(2621440000,0),-50,false),new AL.ptr(new $Uint64(3906250000,0),-24,false),new AL.ptr(new $Uint64(2910383045,2892103680),3,false),new AL.ptr(new $Uint64(2168404344,4170451332),30,false),new AL.ptr(new $Uint64(3231174267,3372684723),56,false),new AL.ptr(new $Uint64(2407412430,2078956656),83,false),new AL.ptr(new $Uint64(3587324068,2884206696),109,false),new AL.ptr(new $Uint64(2672764710,395977285),136,false),new AL.ptr(new $Uint64(3982729777,3569679143),162,false),new AL.ptr(new $Uint64(2967364920,2361961896),189,false),new AL.ptr(new $Uint64(2210859150,447440347),216,false),new AL.ptr(new $Uint64(3294436857,1114709402),242,false),new AL.ptr(new $Uint64(2454546732,2786846552),269,false),new AL.ptr(new $Uint64(3657559652,443583978),295,false),new AL.ptr(new $Uint64(2725094297,2599384906),322,false),new AL.ptr(new $Uint64(4060706939,3028118405),348,false),new AL.ptr(new $Uint64(3025462433,2044532855),375,false),new AL.ptr(new $Uint64(2254145170,1536935362),402,false),new AL.ptr(new $Uint64(3358938053,3365297469),428,false),new AL.ptr(new $Uint64(2502603868,4204241075),455,false),new AL.ptr(new $Uint64(3729170365,2577424355),481,false),new AL.ptr(new $Uint64(2778448436,3677981733),508,false),new AL.ptr(new $Uint64(4140210802,2744688476),534,false),new AL.ptr(new $Uint64(3084697427,1424604878),561,false),new AL.ptr(new $Uint64(2298278679,4062331362),588,false),new AL.ptr(new $Uint64(3424702107,3546052773),614,false),new AL.ptr(new $Uint64(2551601907,2065781727),641,false),new AL.ptr(new $Uint64(3802183132,2535403578),667,false),new AL.ptr(new $Uint64(2832847187,1558426518),694,false),new AL.ptr(new $Uint64(4221271257,2762425404),720,false),new AL.ptr(new $Uint64(3145092172,2812560400),747,false),new AL.ptr(new $Uint64(2343276271,3057687578),774,false),new AL.ptr(new $Uint64(3491753744,2790753324),800,false),new AL.ptr(new $Uint64(2601559269,3918606633),827,false),new AL.ptr(new $Uint64(3876625403,2711358621),853,false),new AL.ptr(new $Uint64(2888311001,1648096297),880,false),new AL.ptr(new $Uint64(2151959390,2057817989),907,false),new AL.ptr(new $Uint64(3206669376,61660461),933,false),new AL.ptr(new $Uint64(2389154863,1581580175),960,false),new AL.ptr(new $Uint64(3560118173,2626467905),986,false),new AL.ptr(new $Uint64(2652494738,3034782633),1013,false),new AL.ptr(new $Uint64(3952525166,3135207385),1039,false),new AL.ptr(new $Uint64(2944860731,2616258155),1066,false)]);AO=$toNativeArray($kindUint64,[new $Uint64(0,1),new $Uint64(0,10),new $Uint64(0,100),new $Uint64(0,1000),new $Uint64(0,10000),new $Uint64(0,100000),new $Uint64(0,1000000),new $Uint64(0,10000000),new $Uint64(0,100000000),new $Uint64(0,1000000000),new $Uint64(2,1410065408),new $Uint64(23,1215752192),new $Uint64(232,3567587328),new $Uint64(2328,1316134912),new $Uint64(23283,276447232),new $Uint64(232830,2764472320),new $Uint64(2328306,1874919424),new $Uint64(23283064,1569325056),new $Uint64(232830643,2808348672),new $Uint64(2328306436,2313682944)]);AT=new AS.ptr(23,8,-127);AU=new AS.ptr(52,11,-1023);BH=new DB([32,126,161,887,890,895,900,1366,1369,1418,1421,1479,1488,1514,1520,1524,1542,1563,1566,1805,1808,1866,1869,1969,1984,2042,2048,2093,2096,2139,2142,2154,2208,2237,2260,2444,2447,2448,2451,2482,2486,2489,2492,2500,2503,2504,2507,2510,2519,2519,2524,2531,2534,2557,2561,2570,2575,2576,2579,2617,2620,2626,2631,2632,2635,2637,2641,2641,2649,2654,2662,2677,2689,2745,2748,2765,2768,2768,2784,2787,2790,2801,2809,2828,2831,2832,2835,2873,2876,2884,2887,2888,2891,2893,2902,2903,2908,2915,2918,2935,2946,2954,2958,2965,2969,2975,2979,2980,2984,2986,2990,3001,3006,3010,3014,3021,3024,3024,3031,3031,3046,3066,3072,3129,3133,3149,3157,3162,3168,3171,3174,3183,3192,3257,3260,3277,3285,3286,3294,3299,3302,3314,3328,3407,3412,3427,3430,3455,3458,3478,3482,3517,3520,3526,3530,3530,3535,3551,3558,3567,3570,3572,3585,3642,3647,3675,3713,3716,3719,3722,3725,3725,3732,3751,3754,3773,3776,3789,3792,3801,3804,3807,3840,3948,3953,4058,4096,4295,4301,4301,4304,4685,4688,4701,4704,4749,4752,4789,4792,4805,4808,4885,4888,4954,4957,4988,4992,5017,5024,5109,5112,5117,5120,5788,5792,5880,5888,5908,5920,5942,5952,5971,5984,6003,6016,6109,6112,6121,6128,6137,6144,6157,6160,6169,6176,6263,6272,6314,6320,6389,6400,6443,6448,6459,6464,6464,6468,6509,6512,6516,6528,6571,6576,6601,6608,6618,6622,6683,6686,6780,6783,6793,6800,6809,6816,6829,6832,6846,6912,6987,6992,7036,7040,7155,7164,7223,7227,7241,7245,7304,7360,7367,7376,7417,7424,7957,7960,7965,7968,8005,8008,8013,8016,8061,8064,8147,8150,8175,8178,8190,8208,8231,8240,8286,8304,8305,8308,8348,8352,8383,8400,8432,8448,8587,8592,9254,9280,9290,9312,11123,11126,11157,11160,11193,11197,11218,11244,11247,11264,11507,11513,11559,11565,11565,11568,11623,11631,11632,11647,11670,11680,11849,11904,12019,12032,12245,12272,12283,12289,12438,12441,12543,12549,12590,12593,12730,12736,12771,12784,19893,19904,40938,40960,42124,42128,42182,42192,42539,42560,42743,42752,42935,42999,43051,43056,43065,43072,43127,43136,43205,43214,43225,43232,43261,43264,43347,43359,43388,43392,43481,43486,43574,43584,43597,43600,43609,43612,43714,43739,43766,43777,43782,43785,43790,43793,43798,43808,43877,43888,44013,44016,44025,44032,55203,55216,55238,55243,55291,63744,64109,64112,64217,64256,64262,64275,64279,64285,64449,64467,64831,64848,64911,64914,64967,65008,65021,65024,65049,65056,65131,65136,65276,65281,65470,65474,65479,65482,65487,65490,65495,65498,65500,65504,65518,65532,65533]);BI=new DB([173,907,909,930,1328,1376,1416,1424,1757,2111,2143,2229,2274,2436,2473,2481,2526,2564,2601,2609,2612,2615,2621,2653,2692,2702,2706,2729,2737,2740,2758,2762,2816,2820,2857,2865,2868,2910,2948,2961,2971,2973,3017,3076,3085,3089,3113,3141,3145,3159,3204,3213,3217,3241,3252,3269,3273,3295,3312,3332,3341,3345,3397,3401,3460,3506,3516,3541,3543,3715,3721,3736,3744,3748,3750,3756,3770,3781,3783,3912,3992,4029,4045,4294,4681,4695,4697,4745,4785,4799,4801,4823,4881,5760,5901,5997,6001,6431,6751,7674,8024,8026,8028,8030,8117,8133,8156,8181,8335,11209,11311,11359,11558,11687,11695,11703,11711,11719,11727,11735,11743,11930,12352,12687,12831,13055,42927,43470,43519,43815,43823,64311,64317,64319,64322,64325,65107,65127,65141,65511]);BJ=new DC([65536,65613,65616,65629,65664,65786,65792,65794,65799,65843,65847,65947,65952,65952,66000,66045,66176,66204,66208,66256,66272,66299,66304,66339,66349,66378,66384,66426,66432,66499,66504,66517,66560,66717,66720,66729,66736,66771,66776,66811,66816,66855,66864,66915,66927,66927,67072,67382,67392,67413,67424,67431,67584,67589,67592,67640,67644,67644,67647,67742,67751,67759,67808,67829,67835,67867,67871,67897,67903,67903,67968,68023,68028,68047,68050,68102,68108,68147,68152,68154,68159,68167,68176,68184,68192,68255,68288,68326,68331,68342,68352,68405,68409,68437,68440,68466,68472,68497,68505,68508,68521,68527,68608,68680,68736,68786,68800,68850,68858,68863,69216,69246,69632,69709,69714,69743,69759,69825,69840,69864,69872,69881,69888,69955,69968,70006,70016,70093,70096,70132,70144,70206,70272,70313,70320,70378,70384,70393,70400,70412,70415,70416,70419,70457,70460,70468,70471,70472,70475,70477,70480,70480,70487,70487,70493,70499,70502,70508,70512,70516,70656,70749,70784,70855,70864,70873,71040,71093,71096,71133,71168,71236,71248,71257,71264,71276,71296,71351,71360,71369,71424,71449,71453,71467,71472,71487,71840,71922,71935,71935,72192,72263,72272,72323,72326,72354,72384,72440,72704,72773,72784,72812,72816,72847,72850,72886,72960,73014,73018,73031,73040,73049,73728,74649,74752,74868,74880,75075,77824,78894,82944,83526,92160,92728,92736,92777,92782,92783,92880,92909,92912,92917,92928,92997,93008,93047,93053,93071,93952,94020,94032,94078,94095,94111,94176,94177,94208,100332,100352,101106,110592,110878,110960,111355,113664,113770,113776,113788,113792,113800,113808,113817,113820,113823,118784,119029,119040,119078,119081,119154,119163,119272,119296,119365,119552,119638,119648,119665,119808,119967,119970,119970,119973,119974,119977,120074,120077,120134,120138,120485,120488,120779,120782,121483,121499,121519,122880,122904,122907,122922,124928,125124,125127,125142,125184,125258,125264,125273,125278,125279,126464,126500,126503,126523,126530,126530,126535,126548,126551,126564,126567,126619,126625,126651,126704,126705,126976,127019,127024,127123,127136,127150,127153,127221,127232,127244,127248,127339,127344,127404,127462,127490,127504,127547,127552,127560,127568,127569,127584,127589,127744,128724,128736,128748,128752,128760,128768,128883,128896,128980,129024,129035,129040,129095,129104,129113,129120,129159,129168,129197,129280,129291,129296,129356,129360,129387,129408,129431,129472,129472,129488,129510,131072,173782,173824,177972,177984,178205,178208,183969,183984,191456,194560,195101,917760,917999]);BK=new DB([12,39,59,62,399,926,2057,2102,2134,2291,2564,2580,2584,4285,4405,4576,4626,4743,4745,4750,4766,4868,4905,4913,4916,5210,5212,6813,7177,7223,7336,7431,7434,7483,7486,9327,27231,27482,27490,54357,54429,54445,54458,54460,54468,54534,54549,54557,54586,54591,54597,54609,55968,57351,57378,57381,60932,60960,60963,60968,60979,60984,60986,61000,61002,61004,61008,61011,61016,61018,61020,61022,61024,61027,61035,61043,61048,61053,61055,61066,61092,61098,61632,61648,61743,63807]);BL=new DB([160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8239,8287,12288]);}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["internal/race"]=(function(){var $pkg={},$init,A,B,C,D,E,H,I;A=function(a){var a;};$pkg.Acquire=A;B=function(a){var a;};$pkg.Release=B;C=function(a){var a;};$pkg.ReleaseMerge=C;D=function(){};$pkg.Disable=D;E=function(){};$pkg.Enable=E;H=function(a,b){var a,b;};$pkg.ReadRange=H;I=function(a,b){var a,b;};$pkg.WriteRange=I;$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["sync/atomic"]=(function(){var $pkg={},$init,A,AF,AJ,H,K,N,O,S,U,V,Y,AA;A=$packages["github.com/gopherjs/gopherjs/js"];AF=$pkg.Value=$newType(0,$kindStruct,"atomic.Value",true,"sync/atomic",true,function(v_){this.$val=this;if(arguments.length===0){this.v=$ifaceNil;return;}this.v=v_;});AJ=$ptrType(AF);H=function(ad,ae,af){var ad,ae,af;if(ad.$get()===ae){ad.$set(af);return true;}return false;};$pkg.CompareAndSwapInt32=H;K=function(ad,ae,af){var ad,ae,af,ag;if((ag=ad.$get(),(ag.$high===ae.$high&&ag.$low===ae.$low))){ad.$set(af);return true;}return false;};$pkg.CompareAndSwapUint64=K;N=function(ad,ae){var ad,ae,af;af=ad.$get()+ae>>0;ad.$set(af);return af;};$pkg.AddInt32=N;O=function(ad,ae){var ad,ae,af;af=ad.$get()+ae>>>0;ad.$set(af);return af;};$pkg.AddUint32=O;S=function(ad){var ad;return ad.$get();};$pkg.LoadInt32=S;U=function(ad){var ad;return ad.$get();};$pkg.LoadUint32=U;V=function(ad){var ad;return ad.$get();};$pkg.LoadUint64=V;Y=function(ad,ae){var ad,ae;ad.$set(ae);};$pkg.StoreInt32=Y;AA=function(ad,ae){var ad,ae;ad.$set(ae);};$pkg.StoreUint32=AA;AF.ptr.prototype.Load=function(){var ad,ae;ad=$ifaceNil;ae=this;ad=ae.v;return ad;};AF.prototype.Load=function(){return this.$val.Load();};AF.ptr.prototype.Store=function(ad){var ad,ae;ae=this;if($interfaceIsEqual(ad,$ifaceNil)){$panic(new $String("sync/atomic: store of nil value into Value"));}if(!($interfaceIsEqual(ae.v,$ifaceNil))&&!(ad.constructor===ae.v.constructor)){$panic(new $String("sync/atomic: store of inconsistently typed value into Value"));}ae.v=ad;};AF.prototype.Store=function(ad){return this.$val.Store(ad);};AJ.methods=[{prop:"Load",name:"Load",pkg:"",typ:$funcType([],[$emptyInterface],false)},{prop:"Store",name:"Store",pkg:"",typ:$funcType([$emptyInterface],[],false)}];AF.init("sync/atomic",[{prop:"v",name:"v",embedded:false,exported:false,typ:$emptyInterface,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["sync"]=(function(){var $pkg={},$init,A,C,D,B,F,Z,AA,AB,AC,AD,AO,AV,AW,AY,AZ,BA,BB,BC,BI,BL,BM,BN,BO,BR,CA,CB,CC,CD,H,I,W,AJ,G,K,L,M,N,O,P,AH,AK,AL,AT,AU;A=$packages["github.com/gopherjs/gopherjs/js"];C=$packages["internal/race"];D=$packages["runtime"];B=$packages["sync/atomic"];F=$pkg.Pool=$newType(0,$kindStruct,"sync.Pool",true,"sync",true,function(local_,localSize_,store_,New_){this.$val=this;if(arguments.length===0){this.local=0;this.localSize=0;this.store=BM.nil;this.New=$throwNilPointerError;return;}this.local=local_;this.localSize=localSize_;this.store=store_;this.New=New_;});Z=$pkg.Mutex=$newType(0,$kindStruct,"sync.Mutex",true,"sync",true,function(state_,sema_){this.$val=this;if(arguments.length===0){this.state=0;this.sema=0;return;}this.state=state_;this.sema=sema_;});AA=$pkg.Locker=$newType(8,$kindInterface,"sync.Locker",true,"sync",true,null);AB=$pkg.Once=$newType(0,$kindStruct,"sync.Once",true,"sync",true,function(m_,done_){this.$val=this;if(arguments.length===0){this.m=new Z.ptr(0,0);this.done=0;return;}this.m=m_;this.done=done_;});AC=$pkg.poolLocalInternal=$newType(0,$kindStruct,"sync.poolLocalInternal",true,"sync",false,function(private$0_,shared_,Mutex_){this.$val=this;if(arguments.length===0){this.private$0=$ifaceNil;this.shared=BM.nil;this.Mutex=new Z.ptr(0,0);return;}this.private$0=private$0_;this.shared=shared_;this.Mutex=Mutex_;});AD=$pkg.poolLocal=$newType(0,$kindStruct,"sync.poolLocal",true,"sync",false,function(poolLocalInternal_,pad_){this.$val=this;if(arguments.length===0){this.poolLocalInternal=new AC.ptr($ifaceNil,BM.nil,new Z.ptr(0,0));this.pad=CD.zero();return;}this.poolLocalInternal=poolLocalInternal_;this.pad=pad_;});AO=$pkg.notifyList=$newType(0,$kindStruct,"sync.notifyList",true,"sync",false,function(wait_,notify_,lock_,head_,tail_){this.$val=this;if(arguments.length===0){this.wait=0;this.notify=0;this.lock=0;this.head=0;this.tail=0;return;}this.wait=wait_;this.notify=notify_;this.lock=lock_;this.head=head_;this.tail=tail_;});AV=$pkg.RWMutex=$newType(0,$kindStruct,"sync.RWMutex",true,"sync",true,function(w_,writerSem_,readerSem_,readerCount_,readerWait_){this.$val=this;if(arguments.length===0){this.w=new Z.ptr(0,0);this.writerSem=0;this.readerSem=0;this.readerCount=0;this.readerWait=0;return;}this.w=w_;this.writerSem=writerSem_;this.readerSem=readerSem_;this.readerCount=readerCount_;this.readerWait=readerWait_;});AW=$pkg.rlocker=$newType(0,$kindStruct,"sync.rlocker",true,"sync",false,function(w_,writerSem_,readerSem_,readerCount_,readerWait_){this.$val=this;if(arguments.length===0){this.w=new Z.ptr(0,0);this.writerSem=0;this.readerSem=0;this.readerCount=0;this.readerWait=0;return;}this.w=w_;this.writerSem=writerSem_;this.readerSem=readerSem_;this.readerCount=readerCount_;this.readerWait=readerWait_;});AY=$ptrType(F);AZ=$sliceType(AY);BA=$ptrType($Uint32);BB=$chanType($Bool,false,false);BC=$sliceType(BB);BI=$ptrType($Int32);BL=$ptrType(AD);BM=$sliceType($emptyInterface);BN=$ptrType(AW);BO=$ptrType(AV);BR=$funcType([],[$emptyInterface],false);CA=$ptrType(Z);CB=$funcType([],[],false);CC=$ptrType(AB);CD=$arrayType($Uint8,100);F.ptr.prototype.Get=function(){var l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(l.store.$length===0){$s=1;continue;}$s=2;continue;case 1:if(!(l.New===$throwNilPointerError)){$s=3;continue;}$s=4;continue;case 3:m=l.New();$s=5;case 5:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}$s=-1;return m;case 4:$s=-1;return $ifaceNil;case 2:p=(n=l.store,o=l.store.$length-1>>0,((o<0||o>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]));l.store=$subslice(l.store,0,(l.store.$length-1>>0));$s=-1;return p;}return;}if($f===undefined){$f={$blk:F.ptr.prototype.Get};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};F.prototype.Get=function(){return this.$val.Get();};F.ptr.prototype.Put=function(l){var l,m;m=this;if($interfaceIsEqual(l,$ifaceNil)){return;}m.store=$append(m.store,l);};F.prototype.Put=function(l){return this.$val.Put(l);};G=function(l){var l;};K=function(l,m){var l,m,n,o,p,q,r,s,t,u,v,w,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(((l.$get()-(n=I[BA.keyFor(l)],n!==undefined?n.v:0)>>>0))===0){$s=1;continue;}$s=2;continue;case 1:o=new $Chan($Bool,0);if(m){p=l;(H||$throwRuntimeError("assignment to entry in nil map"))[BA.keyFor(p)]={k:p,v:$appendSlice(new BC([o]),(q=H[BA.keyFor(l)],q!==undefined?q.v:BC.nil))};}else{r=l;(H||$throwRuntimeError("assignment to entry in nil map"))[BA.keyFor(r)]={k:r,v:$append((s=H[BA.keyFor(l)],s!==undefined?s.v:BC.nil),o)};}t=$recv(o);$s=3;case 3:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}t[0];u=l;(I||$throwRuntimeError("assignment to entry in nil map"))[BA.keyFor(u)]={k:u,v:(v=I[BA.keyFor(l)],v!==undefined?v.v:0)-(1)>>>0};if((w=I[BA.keyFor(l)],w!==undefined?w.v:0)===0){delete I[BA.keyFor(l)];}case 2:l.$set(l.$get()-(1)>>>0);$s=-1;return;}return;}if($f===undefined){$f={$blk:K};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.$s=$s;$f.$r=$r;return $f;};L=function(l,m){var l,m,n,o,p,q,r,s,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l.$set(l.$get()+(1)>>>0);o=(n=H[BA.keyFor(l)],n!==undefined?n.v:BC.nil);if(o.$length===0){$s=-1;return;}p=(0>=o.$length?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+0]);o=$subslice(o,1);q=l;(H||$throwRuntimeError("assignment to entry in nil map"))[BA.keyFor(q)]={k:q,v:o};if(o.$length===0){delete H[BA.keyFor(l)];}r=l;(I||$throwRuntimeError("assignment to entry in nil map"))[BA.keyFor(r)]={k:r,v:(s=I[BA.keyFor(l)],s!==undefined?s.v:0)+(1)>>>0};$r=$send(p,true);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:L};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.$s=$s;$f.$r=$r;return $f;};M=function(l){var l;};N=function(l){var l;return false;};O=function(){return $mul64($internalize(new($global.Date)().getTime(),$Int64),new $Int64(0,1000000));};P=function(l){var l;$throwRuntimeError($externalize(l,$String));};Z.ptr.prototype.Lock=function(){var l,m,n,o,p,q,r,s,t,u,v,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(B.CompareAndSwapInt32((l.$ptr_state||(l.$ptr_state=new BI(function(){return this.$target.state;},function($v){this.$target.state=$v;},l))),0,1)){if(false){C.Acquire((l));}$s=-1;return;}m=new $Int64(0,0);n=false;o=false;p=0;q=l.state;case 1:if(((q&5)===1)&&N(p)){$s=3;continue;}$s=4;continue;case 3:if(!o&&((q&2)===0)&&!(((q>>3>>0)===0))&&B.CompareAndSwapInt32((l.$ptr_state||(l.$ptr_state=new BI(function(){return this.$target.state;},function($v){this.$target.state=$v;},l))),q,q|2)){o=true;}AU();p=p+(1)>>0;q=l.state;$s=1;continue;case 4:r=q;if((q&4)===0){r=r|(1);}if(!(((q&5)===0))){r=r+(8)>>0;}if(n&&!(((q&1)===0))){r=r|(4);}if(o){if((r&2)===0){P("sync: inconsistent mutex state");}r=(r&~(2))>>0;}if(B.CompareAndSwapInt32((l.$ptr_state||(l.$ptr_state=new BI(function(){return this.$target.state;},function($v){this.$target.state=$v;},l))),q,r)){$s=5;continue;}$s=6;continue;case 5:if((q&5)===0){$s=2;continue;}s=!((m.$high===0&&m.$low===0));if((m.$high===0&&m.$low===0)){m=O();}$r=K((l.$ptr_sema||(l.$ptr_sema=new BA(function(){return this.$target.sema;},function($v){this.$target.sema=$v;},l))),s);$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}n=n||(t=(u=O(),new $Int64(u.$high-m.$high,u.$low-m.$low)),(t.$high>0||(t.$high===0&&t.$low>1000000)));q=l.state;if(!(((q&4)===0))){if(!(((q&3)===0))||((q>>3>>0)===0)){P("sync: inconsistent mutex state");}v=-7;if(!n||((q>>3>>0)===1)){v=v-(4)>>0;}B.AddInt32((l.$ptr_state||(l.$ptr_state=new BI(function(){return this.$target.state;},function($v){this.$target.state=$v;},l))),v);$s=2;continue;}o=true;p=0;$s=7;continue;case 6:q=l.state;case 7:$s=1;continue;case 2:if(false){C.Acquire((l));}$s=-1;return;}return;}if($f===undefined){$f={$blk:Z.ptr.prototype.Lock};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.$s=$s;$f.$r=$r;return $f;};Z.prototype.Lock=function(){return this.$val.Lock();};Z.ptr.prototype.Unlock=function(){var l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(false){$unused(l.state);C.Release((l));}m=B.AddInt32((l.$ptr_state||(l.$ptr_state=new BI(function(){return this.$target.state;},function($v){this.$target.state=$v;},l))),-1);if((((m+1>>0))&1)===0){P("sync: unlock of unlocked mutex");}if((m&4)===0){$s=1;continue;}$s=2;continue;case 1:n=m;case 4:if(((n>>3>>0)===0)||!(((n&7)===0))){$s=-1;return;}m=((n-8>>0))|2;if(B.CompareAndSwapInt32((l.$ptr_state||(l.$ptr_state=new BI(function(){return this.$target.state;},function($v){this.$target.state=$v;},l))),n,m)){$s=6;continue;}$s=7;continue;case 6:$r=L((l.$ptr_sema||(l.$ptr_sema=new BA(function(){return this.$target.sema;},function($v){this.$target.sema=$v;},l))),false);$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 7:n=l.state;$s=4;continue;case 5:$s=3;continue;case 2:$r=L((l.$ptr_sema||(l.$ptr_sema=new BA(function(){return this.$target.sema;},function($v){this.$target.sema=$v;},l))),true);$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 3:$s=-1;return;}return;}if($f===undefined){$f={$blk:Z.ptr.prototype.Unlock};}$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};Z.prototype.Unlock=function(){return this.$val.Unlock();};AB.ptr.prototype.Do=function(l){var l,m,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);m=this;if(B.LoadUint32((m.$ptr_done||(m.$ptr_done=new BA(function(){return this.$target.done;},function($v){this.$target.done=$v;},m))))===1){$s=-1;return;}$r=m.m.Lock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$deferred.push([$methodVal(m.m,"Unlock"),[]]);if(m.done===0){$s=2;continue;}$s=3;continue;case 2:$deferred.push([B.StoreUint32,[(m.$ptr_done||(m.$ptr_done=new BA(function(){return this.$target.done;},function($v){this.$target.done=$v;},m))),1]]);$r=l();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 3:$s=-1;return;}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AB.ptr.prototype.Do};}$f.l=l;$f.m=m;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AB.prototype.Do=function(l){return this.$val.Do(l);};AH=function(){var l,m,n,o,p,q,r,s,t,u;l=AJ;m=0;while(true){if(!(m=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+m]);((n<0||n>=AJ.$length)?($throwRuntimeError("index out of range"),undefined):AJ.$array[AJ.$offset+n]=AY.nil);p=0;while(true){if(!(p<((o.localSize>>0)))){break;}q=AL(o.local,p);q.poolLocalInternal.private$0=$ifaceNil;r=q.poolLocalInternal.shared;s=0;while(true){if(!(s=u.$length)?($throwRuntimeError("index out of range"),undefined):u.$array[u.$offset+t]=$ifaceNil));s++;}q.poolLocalInternal.shared=BM.nil;p=p+(1)>>0;}o.local=0;o.localSize=0;m++;}AJ=new AZ([]);};AK=function(){G(AH);};AL=function(l,m){var l,m,n;n=(((l)+($imul(((m>>>0)),128)>>>0)>>>0));return($pointerOfStructConversion(n,BL));};AT=function(){var l;l=new AO.ptr(0,0,0,0,0);M(20);};AU=function(){$throwRuntimeError("native function not implemented: sync.runtime_doSpin");};AV.ptr.prototype.RLock=function(){var l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(false){$unused(l.w.state);C.Disable();}if(B.AddInt32((l.$ptr_readerCount||(l.$ptr_readerCount=new BI(function(){return this.$target.readerCount;},function($v){this.$target.readerCount=$v;},l))),1)<0){$s=1;continue;}$s=2;continue;case 1:$r=K((l.$ptr_readerSem||(l.$ptr_readerSem=new BA(function(){return this.$target.readerSem;},function($v){this.$target.readerSem=$v;},l))),false);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 2:if(false){C.Enable();C.Acquire(((l.$ptr_readerSem||(l.$ptr_readerSem=new BA(function(){return this.$target.readerSem;},function($v){this.$target.readerSem=$v;},l)))));}$s=-1;return;}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.RLock};}$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.RLock=function(){return this.$val.RLock();};AV.ptr.prototype.RUnlock=function(){var l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(false){$unused(l.w.state);C.ReleaseMerge(((l.$ptr_writerSem||(l.$ptr_writerSem=new BA(function(){return this.$target.writerSem;},function($v){this.$target.writerSem=$v;},l)))));C.Disable();}m=B.AddInt32((l.$ptr_readerCount||(l.$ptr_readerCount=new BI(function(){return this.$target.readerCount;},function($v){this.$target.readerCount=$v;},l))),-1);if(m<0){$s=1;continue;}$s=2;continue;case 1:if(((m+1>>0)===0)||((m+1>>0)===-1073741824)){C.Enable();P("sync: RUnlock of unlocked RWMutex");}if(B.AddInt32((l.$ptr_readerWait||(l.$ptr_readerWait=new BI(function(){return this.$target.readerWait;},function($v){this.$target.readerWait=$v;},l))),-1)===0){$s=3;continue;}$s=4;continue;case 3:$r=L((l.$ptr_writerSem||(l.$ptr_writerSem=new BA(function(){return this.$target.writerSem;},function($v){this.$target.writerSem=$v;},l))),false);$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 4:case 2:if(false){C.Enable();}$s=-1;return;}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.RUnlock};}$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.RUnlock=function(){return this.$val.RUnlock();};AV.ptr.prototype.Lock=function(){var l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(false){$unused(l.w.state);C.Disable();}$r=l.w.Lock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}m=B.AddInt32((l.$ptr_readerCount||(l.$ptr_readerCount=new BI(function(){return this.$target.readerCount;},function($v){this.$target.readerCount=$v;},l))),-1073741824)+1073741824>>0;if(!((m===0))&&!((B.AddInt32((l.$ptr_readerWait||(l.$ptr_readerWait=new BI(function(){return this.$target.readerWait;},function($v){this.$target.readerWait=$v;},l))),m)===0))){$s=2;continue;}$s=3;continue;case 2:$r=K((l.$ptr_writerSem||(l.$ptr_writerSem=new BA(function(){return this.$target.writerSem;},function($v){this.$target.writerSem=$v;},l))),false);$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 3:if(false){C.Enable();C.Acquire(((l.$ptr_readerSem||(l.$ptr_readerSem=new BA(function(){return this.$target.readerSem;},function($v){this.$target.readerSem=$v;},l)))));C.Acquire(((l.$ptr_writerSem||(l.$ptr_writerSem=new BA(function(){return this.$target.writerSem;},function($v){this.$target.writerSem=$v;},l)))));}$s=-1;return;}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.Lock};}$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.Lock=function(){return this.$val.Lock();};AV.ptr.prototype.Unlock=function(){var l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(false){$unused(l.w.state);C.Release(((l.$ptr_readerSem||(l.$ptr_readerSem=new BA(function(){return this.$target.readerSem;},function($v){this.$target.readerSem=$v;},l)))));C.Disable();}m=B.AddInt32((l.$ptr_readerCount||(l.$ptr_readerCount=new BI(function(){return this.$target.readerCount;},function($v){this.$target.readerCount=$v;},l))),1073741824);if(m>=1073741824){C.Enable();P("sync: Unlock of unlocked RWMutex");}n=0;case 1:if(!(n<((m>>0)))){$s=2;continue;}$r=L((l.$ptr_readerSem||(l.$ptr_readerSem=new BA(function(){return this.$target.readerSem;},function($v){this.$target.readerSem=$v;},l))),false);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}n=n+(1)>>0;$s=1;continue;case 2:$r=l.w.Unlock();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(false){C.Enable();}$s=-1;return;}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.Unlock};}$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.Unlock=function(){return this.$val.Unlock();};AV.ptr.prototype.RLocker=function(){var l;l=this;return($pointerOfStructConversion(l,BN));};AV.prototype.RLocker=function(){return this.$val.RLocker();};AW.ptr.prototype.Lock=function(){var l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;$r=($pointerOfStructConversion(l,BO)).RLock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AW.ptr.prototype.Lock};}$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};AW.prototype.Lock=function(){return this.$val.Lock();};AW.ptr.prototype.Unlock=function(){var l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;$r=($pointerOfStructConversion(l,BO)).RUnlock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AW.ptr.prototype.Unlock};}$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};AW.prototype.Unlock=function(){return this.$val.Unlock();};AY.methods=[{prop:"Get",name:"Get",pkg:"",typ:$funcType([],[$emptyInterface],false)},{prop:"Put",name:"Put",pkg:"",typ:$funcType([$emptyInterface],[],false)},{prop:"getSlow",name:"getSlow",pkg:"sync",typ:$funcType([],[$emptyInterface],false)},{prop:"pin",name:"pin",pkg:"sync",typ:$funcType([],[BL],false)},{prop:"pinSlow",name:"pinSlow",pkg:"sync",typ:$funcType([],[BL],false)}];CA.methods=[{prop:"Lock",name:"Lock",pkg:"",typ:$funcType([],[],false)},{prop:"Unlock",name:"Unlock",pkg:"",typ:$funcType([],[],false)}];CC.methods=[{prop:"Do",name:"Do",pkg:"",typ:$funcType([CB],[],false)}];BO.methods=[{prop:"RLock",name:"RLock",pkg:"",typ:$funcType([],[],false)},{prop:"RUnlock",name:"RUnlock",pkg:"",typ:$funcType([],[],false)},{prop:"Lock",name:"Lock",pkg:"",typ:$funcType([],[],false)},{prop:"Unlock",name:"Unlock",pkg:"",typ:$funcType([],[],false)},{prop:"RLocker",name:"RLocker",pkg:"",typ:$funcType([],[AA],false)}];BN.methods=[{prop:"Lock",name:"Lock",pkg:"",typ:$funcType([],[],false)},{prop:"Unlock",name:"Unlock",pkg:"",typ:$funcType([],[],false)}];F.init("sync",[{prop:"local",name:"local",embedded:false,exported:false,typ:$UnsafePointer,tag:""},{prop:"localSize",name:"localSize",embedded:false,exported:false,typ:$Uintptr,tag:""},{prop:"store",name:"store",embedded:false,exported:false,typ:BM,tag:""},{prop:"New",name:"New",embedded:false,exported:true,typ:BR,tag:""}]);Z.init("sync",[{prop:"state",name:"state",embedded:false,exported:false,typ:$Int32,tag:""},{prop:"sema",name:"sema",embedded:false,exported:false,typ:$Uint32,tag:""}]);AA.init([{prop:"Lock",name:"Lock",pkg:"",typ:$funcType([],[],false)},{prop:"Unlock",name:"Unlock",pkg:"",typ:$funcType([],[],false)}]);AB.init("sync",[{prop:"m",name:"m",embedded:false,exported:false,typ:Z,tag:""},{prop:"done",name:"done",embedded:false,exported:false,typ:$Uint32,tag:""}]);AC.init("sync",[{prop:"private$0",name:"private",embedded:false,exported:false,typ:$emptyInterface,tag:""},{prop:"shared",name:"shared",embedded:false,exported:false,typ:BM,tag:""},{prop:"Mutex",name:"Mutex",embedded:true,exported:true,typ:Z,tag:""}]);AD.init("sync",[{prop:"poolLocalInternal",name:"poolLocalInternal",embedded:true,exported:false,typ:AC,tag:""},{prop:"pad",name:"pad",embedded:false,exported:false,typ:CD,tag:""}]);AO.init("sync",[{prop:"wait",name:"wait",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"notify",name:"notify",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"lock",name:"lock",embedded:false,exported:false,typ:$Uintptr,tag:""},{prop:"head",name:"head",embedded:false,exported:false,typ:$UnsafePointer,tag:""},{prop:"tail",name:"tail",embedded:false,exported:false,typ:$UnsafePointer,tag:""}]);AV.init("sync",[{prop:"w",name:"w",embedded:false,exported:false,typ:Z,tag:""},{prop:"writerSem",name:"writerSem",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"readerSem",name:"readerSem",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"readerCount",name:"readerCount",embedded:false,exported:false,typ:$Int32,tag:""},{prop:"readerWait",name:"readerWait",embedded:false,exported:false,typ:$Int32,tag:""}]);AW.init("sync",[{prop:"w",name:"w",embedded:false,exported:false,typ:Z,tag:""},{prop:"writerSem",name:"writerSem",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"readerSem",name:"readerSem",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"readerCount",name:"readerCount",embedded:false,exported:false,typ:$Int32,tag:""},{prop:"readerWait",name:"readerWait",embedded:false,exported:false,typ:$Int32,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}AJ=AZ.nil;H={};I={};W=(new Uint8Array(8));AK();AT();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["unicode"]=(function(){var $pkg={},$init,O,P,Q,R,T,AF,IX,IY,IZ,JA,JB,JC,JD,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,BM,BN,BO,BP,BQ,BR,BS,BT,BU,BV,BW,BX,BY,BZ,CA,CB,CC,CD,CE,CF,CG,CH,CI,CJ,CK,CL,CM,CN,CO,CP,CQ,CR,CS,CT,CU,CV,CW,CX,CY,CZ,DA,DB,DC,DD,DE,DF,DG,DH,DI,DJ,DK,DL,DM,DN,DO,DP,DQ,DR,DS,DT,DU,DV,DW,DX,DY,DZ,EA,EB,EC,ED,EE,EF,EG,EH,EI,EJ,EK,EL,EM,EN,EO,EP,EQ,ER,ES,ET,EU,EV,EW,EX,EY,EZ,FA,FB,FC,FD,FE,FF,FG,FH,FI,FJ,FK,FL,FM,FN,FO,FP,FQ,FR,FS,FT,FU,FV,FW,FX,FY,FZ,GA,GB,GC,GD,GE,GF,GG,GH,GI,GJ,GK,GL,GM,GN,GO,GP,GQ,GR,GS,GT,GU,GV,GW,GX,GY,GZ,HA,HB,IK,IL,IM,IN,IO,IP,IQ,IR,IS,IT,IU,IV,IW,A,C,E,G,I,U,V,W,X,AB,AC,AD,AG;O=$pkg.RangeTable=$newType(0,$kindStruct,"unicode.RangeTable",true,"unicode",true,function(R16_,R32_,LatinOffset_){this.$val=this;if(arguments.length===0){this.R16=IY.nil;this.R32=IZ.nil;this.LatinOffset=0;return;}this.R16=R16_;this.R32=R32_;this.LatinOffset=LatinOffset_;});P=$pkg.Range16=$newType(0,$kindStruct,"unicode.Range16",true,"unicode",true,function(Lo_,Hi_,Stride_){this.$val=this;if(arguments.length===0){this.Lo=0;this.Hi=0;this.Stride=0;return;}this.Lo=Lo_;this.Hi=Hi_;this.Stride=Stride_;});Q=$pkg.Range32=$newType(0,$kindStruct,"unicode.Range32",true,"unicode",true,function(Lo_,Hi_,Stride_){this.$val=this;if(arguments.length===0){this.Lo=0;this.Hi=0;this.Stride=0;return;}this.Lo=Lo_;this.Hi=Hi_;this.Stride=Stride_;});R=$pkg.CaseRange=$newType(0,$kindStruct,"unicode.CaseRange",true,"unicode",true,function(Lo_,Hi_,Delta_){this.$val=this;if(arguments.length===0){this.Lo=0;this.Hi=0;this.Delta=IX.zero();return;}this.Lo=Lo_;this.Hi=Hi_;this.Delta=Delta_;});T=$pkg.d=$newType(12,$kindArray,"unicode.d",true,"unicode",false,null);AF=$pkg.foldPair=$newType(0,$kindStruct,"unicode.foldPair",true,"unicode",false,function(From_,To_){this.$val=this;if(arguments.length===0){this.From=0;this.To=0;return;}this.From=From_;this.To=To_;});IX=$arrayType($Int32,3);IY=$sliceType(P);IZ=$sliceType(Q);JA=$ptrType(O);JB=$sliceType(JA);JC=$sliceType(R);JD=$sliceType(AF);A=function(b,c,d){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;e=0;f=false;if(b<0||3<=b){g=65533;h=false;e=g;f=h;return[e,f];}i=0;j=d.$length;while(true){if(!(i>0))/2,(k===k&&k!==1/0&&k!==-1/0)?k>>0:$throwRuntimeError("integer divide by zero"))>>0;m=((l<0||l>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+l]);if(((m.Lo>>0))<=c&&c<=((m.Hi>>0))){o=((n=m.Delta,((b<0||b>=n.length)?($throwRuntimeError("index out of range"),undefined):n[b])));if(o>1114111){p=((m.Lo>>0))+((((((c-((m.Lo>>0))>>0))&~1)>>0)|(((b&1)>>0))))>>0;q=true;e=p;f=q;return[e,f];}r=c+o>>0;s=true;e=r;f=s;return[e,f];}if(c<((m.Lo>>0))){j=l;}else{i=l+1>>0;}}t=c;u=false;e=t;f=u;return[e,f];};C=function(b){var b;if(b<=255){return 48<=b&&b<=57;}return X($pkg.Digit,b);};$pkg.IsDigit=C;E=function(b){var b,c;if(((b>>>0))<=255){return!(((((c=((b<<24>>>24)),((c<0||c>=IL.length)?($throwRuntimeError("index out of range"),undefined):IL[c]))&128)>>>0)===0));}return G(b,$pkg.PrintRanges);};$pkg.IsPrint=E;G=function(b,c){var b,c,d,e,f;d=c;e=0;while(true){if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);if(W(f,b)){return true;}e++;}return false;};$pkg.In=G;I=function(b){var b,c;if(((b>>>0))<=255){return!(((((c=((b<<24>>>24)),((c<0||c>=IL.length)?($throwRuntimeError("index out of range"),undefined):IL[c]))&96)>>>0)===0));}return X($pkg.Letter,b);};$pkg.IsLetter=I;U=function(b,c){var b,c,d,e,f,g,h,i,j,k,l,m,n;if(b.$length<=18||c<=255){d=b;e=0;while(true){if(!(e=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+f]);if(c>>16))%g.Stride,h===h?h:$throwRuntimeError("integer divide by zero"))===0);}e++;}return false;}i=0;j=b.$length;while(true){if(!(i>0))/2,(k===k&&k!==1/0&&k!==-1/0)?k>>0:$throwRuntimeError("integer divide by zero"))>>0;m=((l<0||l>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+l]);if(m.Lo<=c&&c<=m.Hi){return(m.Stride===1)||((n=((c-m.Lo<<16>>>16))%m.Stride,n===n?n:$throwRuntimeError("integer divide by zero"))===0);}if(c>0;}}return false;};V=function(b,c){var b,c,d,e,f,g,h,i,j,k,l,m,n;if(b.$length<=18){d=b;e=0;while(true){if(!(e=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+f]);if(c>>0))%g.Stride,h===h?h:$throwRuntimeError("integer divide by zero"))===0);}e++;}return false;}i=0;j=b.$length;while(true){if(!(i>0))/2,(k===k&&k!==1/0&&k!==-1/0)?k>>0:$throwRuntimeError("integer divide by zero"))>>0;m=$clone(((l<0||l>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+l]),Q);if(m.Lo<=c&&c<=m.Hi){return(m.Stride===1)||((n=((c-m.Lo>>>0))%m.Stride,n===n?n:$throwRuntimeError("integer divide by zero"))===0);}if(c>0;}}return false;};W=function(b,c){var b,c,d,e,f;d=b.R16;if(d.$length>0&&c<=(((e=d.$length-1>>0,((e<0||e>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e])).Hi>>0))){return U(d,((c<<16>>>16)));}f=b.R32;if(f.$length>0&&c>=(((0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0]).Lo>>0))){return V(f,((c>>>0)));}return false;};$pkg.Is=W;X=function(b,c){var b,c,d,e,f,g;d=b.R16;e=b.LatinOffset;if(d.$length>e&&c<=(((f=d.$length-1>>0,((f<0||f>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+f])).Hi>>0))){return U($subslice(d,e),((c<<16>>>16)));}g=b.R32;if(g.$length>0&&c>=(((0>=g.$length?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+0]).Lo>>0))){return V(g,((c>>>0)));}return false;};AB=function(b,c){var b,c,d;d=A(b,c,$pkg.CaseRanges);c=d[0];return c;};$pkg.To=AB;AC=function(b){var b;if(b<=127){if(97<=b&&b<=122){b=b-(32)>>0;}return b;}return AB(0,b);};$pkg.ToUpper=AC;AD=function(b){var b;if(b<=127){if(65<=b&&b<=90){b=b+(32)>>0;}return b;}return AB(1,b);};$pkg.ToLower=AD;AG=function(b){var b,c,d,e,f,g;if(b<0||b>1114111){return b;}if(((b>>0))<128){return((((b<0||b>=IM.length)?($throwRuntimeError("index out of range"),undefined):IM[b])>>0));}c=0;d=IN.$length;while(true){if(!(c>0))/2,(e===e&&e!==1/0&&e!==-1/0)?e>>0:$throwRuntimeError("integer divide by zero"))>>0;if(((((f<0||f>=IN.$length)?($throwRuntimeError("index out of range"),undefined):IN.$array[IN.$offset+f]).From>>0))>0;}else{d=f;}}if(c=IN.$length)?($throwRuntimeError("index out of range"),undefined):IN.$array[IN.$offset+c]).From>>0))===b)){return((((c<0||c>=IN.$length)?($throwRuntimeError("index out of range"),undefined):IN.$array[IN.$offset+c]).To>>0));}g=AD(b);if(!((g===b))){return g;}return AC(b);};$pkg.SimpleFold=AG;O.init("",[{prop:"R16",name:"R16",embedded:false,exported:true,typ:IY,tag:""},{prop:"R32",name:"R32",embedded:false,exported:true,typ:IZ,tag:""},{prop:"LatinOffset",name:"LatinOffset",embedded:false,exported:true,typ:$Int,tag:""}]);P.init("",[{prop:"Lo",name:"Lo",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Hi",name:"Hi",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Stride",name:"Stride",embedded:false,exported:true,typ:$Uint16,tag:""}]);Q.init("",[{prop:"Lo",name:"Lo",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Hi",name:"Hi",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Stride",name:"Stride",embedded:false,exported:true,typ:$Uint32,tag:""}]);R.init("",[{prop:"Lo",name:"Lo",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Hi",name:"Hi",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Delta",name:"Delta",embedded:false,exported:true,typ:T,tag:""}]);T.init($Int32,3);AF.init("",[{prop:"From",name:"From",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"To",name:"To",embedded:false,exported:true,typ:$Uint16,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:AH=new O.ptr(new IY([new P.ptr(0,31,1),new P.ptr(127,159,1),new P.ptr(173,1536,1363),new P.ptr(1537,1541,1),new P.ptr(1564,1757,193),new P.ptr(1807,2274,467),new P.ptr(6158,8203,2045),new P.ptr(8204,8207,1),new P.ptr(8234,8238,1),new P.ptr(8288,8292,1),new P.ptr(8294,8303,1),new P.ptr(55296,63743,1),new P.ptr(65279,65529,250),new P.ptr(65530,65531,1)]),new IZ([new Q.ptr(69821,113824,44003),new Q.ptr(113825,113827,1),new Q.ptr(119155,119162,1),new Q.ptr(917505,917536,31),new Q.ptr(917537,917631,1),new Q.ptr(983040,1048573,1),new Q.ptr(1048576,1114109,1)]),2);AI=new O.ptr(new IY([new P.ptr(0,31,1),new P.ptr(127,159,1)]),IZ.nil,2);AJ=new O.ptr(new IY([new P.ptr(173,1536,1363),new P.ptr(1537,1541,1),new P.ptr(1564,1757,193),new P.ptr(1807,2274,467),new P.ptr(6158,8203,2045),new P.ptr(8204,8207,1),new P.ptr(8234,8238,1),new P.ptr(8288,8292,1),new P.ptr(8294,8303,1),new P.ptr(65279,65529,250),new P.ptr(65530,65531,1)]),new IZ([new Q.ptr(69821,113824,44003),new Q.ptr(113825,113827,1),new Q.ptr(119155,119162,1),new Q.ptr(917505,917536,31),new Q.ptr(917537,917631,1)]),0);AK=new O.ptr(new IY([new P.ptr(57344,63743,1)]),new IZ([new Q.ptr(983040,1048573,1),new Q.ptr(1048576,1114109,1)]),0);AL=new O.ptr(new IY([new P.ptr(55296,57343,1)]),IZ.nil,0);AM=new O.ptr(new IY([new P.ptr(65,90,1),new P.ptr(97,122,1),new P.ptr(170,181,11),new P.ptr(186,192,6),new P.ptr(193,214,1),new P.ptr(216,246,1),new P.ptr(248,705,1),new P.ptr(710,721,1),new P.ptr(736,740,1),new P.ptr(748,750,2),new P.ptr(880,884,1),new P.ptr(886,887,1),new P.ptr(890,893,1),new P.ptr(895,902,7),new P.ptr(904,906,1),new P.ptr(908,910,2),new P.ptr(911,929,1),new P.ptr(931,1013,1),new P.ptr(1015,1153,1),new P.ptr(1162,1327,1),new P.ptr(1329,1366,1),new P.ptr(1369,1377,8),new P.ptr(1378,1415,1),new P.ptr(1488,1514,1),new P.ptr(1520,1522,1),new P.ptr(1568,1610,1),new P.ptr(1646,1647,1),new P.ptr(1649,1747,1),new P.ptr(1749,1765,16),new P.ptr(1766,1774,8),new P.ptr(1775,1786,11),new P.ptr(1787,1788,1),new P.ptr(1791,1808,17),new P.ptr(1810,1839,1),new P.ptr(1869,1957,1),new P.ptr(1969,1994,25),new P.ptr(1995,2026,1),new P.ptr(2036,2037,1),new P.ptr(2042,2048,6),new P.ptr(2049,2069,1),new P.ptr(2074,2084,10),new P.ptr(2088,2112,24),new P.ptr(2113,2136,1),new P.ptr(2144,2154,1),new P.ptr(2208,2228,1),new P.ptr(2230,2237,1),new P.ptr(2308,2361,1),new P.ptr(2365,2384,19),new P.ptr(2392,2401,1),new P.ptr(2417,2432,1),new P.ptr(2437,2444,1),new P.ptr(2447,2448,1),new P.ptr(2451,2472,1),new P.ptr(2474,2480,1),new P.ptr(2482,2486,4),new P.ptr(2487,2489,1),new P.ptr(2493,2510,17),new P.ptr(2524,2525,1),new P.ptr(2527,2529,1),new P.ptr(2544,2545,1),new P.ptr(2556,2565,9),new P.ptr(2566,2570,1),new P.ptr(2575,2576,1),new P.ptr(2579,2600,1),new P.ptr(2602,2608,1),new P.ptr(2610,2611,1),new P.ptr(2613,2614,1),new P.ptr(2616,2617,1),new P.ptr(2649,2652,1),new P.ptr(2654,2674,20),new P.ptr(2675,2676,1),new P.ptr(2693,2701,1),new P.ptr(2703,2705,1),new P.ptr(2707,2728,1),new P.ptr(2730,2736,1),new P.ptr(2738,2739,1),new P.ptr(2741,2745,1),new P.ptr(2749,2768,19),new P.ptr(2784,2785,1),new P.ptr(2809,2821,12),new P.ptr(2822,2828,1),new P.ptr(2831,2832,1),new P.ptr(2835,2856,1),new P.ptr(2858,2864,1),new P.ptr(2866,2867,1),new P.ptr(2869,2873,1),new P.ptr(2877,2908,31),new P.ptr(2909,2911,2),new P.ptr(2912,2913,1),new P.ptr(2929,2947,18),new P.ptr(2949,2954,1),new P.ptr(2958,2960,1),new P.ptr(2962,2965,1),new P.ptr(2969,2970,1),new P.ptr(2972,2974,2),new P.ptr(2975,2979,4),new P.ptr(2980,2984,4),new P.ptr(2985,2986,1),new P.ptr(2990,3001,1),new P.ptr(3024,3077,53),new P.ptr(3078,3084,1),new P.ptr(3086,3088,1),new P.ptr(3090,3112,1),new P.ptr(3114,3129,1),new P.ptr(3133,3160,27),new P.ptr(3161,3162,1),new P.ptr(3168,3169,1),new P.ptr(3200,3205,5),new P.ptr(3206,3212,1),new P.ptr(3214,3216,1),new P.ptr(3218,3240,1),new P.ptr(3242,3251,1),new P.ptr(3253,3257,1),new P.ptr(3261,3294,33),new P.ptr(3296,3297,1),new P.ptr(3313,3314,1),new P.ptr(3333,3340,1),new P.ptr(3342,3344,1),new P.ptr(3346,3386,1),new P.ptr(3389,3406,17),new P.ptr(3412,3414,1),new P.ptr(3423,3425,1),new P.ptr(3450,3455,1),new P.ptr(3461,3478,1),new P.ptr(3482,3505,1),new P.ptr(3507,3515,1),new P.ptr(3517,3520,3),new P.ptr(3521,3526,1),new P.ptr(3585,3632,1),new P.ptr(3634,3635,1),new P.ptr(3648,3654,1),new P.ptr(3713,3714,1),new P.ptr(3716,3719,3),new P.ptr(3720,3722,2),new P.ptr(3725,3732,7),new P.ptr(3733,3735,1),new P.ptr(3737,3743,1),new P.ptr(3745,3747,1),new P.ptr(3749,3751,2),new P.ptr(3754,3755,1),new P.ptr(3757,3760,1),new P.ptr(3762,3763,1),new P.ptr(3773,3776,3),new P.ptr(3777,3780,1),new P.ptr(3782,3804,22),new P.ptr(3805,3807,1),new P.ptr(3840,3904,64),new P.ptr(3905,3911,1),new P.ptr(3913,3948,1),new P.ptr(3976,3980,1),new P.ptr(4096,4138,1),new P.ptr(4159,4176,17),new P.ptr(4177,4181,1),new P.ptr(4186,4189,1),new P.ptr(4193,4197,4),new P.ptr(4198,4206,8),new P.ptr(4207,4208,1),new P.ptr(4213,4225,1),new P.ptr(4238,4256,18),new P.ptr(4257,4293,1),new P.ptr(4295,4301,6),new P.ptr(4304,4346,1),new P.ptr(4348,4680,1),new P.ptr(4682,4685,1),new P.ptr(4688,4694,1),new P.ptr(4696,4698,2),new P.ptr(4699,4701,1),new P.ptr(4704,4744,1),new P.ptr(4746,4749,1),new P.ptr(4752,4784,1),new P.ptr(4786,4789,1),new P.ptr(4792,4798,1),new P.ptr(4800,4802,2),new P.ptr(4803,4805,1),new P.ptr(4808,4822,1),new P.ptr(4824,4880,1),new P.ptr(4882,4885,1),new P.ptr(4888,4954,1),new P.ptr(4992,5007,1),new P.ptr(5024,5109,1),new P.ptr(5112,5117,1),new P.ptr(5121,5740,1),new P.ptr(5743,5759,1),new P.ptr(5761,5786,1),new P.ptr(5792,5866,1),new P.ptr(5873,5880,1),new P.ptr(5888,5900,1),new P.ptr(5902,5905,1),new P.ptr(5920,5937,1),new P.ptr(5952,5969,1),new P.ptr(5984,5996,1),new P.ptr(5998,6000,1),new P.ptr(6016,6067,1),new P.ptr(6103,6108,5),new P.ptr(6176,6263,1),new P.ptr(6272,6276,1),new P.ptr(6279,6312,1),new P.ptr(6314,6320,6),new P.ptr(6321,6389,1),new P.ptr(6400,6430,1),new P.ptr(6480,6509,1),new P.ptr(6512,6516,1),new P.ptr(6528,6571,1),new P.ptr(6576,6601,1),new P.ptr(6656,6678,1),new P.ptr(6688,6740,1),new P.ptr(6823,6917,94),new P.ptr(6918,6963,1),new P.ptr(6981,6987,1),new P.ptr(7043,7072,1),new P.ptr(7086,7087,1),new P.ptr(7098,7141,1),new P.ptr(7168,7203,1),new P.ptr(7245,7247,1),new P.ptr(7258,7293,1),new P.ptr(7296,7304,1),new P.ptr(7401,7404,1),new P.ptr(7406,7409,1),new P.ptr(7413,7414,1),new P.ptr(7424,7615,1),new P.ptr(7680,7957,1),new P.ptr(7960,7965,1),new P.ptr(7968,8005,1),new P.ptr(8008,8013,1),new P.ptr(8016,8023,1),new P.ptr(8025,8031,2),new P.ptr(8032,8061,1),new P.ptr(8064,8116,1),new P.ptr(8118,8124,1),new P.ptr(8126,8130,4),new P.ptr(8131,8132,1),new P.ptr(8134,8140,1),new P.ptr(8144,8147,1),new P.ptr(8150,8155,1),new P.ptr(8160,8172,1),new P.ptr(8178,8180,1),new P.ptr(8182,8188,1),new P.ptr(8305,8319,14),new P.ptr(8336,8348,1),new P.ptr(8450,8455,5),new P.ptr(8458,8467,1),new P.ptr(8469,8473,4),new P.ptr(8474,8477,1),new P.ptr(8484,8490,2),new P.ptr(8491,8493,1),new P.ptr(8495,8505,1),new P.ptr(8508,8511,1),new P.ptr(8517,8521,1),new P.ptr(8526,8579,53),new P.ptr(8580,11264,2684),new P.ptr(11265,11310,1),new P.ptr(11312,11358,1),new P.ptr(11360,11492,1),new P.ptr(11499,11502,1),new P.ptr(11506,11507,1),new P.ptr(11520,11557,1),new P.ptr(11559,11565,6),new P.ptr(11568,11623,1),new P.ptr(11631,11648,17),new P.ptr(11649,11670,1),new P.ptr(11680,11686,1),new P.ptr(11688,11694,1),new P.ptr(11696,11702,1),new P.ptr(11704,11710,1),new P.ptr(11712,11718,1),new P.ptr(11720,11726,1),new P.ptr(11728,11734,1),new P.ptr(11736,11742,1),new P.ptr(11823,12293,470),new P.ptr(12294,12337,43),new P.ptr(12338,12341,1),new P.ptr(12347,12348,1),new P.ptr(12353,12438,1),new P.ptr(12445,12447,1),new P.ptr(12449,12538,1),new P.ptr(12540,12543,1),new P.ptr(12549,12590,1),new P.ptr(12593,12686,1),new P.ptr(12704,12730,1),new P.ptr(12784,12799,1),new P.ptr(13312,19893,1),new P.ptr(19968,40938,1),new P.ptr(40960,42124,1),new P.ptr(42192,42237,1),new P.ptr(42240,42508,1),new P.ptr(42512,42527,1),new P.ptr(42538,42539,1),new P.ptr(42560,42606,1),new P.ptr(42623,42653,1),new P.ptr(42656,42725,1),new P.ptr(42775,42783,1),new P.ptr(42786,42888,1),new P.ptr(42891,42926,1),new P.ptr(42928,42935,1),new P.ptr(42999,43009,1),new P.ptr(43011,43013,1),new P.ptr(43015,43018,1),new P.ptr(43020,43042,1),new P.ptr(43072,43123,1),new P.ptr(43138,43187,1),new P.ptr(43250,43255,1),new P.ptr(43259,43261,2),new P.ptr(43274,43301,1),new P.ptr(43312,43334,1),new P.ptr(43360,43388,1),new P.ptr(43396,43442,1),new P.ptr(43471,43488,17),new P.ptr(43489,43492,1),new P.ptr(43494,43503,1),new P.ptr(43514,43518,1),new P.ptr(43520,43560,1),new P.ptr(43584,43586,1),new P.ptr(43588,43595,1),new P.ptr(43616,43638,1),new P.ptr(43642,43646,4),new P.ptr(43647,43695,1),new P.ptr(43697,43701,4),new P.ptr(43702,43705,3),new P.ptr(43706,43709,1),new P.ptr(43712,43714,2),new P.ptr(43739,43741,1),new P.ptr(43744,43754,1),new P.ptr(43762,43764,1),new P.ptr(43777,43782,1),new P.ptr(43785,43790,1),new P.ptr(43793,43798,1),new P.ptr(43808,43814,1),new P.ptr(43816,43822,1),new P.ptr(43824,43866,1),new P.ptr(43868,43877,1),new P.ptr(43888,44002,1),new P.ptr(44032,55203,1),new P.ptr(55216,55238,1),new P.ptr(55243,55291,1),new P.ptr(63744,64109,1),new P.ptr(64112,64217,1),new P.ptr(64256,64262,1),new P.ptr(64275,64279,1),new P.ptr(64285,64287,2),new P.ptr(64288,64296,1),new P.ptr(64298,64310,1),new P.ptr(64312,64316,1),new P.ptr(64318,64320,2),new P.ptr(64321,64323,2),new P.ptr(64324,64326,2),new P.ptr(64327,64433,1),new P.ptr(64467,64829,1),new P.ptr(64848,64911,1),new P.ptr(64914,64967,1),new P.ptr(65008,65019,1),new P.ptr(65136,65140,1),new P.ptr(65142,65276,1),new P.ptr(65313,65338,1),new P.ptr(65345,65370,1),new P.ptr(65382,65470,1),new P.ptr(65474,65479,1),new P.ptr(65482,65487,1),new P.ptr(65490,65495,1),new P.ptr(65498,65500,1)]),new IZ([new Q.ptr(65536,65547,1),new Q.ptr(65549,65574,1),new Q.ptr(65576,65594,1),new Q.ptr(65596,65597,1),new Q.ptr(65599,65613,1),new Q.ptr(65616,65629,1),new Q.ptr(65664,65786,1),new Q.ptr(66176,66204,1),new Q.ptr(66208,66256,1),new Q.ptr(66304,66335,1),new Q.ptr(66349,66368,1),new Q.ptr(66370,66377,1),new Q.ptr(66384,66421,1),new Q.ptr(66432,66461,1),new Q.ptr(66464,66499,1),new Q.ptr(66504,66511,1),new Q.ptr(66560,66717,1),new Q.ptr(66736,66771,1),new Q.ptr(66776,66811,1),new Q.ptr(66816,66855,1),new Q.ptr(66864,66915,1),new Q.ptr(67072,67382,1),new Q.ptr(67392,67413,1),new Q.ptr(67424,67431,1),new Q.ptr(67584,67589,1),new Q.ptr(67592,67594,2),new Q.ptr(67595,67637,1),new Q.ptr(67639,67640,1),new Q.ptr(67644,67647,3),new Q.ptr(67648,67669,1),new Q.ptr(67680,67702,1),new Q.ptr(67712,67742,1),new Q.ptr(67808,67826,1),new Q.ptr(67828,67829,1),new Q.ptr(67840,67861,1),new Q.ptr(67872,67897,1),new Q.ptr(67968,68023,1),new Q.ptr(68030,68031,1),new Q.ptr(68096,68112,16),new Q.ptr(68113,68115,1),new Q.ptr(68117,68119,1),new Q.ptr(68121,68147,1),new Q.ptr(68192,68220,1),new Q.ptr(68224,68252,1),new Q.ptr(68288,68295,1),new Q.ptr(68297,68324,1),new Q.ptr(68352,68405,1),new Q.ptr(68416,68437,1),new Q.ptr(68448,68466,1),new Q.ptr(68480,68497,1),new Q.ptr(68608,68680,1),new Q.ptr(68736,68786,1),new Q.ptr(68800,68850,1),new Q.ptr(69635,69687,1),new Q.ptr(69763,69807,1),new Q.ptr(69840,69864,1),new Q.ptr(69891,69926,1),new Q.ptr(69968,70002,1),new Q.ptr(70006,70019,13),new Q.ptr(70020,70066,1),new Q.ptr(70081,70084,1),new Q.ptr(70106,70108,2),new Q.ptr(70144,70161,1),new Q.ptr(70163,70187,1),new Q.ptr(70272,70278,1),new Q.ptr(70280,70282,2),new Q.ptr(70283,70285,1),new Q.ptr(70287,70301,1),new Q.ptr(70303,70312,1),new Q.ptr(70320,70366,1),new Q.ptr(70405,70412,1),new Q.ptr(70415,70416,1),new Q.ptr(70419,70440,1),new Q.ptr(70442,70448,1),new Q.ptr(70450,70451,1),new Q.ptr(70453,70457,1),new Q.ptr(70461,70480,19),new Q.ptr(70493,70497,1),new Q.ptr(70656,70708,1),new Q.ptr(70727,70730,1),new Q.ptr(70784,70831,1),new Q.ptr(70852,70853,1),new Q.ptr(70855,71040,185),new Q.ptr(71041,71086,1),new Q.ptr(71128,71131,1),new Q.ptr(71168,71215,1),new Q.ptr(71236,71296,60),new Q.ptr(71297,71338,1),new Q.ptr(71424,71449,1),new Q.ptr(71840,71903,1),new Q.ptr(71935,72192,257),new Q.ptr(72203,72242,1),new Q.ptr(72250,72272,22),new Q.ptr(72284,72323,1),new Q.ptr(72326,72329,1),new Q.ptr(72384,72440,1),new Q.ptr(72704,72712,1),new Q.ptr(72714,72750,1),new Q.ptr(72768,72818,50),new Q.ptr(72819,72847,1),new Q.ptr(72960,72966,1),new Q.ptr(72968,72969,1),new Q.ptr(72971,73008,1),new Q.ptr(73030,73728,698),new Q.ptr(73729,74649,1),new Q.ptr(74880,75075,1),new Q.ptr(77824,78894,1),new Q.ptr(82944,83526,1),new Q.ptr(92160,92728,1),new Q.ptr(92736,92766,1),new Q.ptr(92880,92909,1),new Q.ptr(92928,92975,1),new Q.ptr(92992,92995,1),new Q.ptr(93027,93047,1),new Q.ptr(93053,93071,1),new Q.ptr(93952,94020,1),new Q.ptr(94032,94099,67),new Q.ptr(94100,94111,1),new Q.ptr(94176,94177,1),new Q.ptr(94208,100332,1),new Q.ptr(100352,101106,1),new Q.ptr(110592,110878,1),new Q.ptr(110960,111355,1),new Q.ptr(113664,113770,1),new Q.ptr(113776,113788,1),new Q.ptr(113792,113800,1),new Q.ptr(113808,113817,1),new Q.ptr(119808,119892,1),new Q.ptr(119894,119964,1),new Q.ptr(119966,119967,1),new Q.ptr(119970,119973,3),new Q.ptr(119974,119977,3),new Q.ptr(119978,119980,1),new Q.ptr(119982,119993,1),new Q.ptr(119995,119997,2),new Q.ptr(119998,120003,1),new Q.ptr(120005,120069,1),new Q.ptr(120071,120074,1),new Q.ptr(120077,120084,1),new Q.ptr(120086,120092,1),new Q.ptr(120094,120121,1),new Q.ptr(120123,120126,1),new Q.ptr(120128,120132,1),new Q.ptr(120134,120138,4),new Q.ptr(120139,120144,1),new Q.ptr(120146,120485,1),new Q.ptr(120488,120512,1),new Q.ptr(120514,120538,1),new Q.ptr(120540,120570,1),new Q.ptr(120572,120596,1),new Q.ptr(120598,120628,1),new Q.ptr(120630,120654,1),new Q.ptr(120656,120686,1),new Q.ptr(120688,120712,1),new Q.ptr(120714,120744,1),new Q.ptr(120746,120770,1),new Q.ptr(120772,120779,1),new Q.ptr(124928,125124,1),new Q.ptr(125184,125251,1),new Q.ptr(126464,126467,1),new Q.ptr(126469,126495,1),new Q.ptr(126497,126498,1),new Q.ptr(126500,126503,3),new Q.ptr(126505,126514,1),new Q.ptr(126516,126519,1),new Q.ptr(126521,126523,2),new Q.ptr(126530,126535,5),new Q.ptr(126537,126541,2),new Q.ptr(126542,126543,1),new Q.ptr(126545,126546,1),new Q.ptr(126548,126551,3),new Q.ptr(126553,126561,2),new Q.ptr(126562,126564,2),new Q.ptr(126567,126570,1),new Q.ptr(126572,126578,1),new Q.ptr(126580,126583,1),new Q.ptr(126585,126588,1),new Q.ptr(126590,126592,2),new Q.ptr(126593,126601,1),new Q.ptr(126603,126619,1),new Q.ptr(126625,126627,1),new Q.ptr(126629,126633,1),new Q.ptr(126635,126651,1),new Q.ptr(131072,173782,1),new Q.ptr(173824,177972,1),new Q.ptr(177984,178205,1),new Q.ptr(178208,183969,1),new Q.ptr(183984,191456,1),new Q.ptr(194560,195101,1)]),6);AN=new O.ptr(new IY([new P.ptr(97,122,1),new P.ptr(181,223,42),new P.ptr(224,246,1),new P.ptr(248,255,1),new P.ptr(257,311,2),new P.ptr(312,328,2),new P.ptr(329,375,2),new P.ptr(378,382,2),new P.ptr(383,384,1),new P.ptr(387,389,2),new P.ptr(392,396,4),new P.ptr(397,402,5),new P.ptr(405,409,4),new P.ptr(410,411,1),new P.ptr(414,417,3),new P.ptr(419,421,2),new P.ptr(424,426,2),new P.ptr(427,429,2),new P.ptr(432,436,4),new P.ptr(438,441,3),new P.ptr(442,445,3),new P.ptr(446,447,1),new P.ptr(454,460,3),new P.ptr(462,476,2),new P.ptr(477,495,2),new P.ptr(496,499,3),new P.ptr(501,505,4),new P.ptr(507,563,2),new P.ptr(564,569,1),new P.ptr(572,575,3),new P.ptr(576,578,2),new P.ptr(583,591,2),new P.ptr(592,659,1),new P.ptr(661,687,1),new P.ptr(881,883,2),new P.ptr(887,891,4),new P.ptr(892,893,1),new P.ptr(912,940,28),new P.ptr(941,974,1),new P.ptr(976,977,1),new P.ptr(981,983,1),new P.ptr(985,1007,2),new P.ptr(1008,1011,1),new P.ptr(1013,1019,3),new P.ptr(1020,1072,52),new P.ptr(1073,1119,1),new P.ptr(1121,1153,2),new P.ptr(1163,1215,2),new P.ptr(1218,1230,2),new P.ptr(1231,1327,2),new P.ptr(1377,1415,1),new P.ptr(5112,5117,1),new P.ptr(7296,7304,1),new P.ptr(7424,7467,1),new P.ptr(7531,7543,1),new P.ptr(7545,7578,1),new P.ptr(7681,7829,2),new P.ptr(7830,7837,1),new P.ptr(7839,7935,2),new P.ptr(7936,7943,1),new P.ptr(7952,7957,1),new P.ptr(7968,7975,1),new P.ptr(7984,7991,1),new P.ptr(8000,8005,1),new P.ptr(8016,8023,1),new P.ptr(8032,8039,1),new P.ptr(8048,8061,1),new P.ptr(8064,8071,1),new P.ptr(8080,8087,1),new P.ptr(8096,8103,1),new P.ptr(8112,8116,1),new P.ptr(8118,8119,1),new P.ptr(8126,8130,4),new P.ptr(8131,8132,1),new P.ptr(8134,8135,1),new P.ptr(8144,8147,1),new P.ptr(8150,8151,1),new P.ptr(8160,8167,1),new P.ptr(8178,8180,1),new P.ptr(8182,8183,1),new P.ptr(8458,8462,4),new P.ptr(8463,8467,4),new P.ptr(8495,8505,5),new P.ptr(8508,8509,1),new P.ptr(8518,8521,1),new P.ptr(8526,8580,54),new P.ptr(11312,11358,1),new P.ptr(11361,11365,4),new P.ptr(11366,11372,2),new P.ptr(11377,11379,2),new P.ptr(11380,11382,2),new P.ptr(11383,11387,1),new P.ptr(11393,11491,2),new P.ptr(11492,11500,8),new P.ptr(11502,11507,5),new P.ptr(11520,11557,1),new P.ptr(11559,11565,6),new P.ptr(42561,42605,2),new P.ptr(42625,42651,2),new P.ptr(42787,42799,2),new P.ptr(42800,42801,1),new P.ptr(42803,42865,2),new P.ptr(42866,42872,1),new P.ptr(42874,42876,2),new P.ptr(42879,42887,2),new P.ptr(42892,42894,2),new P.ptr(42897,42899,2),new P.ptr(42900,42901,1),new P.ptr(42903,42921,2),new P.ptr(42933,42935,2),new P.ptr(43002,43824,822),new P.ptr(43825,43866,1),new P.ptr(43872,43877,1),new P.ptr(43888,43967,1),new P.ptr(64256,64262,1),new P.ptr(64275,64279,1),new P.ptr(65345,65370,1)]),new IZ([new Q.ptr(66600,66639,1),new Q.ptr(66776,66811,1),new Q.ptr(68800,68850,1),new Q.ptr(71872,71903,1),new Q.ptr(119834,119859,1),new Q.ptr(119886,119892,1),new Q.ptr(119894,119911,1),new Q.ptr(119938,119963,1),new Q.ptr(119990,119993,1),new Q.ptr(119995,119997,2),new Q.ptr(119998,120003,1),new Q.ptr(120005,120015,1),new Q.ptr(120042,120067,1),new Q.ptr(120094,120119,1),new Q.ptr(120146,120171,1),new Q.ptr(120198,120223,1),new Q.ptr(120250,120275,1),new Q.ptr(120302,120327,1),new Q.ptr(120354,120379,1),new Q.ptr(120406,120431,1),new Q.ptr(120458,120485,1),new Q.ptr(120514,120538,1),new Q.ptr(120540,120545,1),new Q.ptr(120572,120596,1),new Q.ptr(120598,120603,1),new Q.ptr(120630,120654,1),new Q.ptr(120656,120661,1),new Q.ptr(120688,120712,1),new Q.ptr(120714,120719,1),new Q.ptr(120746,120770,1),new Q.ptr(120772,120777,1),new Q.ptr(120779,125218,4439),new Q.ptr(125219,125251,1)]),4);AO=new O.ptr(new IY([new P.ptr(688,705,1),new P.ptr(710,721,1),new P.ptr(736,740,1),new P.ptr(748,750,2),new P.ptr(884,890,6),new P.ptr(1369,1600,231),new P.ptr(1765,1766,1),new P.ptr(2036,2037,1),new P.ptr(2042,2074,32),new P.ptr(2084,2088,4),new P.ptr(2417,3654,1237),new P.ptr(3782,4348,566),new P.ptr(6103,6211,108),new P.ptr(6823,7288,465),new P.ptr(7289,7293,1),new P.ptr(7468,7530,1),new P.ptr(7544,7579,35),new P.ptr(7580,7615,1),new P.ptr(8305,8319,14),new P.ptr(8336,8348,1),new P.ptr(11388,11389,1),new P.ptr(11631,11823,192),new P.ptr(12293,12337,44),new P.ptr(12338,12341,1),new P.ptr(12347,12445,98),new P.ptr(12446,12540,94),new P.ptr(12541,12542,1),new P.ptr(40981,42232,1251),new P.ptr(42233,42237,1),new P.ptr(42508,42623,115),new P.ptr(42652,42653,1),new P.ptr(42775,42783,1),new P.ptr(42864,42888,24),new P.ptr(43000,43001,1),new P.ptr(43471,43494,23),new P.ptr(43632,43741,109),new P.ptr(43763,43764,1),new P.ptr(43868,43871,1),new P.ptr(65392,65438,46),new P.ptr(65439,65439,1)]),new IZ([new Q.ptr(92992,92992,1),new Q.ptr(92993,92995,1),new Q.ptr(94099,94111,1),new Q.ptr(94176,94177,1)]),0);AP=new O.ptr(new IY([new P.ptr(170,186,16),new P.ptr(443,448,5),new P.ptr(449,451,1),new P.ptr(660,1488,828),new P.ptr(1489,1514,1),new P.ptr(1520,1522,1),new P.ptr(1568,1599,1),new P.ptr(1601,1610,1),new P.ptr(1646,1647,1),new P.ptr(1649,1747,1),new P.ptr(1749,1774,25),new P.ptr(1775,1786,11),new P.ptr(1787,1788,1),new P.ptr(1791,1808,17),new P.ptr(1810,1839,1),new P.ptr(1869,1957,1),new P.ptr(1969,1994,25),new P.ptr(1995,2026,1),new P.ptr(2048,2069,1),new P.ptr(2112,2136,1),new P.ptr(2144,2154,1),new P.ptr(2208,2228,1),new P.ptr(2230,2237,1),new P.ptr(2308,2361,1),new P.ptr(2365,2384,19),new P.ptr(2392,2401,1),new P.ptr(2418,2432,1),new P.ptr(2437,2444,1),new P.ptr(2447,2448,1),new P.ptr(2451,2472,1),new P.ptr(2474,2480,1),new P.ptr(2482,2486,4),new P.ptr(2487,2489,1),new P.ptr(2493,2510,17),new P.ptr(2524,2525,1),new P.ptr(2527,2529,1),new P.ptr(2544,2545,1),new P.ptr(2556,2565,9),new P.ptr(2566,2570,1),new P.ptr(2575,2576,1),new P.ptr(2579,2600,1),new P.ptr(2602,2608,1),new P.ptr(2610,2611,1),new P.ptr(2613,2614,1),new P.ptr(2616,2617,1),new P.ptr(2649,2652,1),new P.ptr(2654,2674,20),new P.ptr(2675,2676,1),new P.ptr(2693,2701,1),new P.ptr(2703,2705,1),new P.ptr(2707,2728,1),new P.ptr(2730,2736,1),new P.ptr(2738,2739,1),new P.ptr(2741,2745,1),new P.ptr(2749,2768,19),new P.ptr(2784,2785,1),new P.ptr(2809,2821,12),new P.ptr(2822,2828,1),new P.ptr(2831,2832,1),new P.ptr(2835,2856,1),new P.ptr(2858,2864,1),new P.ptr(2866,2867,1),new P.ptr(2869,2873,1),new P.ptr(2877,2908,31),new P.ptr(2909,2911,2),new P.ptr(2912,2913,1),new P.ptr(2929,2947,18),new P.ptr(2949,2954,1),new P.ptr(2958,2960,1),new P.ptr(2962,2965,1),new P.ptr(2969,2970,1),new P.ptr(2972,2974,2),new P.ptr(2975,2979,4),new P.ptr(2980,2984,4),new P.ptr(2985,2986,1),new P.ptr(2990,3001,1),new P.ptr(3024,3077,53),new P.ptr(3078,3084,1),new P.ptr(3086,3088,1),new P.ptr(3090,3112,1),new P.ptr(3114,3129,1),new P.ptr(3133,3160,27),new P.ptr(3161,3162,1),new P.ptr(3168,3169,1),new P.ptr(3200,3205,5),new P.ptr(3206,3212,1),new P.ptr(3214,3216,1),new P.ptr(3218,3240,1),new P.ptr(3242,3251,1),new P.ptr(3253,3257,1),new P.ptr(3261,3294,33),new P.ptr(3296,3297,1),new P.ptr(3313,3314,1),new P.ptr(3333,3340,1),new P.ptr(3342,3344,1),new P.ptr(3346,3386,1),new P.ptr(3389,3406,17),new P.ptr(3412,3414,1),new P.ptr(3423,3425,1),new P.ptr(3450,3455,1),new P.ptr(3461,3478,1),new P.ptr(3482,3505,1),new P.ptr(3507,3515,1),new P.ptr(3517,3520,3),new P.ptr(3521,3526,1),new P.ptr(3585,3632,1),new P.ptr(3634,3635,1),new P.ptr(3648,3653,1),new P.ptr(3713,3714,1),new P.ptr(3716,3719,3),new P.ptr(3720,3722,2),new P.ptr(3725,3732,7),new P.ptr(3733,3735,1),new P.ptr(3737,3743,1),new P.ptr(3745,3747,1),new P.ptr(3749,3751,2),new P.ptr(3754,3755,1),new P.ptr(3757,3760,1),new P.ptr(3762,3763,1),new P.ptr(3773,3776,3),new P.ptr(3777,3780,1),new P.ptr(3804,3807,1),new P.ptr(3840,3904,64),new P.ptr(3905,3911,1),new P.ptr(3913,3948,1),new P.ptr(3976,3980,1),new P.ptr(4096,4138,1),new P.ptr(4159,4176,17),new P.ptr(4177,4181,1),new P.ptr(4186,4189,1),new P.ptr(4193,4197,4),new P.ptr(4198,4206,8),new P.ptr(4207,4208,1),new P.ptr(4213,4225,1),new P.ptr(4238,4304,66),new P.ptr(4305,4346,1),new P.ptr(4349,4680,1),new P.ptr(4682,4685,1),new P.ptr(4688,4694,1),new P.ptr(4696,4698,2),new P.ptr(4699,4701,1),new P.ptr(4704,4744,1),new P.ptr(4746,4749,1),new P.ptr(4752,4784,1),new P.ptr(4786,4789,1),new P.ptr(4792,4798,1),new P.ptr(4800,4802,2),new P.ptr(4803,4805,1),new P.ptr(4808,4822,1),new P.ptr(4824,4880,1),new P.ptr(4882,4885,1),new P.ptr(4888,4954,1),new P.ptr(4992,5007,1),new P.ptr(5121,5740,1),new P.ptr(5743,5759,1),new P.ptr(5761,5786,1),new P.ptr(5792,5866,1),new P.ptr(5873,5880,1),new P.ptr(5888,5900,1),new P.ptr(5902,5905,1),new P.ptr(5920,5937,1),new P.ptr(5952,5969,1),new P.ptr(5984,5996,1),new P.ptr(5998,6000,1),new P.ptr(6016,6067,1),new P.ptr(6108,6176,68),new P.ptr(6177,6210,1),new P.ptr(6212,6263,1),new P.ptr(6272,6276,1),new P.ptr(6279,6312,1),new P.ptr(6314,6320,6),new P.ptr(6321,6389,1),new P.ptr(6400,6430,1),new P.ptr(6480,6509,1),new P.ptr(6512,6516,1),new P.ptr(6528,6571,1),new P.ptr(6576,6601,1),new P.ptr(6656,6678,1),new P.ptr(6688,6740,1),new P.ptr(6917,6963,1),new P.ptr(6981,6987,1),new P.ptr(7043,7072,1),new P.ptr(7086,7087,1),new P.ptr(7098,7141,1),new P.ptr(7168,7203,1),new P.ptr(7245,7247,1),new P.ptr(7258,7287,1),new P.ptr(7401,7404,1),new P.ptr(7406,7409,1),new P.ptr(7413,7414,1),new P.ptr(8501,8504,1),new P.ptr(11568,11623,1),new P.ptr(11648,11670,1),new P.ptr(11680,11686,1),new P.ptr(11688,11694,1),new P.ptr(11696,11702,1),new P.ptr(11704,11710,1),new P.ptr(11712,11718,1),new P.ptr(11720,11726,1),new P.ptr(11728,11734,1),new P.ptr(11736,11742,1),new P.ptr(12294,12348,54),new P.ptr(12353,12438,1),new P.ptr(12447,12449,2),new P.ptr(12450,12538,1),new P.ptr(12543,12549,6),new P.ptr(12550,12590,1),new P.ptr(12593,12686,1),new P.ptr(12704,12730,1),new P.ptr(12784,12799,1),new P.ptr(13312,19893,1),new P.ptr(19968,40938,1),new P.ptr(40960,40980,1),new P.ptr(40982,42124,1),new P.ptr(42192,42231,1),new P.ptr(42240,42507,1),new P.ptr(42512,42527,1),new P.ptr(42538,42539,1),new P.ptr(42606,42656,50),new P.ptr(42657,42725,1),new P.ptr(42895,42999,104),new P.ptr(43003,43009,1),new P.ptr(43011,43013,1),new P.ptr(43015,43018,1),new P.ptr(43020,43042,1),new P.ptr(43072,43123,1),new P.ptr(43138,43187,1),new P.ptr(43250,43255,1),new P.ptr(43259,43261,2),new P.ptr(43274,43301,1),new P.ptr(43312,43334,1),new P.ptr(43360,43388,1),new P.ptr(43396,43442,1),new P.ptr(43488,43492,1),new P.ptr(43495,43503,1),new P.ptr(43514,43518,1),new P.ptr(43520,43560,1),new P.ptr(43584,43586,1),new P.ptr(43588,43595,1),new P.ptr(43616,43631,1),new P.ptr(43633,43638,1),new P.ptr(43642,43646,4),new P.ptr(43647,43695,1),new P.ptr(43697,43701,4),new P.ptr(43702,43705,3),new P.ptr(43706,43709,1),new P.ptr(43712,43714,2),new P.ptr(43739,43740,1),new P.ptr(43744,43754,1),new P.ptr(43762,43777,15),new P.ptr(43778,43782,1),new P.ptr(43785,43790,1),new P.ptr(43793,43798,1),new P.ptr(43808,43814,1),new P.ptr(43816,43822,1),new P.ptr(43968,44002,1),new P.ptr(44032,55203,1),new P.ptr(55216,55238,1),new P.ptr(55243,55291,1),new P.ptr(63744,64109,1),new P.ptr(64112,64217,1),new P.ptr(64285,64287,2),new P.ptr(64288,64296,1),new P.ptr(64298,64310,1),new P.ptr(64312,64316,1),new P.ptr(64318,64320,2),new P.ptr(64321,64323,2),new P.ptr(64324,64326,2),new P.ptr(64327,64433,1),new P.ptr(64467,64829,1),new P.ptr(64848,64911,1),new P.ptr(64914,64967,1),new P.ptr(65008,65019,1),new P.ptr(65136,65140,1),new P.ptr(65142,65276,1),new P.ptr(65382,65391,1),new P.ptr(65393,65437,1),new P.ptr(65440,65470,1),new P.ptr(65474,65479,1),new P.ptr(65482,65487,1),new P.ptr(65490,65495,1),new P.ptr(65498,65500,1)]),new IZ([new Q.ptr(65536,65547,1),new Q.ptr(65549,65574,1),new Q.ptr(65576,65594,1),new Q.ptr(65596,65597,1),new Q.ptr(65599,65613,1),new Q.ptr(65616,65629,1),new Q.ptr(65664,65786,1),new Q.ptr(66176,66204,1),new Q.ptr(66208,66256,1),new Q.ptr(66304,66335,1),new Q.ptr(66349,66368,1),new Q.ptr(66370,66377,1),new Q.ptr(66384,66421,1),new Q.ptr(66432,66461,1),new Q.ptr(66464,66499,1),new Q.ptr(66504,66511,1),new Q.ptr(66640,66717,1),new Q.ptr(66816,66855,1),new Q.ptr(66864,66915,1),new Q.ptr(67072,67382,1),new Q.ptr(67392,67413,1),new Q.ptr(67424,67431,1),new Q.ptr(67584,67589,1),new Q.ptr(67592,67594,2),new Q.ptr(67595,67637,1),new Q.ptr(67639,67640,1),new Q.ptr(67644,67647,3),new Q.ptr(67648,67669,1),new Q.ptr(67680,67702,1),new Q.ptr(67712,67742,1),new Q.ptr(67808,67826,1),new Q.ptr(67828,67829,1),new Q.ptr(67840,67861,1),new Q.ptr(67872,67897,1),new Q.ptr(67968,68023,1),new Q.ptr(68030,68031,1),new Q.ptr(68096,68112,16),new Q.ptr(68113,68115,1),new Q.ptr(68117,68119,1),new Q.ptr(68121,68147,1),new Q.ptr(68192,68220,1),new Q.ptr(68224,68252,1),new Q.ptr(68288,68295,1),new Q.ptr(68297,68324,1),new Q.ptr(68352,68405,1),new Q.ptr(68416,68437,1),new Q.ptr(68448,68466,1),new Q.ptr(68480,68497,1),new Q.ptr(68608,68680,1),new Q.ptr(69635,69687,1),new Q.ptr(69763,69807,1),new Q.ptr(69840,69864,1),new Q.ptr(69891,69926,1),new Q.ptr(69968,70002,1),new Q.ptr(70006,70019,13),new Q.ptr(70020,70066,1),new Q.ptr(70081,70084,1),new Q.ptr(70106,70108,2),new Q.ptr(70144,70161,1),new Q.ptr(70163,70187,1),new Q.ptr(70272,70278,1),new Q.ptr(70280,70282,2),new Q.ptr(70283,70285,1),new Q.ptr(70287,70301,1),new Q.ptr(70303,70312,1),new Q.ptr(70320,70366,1),new Q.ptr(70405,70412,1),new Q.ptr(70415,70416,1),new Q.ptr(70419,70440,1),new Q.ptr(70442,70448,1),new Q.ptr(70450,70451,1),new Q.ptr(70453,70457,1),new Q.ptr(70461,70480,19),new Q.ptr(70493,70497,1),new Q.ptr(70656,70708,1),new Q.ptr(70727,70730,1),new Q.ptr(70784,70831,1),new Q.ptr(70852,70853,1),new Q.ptr(70855,71040,185),new Q.ptr(71041,71086,1),new Q.ptr(71128,71131,1),new Q.ptr(71168,71215,1),new Q.ptr(71236,71296,60),new Q.ptr(71297,71338,1),new Q.ptr(71424,71449,1),new Q.ptr(71935,72192,257),new Q.ptr(72203,72242,1),new Q.ptr(72250,72272,22),new Q.ptr(72284,72323,1),new Q.ptr(72326,72329,1),new Q.ptr(72384,72440,1),new Q.ptr(72704,72712,1),new Q.ptr(72714,72750,1),new Q.ptr(72768,72818,50),new Q.ptr(72819,72847,1),new Q.ptr(72960,72966,1),new Q.ptr(72968,72969,1),new Q.ptr(72971,73008,1),new Q.ptr(73030,73728,698),new Q.ptr(73729,74649,1),new Q.ptr(74880,75075,1),new Q.ptr(77824,78894,1),new Q.ptr(82944,83526,1),new Q.ptr(92160,92728,1),new Q.ptr(92736,92766,1),new Q.ptr(92880,92909,1),new Q.ptr(92928,92975,1),new Q.ptr(93027,93047,1),new Q.ptr(93053,93071,1),new Q.ptr(93952,94020,1),new Q.ptr(94032,94208,176),new Q.ptr(94209,100332,1),new Q.ptr(100352,101106,1),new Q.ptr(110592,110878,1),new Q.ptr(110960,111355,1),new Q.ptr(113664,113770,1),new Q.ptr(113776,113788,1),new Q.ptr(113792,113800,1),new Q.ptr(113808,113817,1),new Q.ptr(124928,125124,1),new Q.ptr(126464,126467,1),new Q.ptr(126469,126495,1),new Q.ptr(126497,126498,1),new Q.ptr(126500,126503,3),new Q.ptr(126505,126514,1),new Q.ptr(126516,126519,1),new Q.ptr(126521,126523,2),new Q.ptr(126530,126535,5),new Q.ptr(126537,126541,2),new Q.ptr(126542,126543,1),new Q.ptr(126545,126546,1),new Q.ptr(126548,126551,3),new Q.ptr(126553,126561,2),new Q.ptr(126562,126564,2),new Q.ptr(126567,126570,1),new Q.ptr(126572,126578,1),new Q.ptr(126580,126583,1),new Q.ptr(126585,126588,1),new Q.ptr(126590,126592,2),new Q.ptr(126593,126601,1),new Q.ptr(126603,126619,1),new Q.ptr(126625,126627,1),new Q.ptr(126629,126633,1),new Q.ptr(126635,126651,1),new Q.ptr(131072,173782,1),new Q.ptr(173824,177972,1),new Q.ptr(177984,178205,1),new Q.ptr(178208,183969,1),new Q.ptr(183984,191456,1),new Q.ptr(194560,195101,1)]),1);AQ=new O.ptr(new IY([new P.ptr(453,459,3),new P.ptr(498,8072,7574),new P.ptr(8073,8079,1),new P.ptr(8088,8095,1),new P.ptr(8104,8111,1),new P.ptr(8124,8140,16),new P.ptr(8188,8188,1)]),IZ.nil,0);AR=new O.ptr(new IY([new P.ptr(65,90,1),new P.ptr(192,214,1),new P.ptr(216,222,1),new P.ptr(256,310,2),new P.ptr(313,327,2),new P.ptr(330,376,2),new P.ptr(377,381,2),new P.ptr(385,386,1),new P.ptr(388,390,2),new P.ptr(391,393,2),new P.ptr(394,395,1),new P.ptr(398,401,1),new P.ptr(403,404,1),new P.ptr(406,408,1),new P.ptr(412,413,1),new P.ptr(415,416,1),new P.ptr(418,422,2),new P.ptr(423,425,2),new P.ptr(428,430,2),new P.ptr(431,433,2),new P.ptr(434,435,1),new P.ptr(437,439,2),new P.ptr(440,444,4),new P.ptr(452,461,3),new P.ptr(463,475,2),new P.ptr(478,494,2),new P.ptr(497,500,3),new P.ptr(502,504,1),new P.ptr(506,562,2),new P.ptr(570,571,1),new P.ptr(573,574,1),new P.ptr(577,579,2),new P.ptr(580,582,1),new P.ptr(584,590,2),new P.ptr(880,882,2),new P.ptr(886,895,9),new P.ptr(902,904,2),new P.ptr(905,906,1),new P.ptr(908,910,2),new P.ptr(911,913,2),new P.ptr(914,929,1),new P.ptr(931,939,1),new P.ptr(975,978,3),new P.ptr(979,980,1),new P.ptr(984,1006,2),new P.ptr(1012,1015,3),new P.ptr(1017,1018,1),new P.ptr(1021,1071,1),new P.ptr(1120,1152,2),new P.ptr(1162,1216,2),new P.ptr(1217,1229,2),new P.ptr(1232,1326,2),new P.ptr(1329,1366,1),new P.ptr(4256,4293,1),new P.ptr(4295,4301,6),new P.ptr(5024,5109,1),new P.ptr(7680,7828,2),new P.ptr(7838,7934,2),new P.ptr(7944,7951,1),new P.ptr(7960,7965,1),new P.ptr(7976,7983,1),new P.ptr(7992,7999,1),new P.ptr(8008,8013,1),new P.ptr(8025,8031,2),new P.ptr(8040,8047,1),new P.ptr(8120,8123,1),new P.ptr(8136,8139,1),new P.ptr(8152,8155,1),new P.ptr(8168,8172,1),new P.ptr(8184,8187,1),new P.ptr(8450,8455,5),new P.ptr(8459,8461,1),new P.ptr(8464,8466,1),new P.ptr(8469,8473,4),new P.ptr(8474,8477,1),new P.ptr(8484,8490,2),new P.ptr(8491,8493,1),new P.ptr(8496,8499,1),new P.ptr(8510,8511,1),new P.ptr(8517,8579,62),new P.ptr(11264,11310,1),new P.ptr(11360,11362,2),new P.ptr(11363,11364,1),new P.ptr(11367,11373,2),new P.ptr(11374,11376,1),new P.ptr(11378,11381,3),new P.ptr(11390,11392,1),new P.ptr(11394,11490,2),new P.ptr(11499,11501,2),new P.ptr(11506,42560,31054),new P.ptr(42562,42604,2),new P.ptr(42624,42650,2),new P.ptr(42786,42798,2),new P.ptr(42802,42862,2),new P.ptr(42873,42877,2),new P.ptr(42878,42886,2),new P.ptr(42891,42893,2),new P.ptr(42896,42898,2),new P.ptr(42902,42922,2),new P.ptr(42923,42926,1),new P.ptr(42928,42932,1),new P.ptr(42934,65313,22379),new P.ptr(65314,65338,1)]),new IZ([new Q.ptr(66560,66599,1),new Q.ptr(66736,66771,1),new Q.ptr(68736,68786,1),new Q.ptr(71840,71871,1),new Q.ptr(119808,119833,1),new Q.ptr(119860,119885,1),new Q.ptr(119912,119937,1),new Q.ptr(119964,119966,2),new Q.ptr(119967,119973,3),new Q.ptr(119974,119977,3),new Q.ptr(119978,119980,1),new Q.ptr(119982,119989,1),new Q.ptr(120016,120041,1),new Q.ptr(120068,120069,1),new Q.ptr(120071,120074,1),new Q.ptr(120077,120084,1),new Q.ptr(120086,120092,1),new Q.ptr(120120,120121,1),new Q.ptr(120123,120126,1),new Q.ptr(120128,120132,1),new Q.ptr(120134,120138,4),new Q.ptr(120139,120144,1),new Q.ptr(120172,120197,1),new Q.ptr(120224,120249,1),new Q.ptr(120276,120301,1),new Q.ptr(120328,120353,1),new Q.ptr(120380,120405,1),new Q.ptr(120432,120457,1),new Q.ptr(120488,120512,1),new Q.ptr(120546,120570,1),new Q.ptr(120604,120628,1),new Q.ptr(120662,120686,1),new Q.ptr(120720,120744,1),new Q.ptr(120778,125184,4406),new Q.ptr(125185,125217,1)]),3);AS=new O.ptr(new IY([new P.ptr(768,879,1),new P.ptr(1155,1161,1),new P.ptr(1425,1469,1),new P.ptr(1471,1473,2),new P.ptr(1474,1476,2),new P.ptr(1477,1479,2),new P.ptr(1552,1562,1),new P.ptr(1611,1631,1),new P.ptr(1648,1750,102),new P.ptr(1751,1756,1),new P.ptr(1759,1764,1),new P.ptr(1767,1768,1),new P.ptr(1770,1773,1),new P.ptr(1809,1840,31),new P.ptr(1841,1866,1),new P.ptr(1958,1968,1),new P.ptr(2027,2035,1),new P.ptr(2070,2073,1),new P.ptr(2075,2083,1),new P.ptr(2085,2087,1),new P.ptr(2089,2093,1),new P.ptr(2137,2139,1),new P.ptr(2260,2273,1),new P.ptr(2275,2307,1),new P.ptr(2362,2364,1),new P.ptr(2366,2383,1),new P.ptr(2385,2391,1),new P.ptr(2402,2403,1),new P.ptr(2433,2435,1),new P.ptr(2492,2494,2),new P.ptr(2495,2500,1),new P.ptr(2503,2504,1),new P.ptr(2507,2509,1),new P.ptr(2519,2530,11),new P.ptr(2531,2561,30),new P.ptr(2562,2563,1),new P.ptr(2620,2622,2),new P.ptr(2623,2626,1),new P.ptr(2631,2632,1),new P.ptr(2635,2637,1),new P.ptr(2641,2672,31),new P.ptr(2673,2677,4),new P.ptr(2689,2691,1),new P.ptr(2748,2750,2),new P.ptr(2751,2757,1),new P.ptr(2759,2761,1),new P.ptr(2763,2765,1),new P.ptr(2786,2787,1),new P.ptr(2810,2815,1),new P.ptr(2817,2819,1),new P.ptr(2876,2878,2),new P.ptr(2879,2884,1),new P.ptr(2887,2888,1),new P.ptr(2891,2893,1),new P.ptr(2902,2903,1),new P.ptr(2914,2915,1),new P.ptr(2946,3006,60),new P.ptr(3007,3010,1),new P.ptr(3014,3016,1),new P.ptr(3018,3021,1),new P.ptr(3031,3072,41),new P.ptr(3073,3075,1),new P.ptr(3134,3140,1),new P.ptr(3142,3144,1),new P.ptr(3146,3149,1),new P.ptr(3157,3158,1),new P.ptr(3170,3171,1),new P.ptr(3201,3203,1),new P.ptr(3260,3262,2),new P.ptr(3263,3268,1),new P.ptr(3270,3272,1),new P.ptr(3274,3277,1),new P.ptr(3285,3286,1),new P.ptr(3298,3299,1),new P.ptr(3328,3331,1),new P.ptr(3387,3388,1),new P.ptr(3390,3396,1),new P.ptr(3398,3400,1),new P.ptr(3402,3405,1),new P.ptr(3415,3426,11),new P.ptr(3427,3458,31),new P.ptr(3459,3530,71),new P.ptr(3535,3540,1),new P.ptr(3542,3544,2),new P.ptr(3545,3551,1),new P.ptr(3570,3571,1),new P.ptr(3633,3636,3),new P.ptr(3637,3642,1),new P.ptr(3655,3662,1),new P.ptr(3761,3764,3),new P.ptr(3765,3769,1),new P.ptr(3771,3772,1),new P.ptr(3784,3789,1),new P.ptr(3864,3865,1),new P.ptr(3893,3897,2),new P.ptr(3902,3903,1),new P.ptr(3953,3972,1),new P.ptr(3974,3975,1),new P.ptr(3981,3991,1),new P.ptr(3993,4028,1),new P.ptr(4038,4139,101),new P.ptr(4140,4158,1),new P.ptr(4182,4185,1),new P.ptr(4190,4192,1),new P.ptr(4194,4196,1),new P.ptr(4199,4205,1),new P.ptr(4209,4212,1),new P.ptr(4226,4237,1),new P.ptr(4239,4250,11),new P.ptr(4251,4253,1),new P.ptr(4957,4959,1),new P.ptr(5906,5908,1),new P.ptr(5938,5940,1),new P.ptr(5970,5971,1),new P.ptr(6002,6003,1),new P.ptr(6068,6099,1),new P.ptr(6109,6155,46),new P.ptr(6156,6157,1),new P.ptr(6277,6278,1),new P.ptr(6313,6432,119),new P.ptr(6433,6443,1),new P.ptr(6448,6459,1),new P.ptr(6679,6683,1),new P.ptr(6741,6750,1),new P.ptr(6752,6780,1),new P.ptr(6783,6832,49),new P.ptr(6833,6846,1),new P.ptr(6912,6916,1),new P.ptr(6964,6980,1),new P.ptr(7019,7027,1),new P.ptr(7040,7042,1),new P.ptr(7073,7085,1),new P.ptr(7142,7155,1),new P.ptr(7204,7223,1),new P.ptr(7376,7378,1),new P.ptr(7380,7400,1),new P.ptr(7405,7410,5),new P.ptr(7411,7412,1),new P.ptr(7415,7417,1),new P.ptr(7616,7673,1),new P.ptr(7675,7679,1),new P.ptr(8400,8432,1),new P.ptr(11503,11505,1),new P.ptr(11647,11744,97),new P.ptr(11745,11775,1),new P.ptr(12330,12335,1),new P.ptr(12441,12442,1),new P.ptr(42607,42610,1),new P.ptr(42612,42621,1),new P.ptr(42654,42655,1),new P.ptr(42736,42737,1),new P.ptr(43010,43014,4),new P.ptr(43019,43043,24),new P.ptr(43044,43047,1),new P.ptr(43136,43137,1),new P.ptr(43188,43205,1),new P.ptr(43232,43249,1),new P.ptr(43302,43309,1),new P.ptr(43335,43347,1),new P.ptr(43392,43395,1),new P.ptr(43443,43456,1),new P.ptr(43493,43561,68),new P.ptr(43562,43574,1),new P.ptr(43587,43596,9),new P.ptr(43597,43643,46),new P.ptr(43644,43645,1),new P.ptr(43696,43698,2),new P.ptr(43699,43700,1),new P.ptr(43703,43704,1),new P.ptr(43710,43711,1),new P.ptr(43713,43755,42),new P.ptr(43756,43759,1),new P.ptr(43765,43766,1),new P.ptr(44003,44010,1),new P.ptr(44012,44013,1),new P.ptr(64286,65024,738),new P.ptr(65025,65039,1),new P.ptr(65056,65071,1)]),new IZ([new Q.ptr(66045,66272,227),new Q.ptr(66422,66426,1),new Q.ptr(68097,68099,1),new Q.ptr(68101,68102,1),new Q.ptr(68108,68111,1),new Q.ptr(68152,68154,1),new Q.ptr(68159,68325,166),new Q.ptr(68326,69632,1306),new Q.ptr(69633,69634,1),new Q.ptr(69688,69702,1),new Q.ptr(69759,69762,1),new Q.ptr(69808,69818,1),new Q.ptr(69888,69890,1),new Q.ptr(69927,69940,1),new Q.ptr(70003,70016,13),new Q.ptr(70017,70018,1),new Q.ptr(70067,70080,1),new Q.ptr(70090,70092,1),new Q.ptr(70188,70199,1),new Q.ptr(70206,70367,161),new Q.ptr(70368,70378,1),new Q.ptr(70400,70403,1),new Q.ptr(70460,70462,2),new Q.ptr(70463,70468,1),new Q.ptr(70471,70472,1),new Q.ptr(70475,70477,1),new Q.ptr(70487,70498,11),new Q.ptr(70499,70502,3),new Q.ptr(70503,70508,1),new Q.ptr(70512,70516,1),new Q.ptr(70709,70726,1),new Q.ptr(70832,70851,1),new Q.ptr(71087,71093,1),new Q.ptr(71096,71104,1),new Q.ptr(71132,71133,1),new Q.ptr(71216,71232,1),new Q.ptr(71339,71351,1),new Q.ptr(71453,71467,1),new Q.ptr(72193,72202,1),new Q.ptr(72243,72249,1),new Q.ptr(72251,72254,1),new Q.ptr(72263,72273,10),new Q.ptr(72274,72283,1),new Q.ptr(72330,72345,1),new Q.ptr(72751,72758,1),new Q.ptr(72760,72767,1),new Q.ptr(72850,72871,1),new Q.ptr(72873,72886,1),new Q.ptr(73009,73014,1),new Q.ptr(73018,73020,2),new Q.ptr(73021,73023,2),new Q.ptr(73024,73029,1),new Q.ptr(73031,92912,19881),new Q.ptr(92913,92916,1),new Q.ptr(92976,92982,1),new Q.ptr(94033,94078,1),new Q.ptr(94095,94098,1),new Q.ptr(113821,113822,1),new Q.ptr(119141,119145,1),new Q.ptr(119149,119154,1),new Q.ptr(119163,119170,1),new Q.ptr(119173,119179,1),new Q.ptr(119210,119213,1),new Q.ptr(119362,119364,1),new Q.ptr(121344,121398,1),new Q.ptr(121403,121452,1),new Q.ptr(121461,121476,15),new Q.ptr(121499,121503,1),new Q.ptr(121505,121519,1),new Q.ptr(122880,122886,1),new Q.ptr(122888,122904,1),new Q.ptr(122907,122913,1),new Q.ptr(122915,122916,1),new Q.ptr(122918,122922,1),new Q.ptr(125136,125142,1),new Q.ptr(125252,125258,1),new Q.ptr(917760,917999,1)]),0);AT=new O.ptr(new IY([new P.ptr(2307,2363,56),new P.ptr(2366,2368,1),new P.ptr(2377,2380,1),new P.ptr(2382,2383,1),new P.ptr(2434,2435,1),new P.ptr(2494,2496,1),new P.ptr(2503,2504,1),new P.ptr(2507,2508,1),new P.ptr(2519,2563,44),new P.ptr(2622,2624,1),new P.ptr(2691,2750,59),new P.ptr(2751,2752,1),new P.ptr(2761,2763,2),new P.ptr(2764,2818,54),new P.ptr(2819,2878,59),new P.ptr(2880,2887,7),new P.ptr(2888,2891,3),new P.ptr(2892,2903,11),new P.ptr(3006,3007,1),new P.ptr(3009,3010,1),new P.ptr(3014,3016,1),new P.ptr(3018,3020,1),new P.ptr(3031,3073,42),new P.ptr(3074,3075,1),new P.ptr(3137,3140,1),new P.ptr(3202,3203,1),new P.ptr(3262,3264,2),new P.ptr(3265,3268,1),new P.ptr(3271,3272,1),new P.ptr(3274,3275,1),new P.ptr(3285,3286,1),new P.ptr(3330,3331,1),new P.ptr(3390,3392,1),new P.ptr(3398,3400,1),new P.ptr(3402,3404,1),new P.ptr(3415,3458,43),new P.ptr(3459,3535,76),new P.ptr(3536,3537,1),new P.ptr(3544,3551,1),new P.ptr(3570,3571,1),new P.ptr(3902,3903,1),new P.ptr(3967,4139,172),new P.ptr(4140,4145,5),new P.ptr(4152,4155,3),new P.ptr(4156,4182,26),new P.ptr(4183,4194,11),new P.ptr(4195,4196,1),new P.ptr(4199,4205,1),new P.ptr(4227,4228,1),new P.ptr(4231,4236,1),new P.ptr(4239,4250,11),new P.ptr(4251,4252,1),new P.ptr(6070,6078,8),new P.ptr(6079,6085,1),new P.ptr(6087,6088,1),new P.ptr(6435,6438,1),new P.ptr(6441,6443,1),new P.ptr(6448,6449,1),new P.ptr(6451,6456,1),new P.ptr(6681,6682,1),new P.ptr(6741,6743,2),new P.ptr(6753,6755,2),new P.ptr(6756,6765,9),new P.ptr(6766,6770,1),new P.ptr(6916,6965,49),new P.ptr(6971,6973,2),new P.ptr(6974,6977,1),new P.ptr(6979,6980,1),new P.ptr(7042,7073,31),new P.ptr(7078,7079,1),new P.ptr(7082,7143,61),new P.ptr(7146,7148,1),new P.ptr(7150,7154,4),new P.ptr(7155,7204,49),new P.ptr(7205,7211,1),new P.ptr(7220,7221,1),new P.ptr(7393,7410,17),new P.ptr(7411,7415,4),new P.ptr(12334,12335,1),new P.ptr(43043,43044,1),new P.ptr(43047,43136,89),new P.ptr(43137,43188,51),new P.ptr(43189,43203,1),new P.ptr(43346,43347,1),new P.ptr(43395,43444,49),new P.ptr(43445,43450,5),new P.ptr(43451,43453,2),new P.ptr(43454,43456,1),new P.ptr(43567,43568,1),new P.ptr(43571,43572,1),new P.ptr(43597,43643,46),new P.ptr(43645,43755,110),new P.ptr(43758,43759,1),new P.ptr(43765,44003,238),new P.ptr(44004,44006,2),new P.ptr(44007,44009,2),new P.ptr(44010,44012,2)]),new IZ([new Q.ptr(69632,69634,2),new Q.ptr(69762,69808,46),new Q.ptr(69809,69810,1),new Q.ptr(69815,69816,1),new Q.ptr(69932,70018,86),new Q.ptr(70067,70069,1),new Q.ptr(70079,70080,1),new Q.ptr(70188,70190,1),new Q.ptr(70194,70195,1),new Q.ptr(70197,70368,171),new Q.ptr(70369,70370,1),new Q.ptr(70402,70403,1),new Q.ptr(70462,70463,1),new Q.ptr(70465,70468,1),new Q.ptr(70471,70472,1),new Q.ptr(70475,70477,1),new Q.ptr(70487,70498,11),new Q.ptr(70499,70709,210),new Q.ptr(70710,70711,1),new Q.ptr(70720,70721,1),new Q.ptr(70725,70832,107),new Q.ptr(70833,70834,1),new Q.ptr(70841,70843,2),new Q.ptr(70844,70846,1),new Q.ptr(70849,71087,238),new Q.ptr(71088,71089,1),new Q.ptr(71096,71099,1),new Q.ptr(71102,71216,114),new Q.ptr(71217,71218,1),new Q.ptr(71227,71228,1),new Q.ptr(71230,71340,110),new Q.ptr(71342,71343,1),new Q.ptr(71350,71456,106),new Q.ptr(71457,71462,5),new Q.ptr(72199,72200,1),new Q.ptr(72249,72279,30),new Q.ptr(72280,72343,63),new Q.ptr(72751,72766,15),new Q.ptr(72873,72881,8),new Q.ptr(72884,94033,21149),new Q.ptr(94034,94078,1),new Q.ptr(119141,119142,1),new Q.ptr(119149,119154,1)]),0);AU=new O.ptr(new IY([new P.ptr(1160,1161,1),new P.ptr(6846,8413,1567),new P.ptr(8414,8416,1),new P.ptr(8418,8420,1),new P.ptr(42608,42610,1)]),IZ.nil,0);AV=new O.ptr(new IY([new P.ptr(768,879,1),new P.ptr(1155,1159,1),new P.ptr(1425,1469,1),new P.ptr(1471,1473,2),new P.ptr(1474,1476,2),new P.ptr(1477,1479,2),new P.ptr(1552,1562,1),new P.ptr(1611,1631,1),new P.ptr(1648,1750,102),new P.ptr(1751,1756,1),new P.ptr(1759,1764,1),new P.ptr(1767,1768,1),new P.ptr(1770,1773,1),new P.ptr(1809,1840,31),new P.ptr(1841,1866,1),new P.ptr(1958,1968,1),new P.ptr(2027,2035,1),new P.ptr(2070,2073,1),new P.ptr(2075,2083,1),new P.ptr(2085,2087,1),new P.ptr(2089,2093,1),new P.ptr(2137,2139,1),new P.ptr(2260,2273,1),new P.ptr(2275,2306,1),new P.ptr(2362,2364,2),new P.ptr(2369,2376,1),new P.ptr(2381,2385,4),new P.ptr(2386,2391,1),new P.ptr(2402,2403,1),new P.ptr(2433,2492,59),new P.ptr(2497,2500,1),new P.ptr(2509,2530,21),new P.ptr(2531,2561,30),new P.ptr(2562,2620,58),new P.ptr(2625,2626,1),new P.ptr(2631,2632,1),new P.ptr(2635,2637,1),new P.ptr(2641,2672,31),new P.ptr(2673,2677,4),new P.ptr(2689,2690,1),new P.ptr(2748,2753,5),new P.ptr(2754,2757,1),new P.ptr(2759,2760,1),new P.ptr(2765,2786,21),new P.ptr(2787,2810,23),new P.ptr(2811,2815,1),new P.ptr(2817,2876,59),new P.ptr(2879,2881,2),new P.ptr(2882,2884,1),new P.ptr(2893,2902,9),new P.ptr(2914,2915,1),new P.ptr(2946,3008,62),new P.ptr(3021,3072,51),new P.ptr(3134,3136,1),new P.ptr(3142,3144,1),new P.ptr(3146,3149,1),new P.ptr(3157,3158,1),new P.ptr(3170,3171,1),new P.ptr(3201,3260,59),new P.ptr(3263,3270,7),new P.ptr(3276,3277,1),new P.ptr(3298,3299,1),new P.ptr(3328,3329,1),new P.ptr(3387,3388,1),new P.ptr(3393,3396,1),new P.ptr(3405,3426,21),new P.ptr(3427,3530,103),new P.ptr(3538,3540,1),new P.ptr(3542,3633,91),new P.ptr(3636,3642,1),new P.ptr(3655,3662,1),new P.ptr(3761,3764,3),new P.ptr(3765,3769,1),new P.ptr(3771,3772,1),new P.ptr(3784,3789,1),new P.ptr(3864,3865,1),new P.ptr(3893,3897,2),new P.ptr(3953,3966,1),new P.ptr(3968,3972,1),new P.ptr(3974,3975,1),new P.ptr(3981,3991,1),new P.ptr(3993,4028,1),new P.ptr(4038,4141,103),new P.ptr(4142,4144,1),new P.ptr(4146,4151,1),new P.ptr(4153,4154,1),new P.ptr(4157,4158,1),new P.ptr(4184,4185,1),new P.ptr(4190,4192,1),new P.ptr(4209,4212,1),new P.ptr(4226,4229,3),new P.ptr(4230,4237,7),new P.ptr(4253,4957,704),new P.ptr(4958,4959,1),new P.ptr(5906,5908,1),new P.ptr(5938,5940,1),new P.ptr(5970,5971,1),new P.ptr(6002,6003,1),new P.ptr(6068,6069,1),new P.ptr(6071,6077,1),new P.ptr(6086,6089,3),new P.ptr(6090,6099,1),new P.ptr(6109,6155,46),new P.ptr(6156,6157,1),new P.ptr(6277,6278,1),new P.ptr(6313,6432,119),new P.ptr(6433,6434,1),new P.ptr(6439,6440,1),new P.ptr(6450,6457,7),new P.ptr(6458,6459,1),new P.ptr(6679,6680,1),new P.ptr(6683,6742,59),new P.ptr(6744,6750,1),new P.ptr(6752,6754,2),new P.ptr(6757,6764,1),new P.ptr(6771,6780,1),new P.ptr(6783,6832,49),new P.ptr(6833,6845,1),new P.ptr(6912,6915,1),new P.ptr(6964,6966,2),new P.ptr(6967,6970,1),new P.ptr(6972,6978,6),new P.ptr(7019,7027,1),new P.ptr(7040,7041,1),new P.ptr(7074,7077,1),new P.ptr(7080,7081,1),new P.ptr(7083,7085,1),new P.ptr(7142,7144,2),new P.ptr(7145,7149,4),new P.ptr(7151,7153,1),new P.ptr(7212,7219,1),new P.ptr(7222,7223,1),new P.ptr(7376,7378,1),new P.ptr(7380,7392,1),new P.ptr(7394,7400,1),new P.ptr(7405,7412,7),new P.ptr(7416,7417,1),new P.ptr(7616,7673,1),new P.ptr(7675,7679,1),new P.ptr(8400,8412,1),new P.ptr(8417,8421,4),new P.ptr(8422,8432,1),new P.ptr(11503,11505,1),new P.ptr(11647,11744,97),new P.ptr(11745,11775,1),new P.ptr(12330,12333,1),new P.ptr(12441,12442,1),new P.ptr(42607,42612,5),new P.ptr(42613,42621,1),new P.ptr(42654,42655,1),new P.ptr(42736,42737,1),new P.ptr(43010,43014,4),new P.ptr(43019,43045,26),new P.ptr(43046,43204,158),new P.ptr(43205,43232,27),new P.ptr(43233,43249,1),new P.ptr(43302,43309,1),new P.ptr(43335,43345,1),new P.ptr(43392,43394,1),new P.ptr(43443,43446,3),new P.ptr(43447,43449,1),new P.ptr(43452,43493,41),new P.ptr(43561,43566,1),new P.ptr(43569,43570,1),new P.ptr(43573,43574,1),new P.ptr(43587,43596,9),new P.ptr(43644,43696,52),new P.ptr(43698,43700,1),new P.ptr(43703,43704,1),new P.ptr(43710,43711,1),new P.ptr(43713,43756,43),new P.ptr(43757,43766,9),new P.ptr(44005,44008,3),new P.ptr(44013,64286,20273),new P.ptr(65024,65039,1),new P.ptr(65056,65071,1)]),new IZ([new Q.ptr(66045,66272,227),new Q.ptr(66422,66426,1),new Q.ptr(68097,68099,1),new Q.ptr(68101,68102,1),new Q.ptr(68108,68111,1),new Q.ptr(68152,68154,1),new Q.ptr(68159,68325,166),new Q.ptr(68326,69633,1307),new Q.ptr(69688,69702,1),new Q.ptr(69759,69761,1),new Q.ptr(69811,69814,1),new Q.ptr(69817,69818,1),new Q.ptr(69888,69890,1),new Q.ptr(69927,69931,1),new Q.ptr(69933,69940,1),new Q.ptr(70003,70016,13),new Q.ptr(70017,70070,53),new Q.ptr(70071,70078,1),new Q.ptr(70090,70092,1),new Q.ptr(70191,70193,1),new Q.ptr(70196,70198,2),new Q.ptr(70199,70206,7),new Q.ptr(70367,70371,4),new Q.ptr(70372,70378,1),new Q.ptr(70400,70401,1),new Q.ptr(70460,70464,4),new Q.ptr(70502,70508,1),new Q.ptr(70512,70516,1),new Q.ptr(70712,70719,1),new Q.ptr(70722,70724,1),new Q.ptr(70726,70835,109),new Q.ptr(70836,70840,1),new Q.ptr(70842,70847,5),new Q.ptr(70848,70850,2),new Q.ptr(70851,71090,239),new Q.ptr(71091,71093,1),new Q.ptr(71100,71101,1),new Q.ptr(71103,71104,1),new Q.ptr(71132,71133,1),new Q.ptr(71219,71226,1),new Q.ptr(71229,71231,2),new Q.ptr(71232,71339,107),new Q.ptr(71341,71344,3),new Q.ptr(71345,71349,1),new Q.ptr(71351,71453,102),new Q.ptr(71454,71455,1),new Q.ptr(71458,71461,1),new Q.ptr(71463,71467,1),new Q.ptr(72193,72198,1),new Q.ptr(72201,72202,1),new Q.ptr(72243,72248,1),new Q.ptr(72251,72254,1),new Q.ptr(72263,72273,10),new Q.ptr(72274,72278,1),new Q.ptr(72281,72283,1),new Q.ptr(72330,72342,1),new Q.ptr(72344,72345,1),new Q.ptr(72752,72758,1),new Q.ptr(72760,72765,1),new Q.ptr(72767,72850,83),new Q.ptr(72851,72871,1),new Q.ptr(72874,72880,1),new Q.ptr(72882,72883,1),new Q.ptr(72885,72886,1),new Q.ptr(73009,73014,1),new Q.ptr(73018,73020,2),new Q.ptr(73021,73023,2),new Q.ptr(73024,73029,1),new Q.ptr(73031,92912,19881),new Q.ptr(92913,92916,1),new Q.ptr(92976,92982,1),new Q.ptr(94095,94098,1),new Q.ptr(113821,113822,1),new Q.ptr(119143,119145,1),new Q.ptr(119163,119170,1),new Q.ptr(119173,119179,1),new Q.ptr(119210,119213,1),new Q.ptr(119362,119364,1),new Q.ptr(121344,121398,1),new Q.ptr(121403,121452,1),new Q.ptr(121461,121476,15),new Q.ptr(121499,121503,1),new Q.ptr(121505,121519,1),new Q.ptr(122880,122886,1),new Q.ptr(122888,122904,1),new Q.ptr(122907,122913,1),new Q.ptr(122915,122916,1),new Q.ptr(122918,122922,1),new Q.ptr(125136,125142,1),new Q.ptr(125252,125258,1),new Q.ptr(917760,917999,1)]),0);AW=new O.ptr(new IY([new P.ptr(48,57,1),new P.ptr(178,179,1),new P.ptr(185,188,3),new P.ptr(189,190,1),new P.ptr(1632,1641,1),new P.ptr(1776,1785,1),new P.ptr(1984,1993,1),new P.ptr(2406,2415,1),new P.ptr(2534,2543,1),new P.ptr(2548,2553,1),new P.ptr(2662,2671,1),new P.ptr(2790,2799,1),new P.ptr(2918,2927,1),new P.ptr(2930,2935,1),new P.ptr(3046,3058,1),new P.ptr(3174,3183,1),new P.ptr(3192,3198,1),new P.ptr(3302,3311,1),new P.ptr(3416,3422,1),new P.ptr(3430,3448,1),new P.ptr(3558,3567,1),new P.ptr(3664,3673,1),new P.ptr(3792,3801,1),new P.ptr(3872,3891,1),new P.ptr(4160,4169,1),new P.ptr(4240,4249,1),new P.ptr(4969,4988,1),new P.ptr(5870,5872,1),new P.ptr(6112,6121,1),new P.ptr(6128,6137,1),new P.ptr(6160,6169,1),new P.ptr(6470,6479,1),new P.ptr(6608,6618,1),new P.ptr(6784,6793,1),new P.ptr(6800,6809,1),new P.ptr(6992,7001,1),new P.ptr(7088,7097,1),new P.ptr(7232,7241,1),new P.ptr(7248,7257,1),new P.ptr(8304,8308,4),new P.ptr(8309,8313,1),new P.ptr(8320,8329,1),new P.ptr(8528,8578,1),new P.ptr(8581,8585,1),new P.ptr(9312,9371,1),new P.ptr(9450,9471,1),new P.ptr(10102,10131,1),new P.ptr(11517,12295,778),new P.ptr(12321,12329,1),new P.ptr(12344,12346,1),new P.ptr(12690,12693,1),new P.ptr(12832,12841,1),new P.ptr(12872,12879,1),new P.ptr(12881,12895,1),new P.ptr(12928,12937,1),new P.ptr(12977,12991,1),new P.ptr(42528,42537,1),new P.ptr(42726,42735,1),new P.ptr(43056,43061,1),new P.ptr(43216,43225,1),new P.ptr(43264,43273,1),new P.ptr(43472,43481,1),new P.ptr(43504,43513,1),new P.ptr(43600,43609,1),new P.ptr(44016,44025,1),new P.ptr(65296,65305,1)]),new IZ([new Q.ptr(65799,65843,1),new Q.ptr(65856,65912,1),new Q.ptr(65930,65931,1),new Q.ptr(66273,66299,1),new Q.ptr(66336,66339,1),new Q.ptr(66369,66378,9),new Q.ptr(66513,66517,1),new Q.ptr(66720,66729,1),new Q.ptr(67672,67679,1),new Q.ptr(67705,67711,1),new Q.ptr(67751,67759,1),new Q.ptr(67835,67839,1),new Q.ptr(67862,67867,1),new Q.ptr(68028,68029,1),new Q.ptr(68032,68047,1),new Q.ptr(68050,68095,1),new Q.ptr(68160,68167,1),new Q.ptr(68221,68222,1),new Q.ptr(68253,68255,1),new Q.ptr(68331,68335,1),new Q.ptr(68440,68447,1),new Q.ptr(68472,68479,1),new Q.ptr(68521,68527,1),new Q.ptr(68858,68863,1),new Q.ptr(69216,69246,1),new Q.ptr(69714,69743,1),new Q.ptr(69872,69881,1),new Q.ptr(69942,69951,1),new Q.ptr(70096,70105,1),new Q.ptr(70113,70132,1),new Q.ptr(70384,70393,1),new Q.ptr(70736,70745,1),new Q.ptr(70864,70873,1),new Q.ptr(71248,71257,1),new Q.ptr(71360,71369,1),new Q.ptr(71472,71483,1),new Q.ptr(71904,71922,1),new Q.ptr(72784,72812,1),new Q.ptr(73040,73049,1),new Q.ptr(74752,74862,1),new Q.ptr(92768,92777,1),new Q.ptr(93008,93017,1),new Q.ptr(93019,93025,1),new Q.ptr(119648,119665,1),new Q.ptr(120782,120831,1),new Q.ptr(125127,125135,1),new Q.ptr(125264,125273,1),new Q.ptr(127232,127244,1)]),4);AX=new O.ptr(new IY([new P.ptr(48,57,1),new P.ptr(1632,1641,1),new P.ptr(1776,1785,1),new P.ptr(1984,1993,1),new P.ptr(2406,2415,1),new P.ptr(2534,2543,1),new P.ptr(2662,2671,1),new P.ptr(2790,2799,1),new P.ptr(2918,2927,1),new P.ptr(3046,3055,1),new P.ptr(3174,3183,1),new P.ptr(3302,3311,1),new P.ptr(3430,3439,1),new P.ptr(3558,3567,1),new P.ptr(3664,3673,1),new P.ptr(3792,3801,1),new P.ptr(3872,3881,1),new P.ptr(4160,4169,1),new P.ptr(4240,4249,1),new P.ptr(6112,6121,1),new P.ptr(6160,6169,1),new P.ptr(6470,6479,1),new P.ptr(6608,6617,1),new P.ptr(6784,6793,1),new P.ptr(6800,6809,1),new P.ptr(6992,7001,1),new P.ptr(7088,7097,1),new P.ptr(7232,7241,1),new P.ptr(7248,7257,1),new P.ptr(42528,42537,1),new P.ptr(43216,43225,1),new P.ptr(43264,43273,1),new P.ptr(43472,43481,1),new P.ptr(43504,43513,1),new P.ptr(43600,43609,1),new P.ptr(44016,44025,1),new P.ptr(65296,65305,1)]),new IZ([new Q.ptr(66720,66729,1),new Q.ptr(69734,69743,1),new Q.ptr(69872,69881,1),new Q.ptr(69942,69951,1),new Q.ptr(70096,70105,1),new Q.ptr(70384,70393,1),new Q.ptr(70736,70745,1),new Q.ptr(70864,70873,1),new Q.ptr(71248,71257,1),new Q.ptr(71360,71369,1),new Q.ptr(71472,71481,1),new Q.ptr(71904,71913,1),new Q.ptr(72784,72793,1),new Q.ptr(73040,73049,1),new Q.ptr(92768,92777,1),new Q.ptr(93008,93017,1),new Q.ptr(120782,120831,1),new Q.ptr(125264,125273,1)]),1);AY=new O.ptr(new IY([new P.ptr(5870,5872,1),new P.ptr(8544,8578,1),new P.ptr(8581,8584,1),new P.ptr(12295,12321,26),new P.ptr(12322,12329,1),new P.ptr(12344,12346,1),new P.ptr(42726,42735,1)]),new IZ([new Q.ptr(65856,65908,1),new Q.ptr(66369,66378,9),new Q.ptr(66513,66517,1),new Q.ptr(74752,74862,1)]),0);AZ=new O.ptr(new IY([new P.ptr(178,179,1),new P.ptr(185,188,3),new P.ptr(189,190,1),new P.ptr(2548,2553,1),new P.ptr(2930,2935,1),new P.ptr(3056,3058,1),new P.ptr(3192,3198,1),new P.ptr(3416,3422,1),new P.ptr(3440,3448,1),new P.ptr(3882,3891,1),new P.ptr(4969,4988,1),new P.ptr(6128,6137,1),new P.ptr(6618,8304,1686),new P.ptr(8308,8313,1),new P.ptr(8320,8329,1),new P.ptr(8528,8543,1),new P.ptr(8585,9312,727),new P.ptr(9313,9371,1),new P.ptr(9450,9471,1),new P.ptr(10102,10131,1),new P.ptr(11517,12690,1173),new P.ptr(12691,12693,1),new P.ptr(12832,12841,1),new P.ptr(12872,12879,1),new P.ptr(12881,12895,1),new P.ptr(12928,12937,1),new P.ptr(12977,12991,1),new P.ptr(43056,43061,1)]),new IZ([new Q.ptr(65799,65843,1),new Q.ptr(65909,65912,1),new Q.ptr(65930,65931,1),new Q.ptr(66273,66299,1),new Q.ptr(66336,66339,1),new Q.ptr(67672,67679,1),new Q.ptr(67705,67711,1),new Q.ptr(67751,67759,1),new Q.ptr(67835,67839,1),new Q.ptr(67862,67867,1),new Q.ptr(68028,68029,1),new Q.ptr(68032,68047,1),new Q.ptr(68050,68095,1),new Q.ptr(68160,68167,1),new Q.ptr(68221,68222,1),new Q.ptr(68253,68255,1),new Q.ptr(68331,68335,1),new Q.ptr(68440,68447,1),new Q.ptr(68472,68479,1),new Q.ptr(68521,68527,1),new Q.ptr(68858,68863,1),new Q.ptr(69216,69246,1),new Q.ptr(69714,69733,1),new Q.ptr(70113,70132,1),new Q.ptr(71482,71483,1),new Q.ptr(71914,71922,1),new Q.ptr(72794,72812,1),new Q.ptr(93019,93025,1),new Q.ptr(119648,119665,1),new Q.ptr(125127,125135,1),new Q.ptr(127232,127244,1)]),3);BA=new O.ptr(new IY([new P.ptr(33,35,1),new P.ptr(37,42,1),new P.ptr(44,47,1),new P.ptr(58,59,1),new P.ptr(63,64,1),new P.ptr(91,93,1),new P.ptr(95,123,28),new P.ptr(125,161,36),new P.ptr(167,171,4),new P.ptr(182,183,1),new P.ptr(187,191,4),new P.ptr(894,903,9),new P.ptr(1370,1375,1),new P.ptr(1417,1418,1),new P.ptr(1470,1472,2),new P.ptr(1475,1478,3),new P.ptr(1523,1524,1),new P.ptr(1545,1546,1),new P.ptr(1548,1549,1),new P.ptr(1563,1566,3),new P.ptr(1567,1642,75),new P.ptr(1643,1645,1),new P.ptr(1748,1792,44),new P.ptr(1793,1805,1),new P.ptr(2039,2041,1),new P.ptr(2096,2110,1),new P.ptr(2142,2404,262),new P.ptr(2405,2416,11),new P.ptr(2557,2800,243),new P.ptr(3572,3663,91),new P.ptr(3674,3675,1),new P.ptr(3844,3858,1),new P.ptr(3860,3898,38),new P.ptr(3899,3901,1),new P.ptr(3973,4048,75),new P.ptr(4049,4052,1),new P.ptr(4057,4058,1),new P.ptr(4170,4175,1),new P.ptr(4347,4960,613),new P.ptr(4961,4968,1),new P.ptr(5120,5741,621),new P.ptr(5742,5787,45),new P.ptr(5788,5867,79),new P.ptr(5868,5869,1),new P.ptr(5941,5942,1),new P.ptr(6100,6102,1),new P.ptr(6104,6106,1),new P.ptr(6144,6154,1),new P.ptr(6468,6469,1),new P.ptr(6686,6687,1),new P.ptr(6816,6822,1),new P.ptr(6824,6829,1),new P.ptr(7002,7008,1),new P.ptr(7164,7167,1),new P.ptr(7227,7231,1),new P.ptr(7294,7295,1),new P.ptr(7360,7367,1),new P.ptr(7379,8208,829),new P.ptr(8209,8231,1),new P.ptr(8240,8259,1),new P.ptr(8261,8273,1),new P.ptr(8275,8286,1),new P.ptr(8317,8318,1),new P.ptr(8333,8334,1),new P.ptr(8968,8971,1),new P.ptr(9001,9002,1),new P.ptr(10088,10101,1),new P.ptr(10181,10182,1),new P.ptr(10214,10223,1),new P.ptr(10627,10648,1),new P.ptr(10712,10715,1),new P.ptr(10748,10749,1),new P.ptr(11513,11516,1),new P.ptr(11518,11519,1),new P.ptr(11632,11776,144),new P.ptr(11777,11822,1),new P.ptr(11824,11849,1),new P.ptr(12289,12291,1),new P.ptr(12296,12305,1),new P.ptr(12308,12319,1),new P.ptr(12336,12349,13),new P.ptr(12448,12539,91),new P.ptr(42238,42239,1),new P.ptr(42509,42511,1),new P.ptr(42611,42622,11),new P.ptr(42738,42743,1),new P.ptr(43124,43127,1),new P.ptr(43214,43215,1),new P.ptr(43256,43258,1),new P.ptr(43260,43310,50),new P.ptr(43311,43359,48),new P.ptr(43457,43469,1),new P.ptr(43486,43487,1),new P.ptr(43612,43615,1),new P.ptr(43742,43743,1),new P.ptr(43760,43761,1),new P.ptr(44011,64830,20819),new P.ptr(64831,65040,209),new P.ptr(65041,65049,1),new P.ptr(65072,65106,1),new P.ptr(65108,65121,1),new P.ptr(65123,65128,5),new P.ptr(65130,65131,1),new P.ptr(65281,65283,1),new P.ptr(65285,65290,1),new P.ptr(65292,65295,1),new P.ptr(65306,65307,1),new P.ptr(65311,65312,1),new P.ptr(65339,65341,1),new P.ptr(65343,65371,28),new P.ptr(65373,65375,2),new P.ptr(65376,65381,1)]),new IZ([new Q.ptr(65792,65794,1),new Q.ptr(66463,66512,49),new Q.ptr(66927,67671,744),new Q.ptr(67871,67903,32),new Q.ptr(68176,68184,1),new Q.ptr(68223,68336,113),new Q.ptr(68337,68342,1),new Q.ptr(68409,68415,1),new Q.ptr(68505,68508,1),new Q.ptr(69703,69709,1),new Q.ptr(69819,69820,1),new Q.ptr(69822,69825,1),new Q.ptr(69952,69955,1),new Q.ptr(70004,70005,1),new Q.ptr(70085,70089,1),new Q.ptr(70093,70107,14),new Q.ptr(70109,70111,1),new Q.ptr(70200,70205,1),new Q.ptr(70313,70731,418),new Q.ptr(70732,70735,1),new Q.ptr(70747,70749,2),new Q.ptr(70854,71105,251),new Q.ptr(71106,71127,1),new Q.ptr(71233,71235,1),new Q.ptr(71264,71276,1),new Q.ptr(71484,71486,1),new Q.ptr(72255,72262,1),new Q.ptr(72346,72348,1),new Q.ptr(72350,72354,1),new Q.ptr(72769,72773,1),new Q.ptr(72816,72817,1),new Q.ptr(74864,74868,1),new Q.ptr(92782,92783,1),new Q.ptr(92917,92983,66),new Q.ptr(92984,92987,1),new Q.ptr(92996,113823,20827),new Q.ptr(121479,121483,1),new Q.ptr(125278,125279,1)]),11);BB=new O.ptr(new IY([new P.ptr(95,8255,8160),new P.ptr(8256,8276,20),new P.ptr(65075,65076,1),new P.ptr(65101,65103,1),new P.ptr(65343,65343,1)]),IZ.nil,0);BC=new O.ptr(new IY([new P.ptr(45,1418,1373),new P.ptr(1470,5120,3650),new P.ptr(6150,8208,2058),new P.ptr(8209,8213,1),new P.ptr(11799,11802,3),new P.ptr(11834,11835,1),new P.ptr(11840,12316,476),new P.ptr(12336,12448,112),new P.ptr(65073,65074,1),new P.ptr(65112,65123,11),new P.ptr(65293,65293,1)]),IZ.nil,0);BD=new O.ptr(new IY([new P.ptr(41,93,52),new P.ptr(125,3899,3774),new P.ptr(3901,5788,1887),new P.ptr(8262,8318,56),new P.ptr(8334,8969,635),new P.ptr(8971,9002,31),new P.ptr(10089,10101,2),new P.ptr(10182,10215,33),new P.ptr(10217,10223,2),new P.ptr(10628,10648,2),new P.ptr(10713,10715,2),new P.ptr(10749,11811,1062),new P.ptr(11813,11817,2),new P.ptr(12297,12305,2),new P.ptr(12309,12315,2),new P.ptr(12318,12319,1),new P.ptr(64830,65048,218),new P.ptr(65078,65092,2),new P.ptr(65096,65114,18),new P.ptr(65116,65118,2),new P.ptr(65289,65341,52),new P.ptr(65373,65379,3)]),IZ.nil,1);BE=new O.ptr(new IY([new P.ptr(187,8217,8030),new P.ptr(8221,8250,29),new P.ptr(11779,11781,2),new P.ptr(11786,11789,3),new P.ptr(11805,11809,4)]),IZ.nil,0);BF=new O.ptr(new IY([new P.ptr(171,8216,8045),new P.ptr(8219,8220,1),new P.ptr(8223,8249,26),new P.ptr(11778,11780,2),new P.ptr(11785,11788,3),new P.ptr(11804,11808,4)]),IZ.nil,0);BG=new O.ptr(new IY([new P.ptr(33,35,1),new P.ptr(37,39,1),new P.ptr(42,46,2),new P.ptr(47,58,11),new P.ptr(59,63,4),new P.ptr(64,92,28),new P.ptr(161,167,6),new P.ptr(182,183,1),new P.ptr(191,894,703),new P.ptr(903,1370,467),new P.ptr(1371,1375,1),new P.ptr(1417,1472,55),new P.ptr(1475,1478,3),new P.ptr(1523,1524,1),new P.ptr(1545,1546,1),new P.ptr(1548,1549,1),new P.ptr(1563,1566,3),new P.ptr(1567,1642,75),new P.ptr(1643,1645,1),new P.ptr(1748,1792,44),new P.ptr(1793,1805,1),new P.ptr(2039,2041,1),new P.ptr(2096,2110,1),new P.ptr(2142,2404,262),new P.ptr(2405,2416,11),new P.ptr(2557,2800,243),new P.ptr(3572,3663,91),new P.ptr(3674,3675,1),new P.ptr(3844,3858,1),new P.ptr(3860,3973,113),new P.ptr(4048,4052,1),new P.ptr(4057,4058,1),new P.ptr(4170,4175,1),new P.ptr(4347,4960,613),new P.ptr(4961,4968,1),new P.ptr(5741,5742,1),new P.ptr(5867,5869,1),new P.ptr(5941,5942,1),new P.ptr(6100,6102,1),new P.ptr(6104,6106,1),new P.ptr(6144,6149,1),new P.ptr(6151,6154,1),new P.ptr(6468,6469,1),new P.ptr(6686,6687,1),new P.ptr(6816,6822,1),new P.ptr(6824,6829,1),new P.ptr(7002,7008,1),new P.ptr(7164,7167,1),new P.ptr(7227,7231,1),new P.ptr(7294,7295,1),new P.ptr(7360,7367,1),new P.ptr(7379,8214,835),new P.ptr(8215,8224,9),new P.ptr(8225,8231,1),new P.ptr(8240,8248,1),new P.ptr(8251,8254,1),new P.ptr(8257,8259,1),new P.ptr(8263,8273,1),new P.ptr(8275,8277,2),new P.ptr(8278,8286,1),new P.ptr(11513,11516,1),new P.ptr(11518,11519,1),new P.ptr(11632,11776,144),new P.ptr(11777,11782,5),new P.ptr(11783,11784,1),new P.ptr(11787,11790,3),new P.ptr(11791,11798,1),new P.ptr(11800,11801,1),new P.ptr(11803,11806,3),new P.ptr(11807,11818,11),new P.ptr(11819,11822,1),new P.ptr(11824,11833,1),new P.ptr(11836,11839,1),new P.ptr(11841,11843,2),new P.ptr(11844,11849,1),new P.ptr(12289,12291,1),new P.ptr(12349,12539,190),new P.ptr(42238,42239,1),new P.ptr(42509,42511,1),new P.ptr(42611,42622,11),new P.ptr(42738,42743,1),new P.ptr(43124,43127,1),new P.ptr(43214,43215,1),new P.ptr(43256,43258,1),new P.ptr(43260,43310,50),new P.ptr(43311,43359,48),new P.ptr(43457,43469,1),new P.ptr(43486,43487,1),new P.ptr(43612,43615,1),new P.ptr(43742,43743,1),new P.ptr(43760,43761,1),new P.ptr(44011,65040,21029),new P.ptr(65041,65046,1),new P.ptr(65049,65072,23),new P.ptr(65093,65094,1),new P.ptr(65097,65100,1),new P.ptr(65104,65106,1),new P.ptr(65108,65111,1),new P.ptr(65119,65121,1),new P.ptr(65128,65130,2),new P.ptr(65131,65281,150),new P.ptr(65282,65283,1),new P.ptr(65285,65287,1),new P.ptr(65290,65294,2),new P.ptr(65295,65306,11),new P.ptr(65307,65311,4),new P.ptr(65312,65340,28),new P.ptr(65377,65380,3),new P.ptr(65381,65381,1)]),new IZ([new Q.ptr(65792,65792,1),new Q.ptr(65793,65794,1),new Q.ptr(66463,66512,49),new Q.ptr(66927,67671,744),new Q.ptr(67871,67903,32),new Q.ptr(68176,68184,1),new Q.ptr(68223,68336,113),new Q.ptr(68337,68342,1),new Q.ptr(68409,68415,1),new Q.ptr(68505,68508,1),new Q.ptr(69703,69709,1),new Q.ptr(69819,69820,1),new Q.ptr(69822,69825,1),new Q.ptr(69952,69955,1),new Q.ptr(70004,70005,1),new Q.ptr(70085,70089,1),new Q.ptr(70093,70107,14),new Q.ptr(70109,70111,1),new Q.ptr(70200,70205,1),new Q.ptr(70313,70731,418),new Q.ptr(70732,70735,1),new Q.ptr(70747,70749,2),new Q.ptr(70854,71105,251),new Q.ptr(71106,71127,1),new Q.ptr(71233,71235,1),new Q.ptr(71264,71276,1),new Q.ptr(71484,71486,1),new Q.ptr(72255,72262,1),new Q.ptr(72346,72348,1),new Q.ptr(72350,72354,1),new Q.ptr(72769,72773,1),new Q.ptr(72816,72817,1),new Q.ptr(74864,74868,1),new Q.ptr(92782,92783,1),new Q.ptr(92917,92983,66),new Q.ptr(92984,92987,1),new Q.ptr(92996,113823,20827),new Q.ptr(121479,121483,1),new Q.ptr(125278,125279,1)]),8);BH=new O.ptr(new IY([new P.ptr(40,91,51),new P.ptr(123,3898,3775),new P.ptr(3900,5787,1887),new P.ptr(8218,8222,4),new P.ptr(8261,8317,56),new P.ptr(8333,8968,635),new P.ptr(8970,9001,31),new P.ptr(10088,10100,2),new P.ptr(10181,10214,33),new P.ptr(10216,10222,2),new P.ptr(10627,10647,2),new P.ptr(10712,10714,2),new P.ptr(10748,11810,1062),new P.ptr(11812,11816,2),new P.ptr(11842,12296,454),new P.ptr(12298,12304,2),new P.ptr(12308,12314,2),new P.ptr(12317,64831,52514),new P.ptr(65047,65077,30),new P.ptr(65079,65091,2),new P.ptr(65095,65113,18),new P.ptr(65115,65117,2),new P.ptr(65288,65339,51),new P.ptr(65371,65375,4),new P.ptr(65378,65378,1)]),IZ.nil,1);BI=new O.ptr(new IY([new P.ptr(36,43,7),new P.ptr(60,62,1),new P.ptr(94,96,2),new P.ptr(124,126,2),new P.ptr(162,166,1),new P.ptr(168,169,1),new P.ptr(172,174,2),new P.ptr(175,177,1),new P.ptr(180,184,4),new P.ptr(215,247,32),new P.ptr(706,709,1),new P.ptr(722,735,1),new P.ptr(741,747,1),new P.ptr(749,751,2),new P.ptr(752,767,1),new P.ptr(885,900,15),new P.ptr(901,1014,113),new P.ptr(1154,1421,267),new P.ptr(1422,1423,1),new P.ptr(1542,1544,1),new P.ptr(1547,1550,3),new P.ptr(1551,1758,207),new P.ptr(1769,1789,20),new P.ptr(1790,2038,248),new P.ptr(2546,2547,1),new P.ptr(2554,2555,1),new P.ptr(2801,2928,127),new P.ptr(3059,3066,1),new P.ptr(3199,3407,208),new P.ptr(3449,3647,198),new P.ptr(3841,3843,1),new P.ptr(3859,3861,2),new P.ptr(3862,3863,1),new P.ptr(3866,3871,1),new P.ptr(3892,3896,2),new P.ptr(4030,4037,1),new P.ptr(4039,4044,1),new P.ptr(4046,4047,1),new P.ptr(4053,4056,1),new P.ptr(4254,4255,1),new P.ptr(5008,5017,1),new P.ptr(6107,6464,357),new P.ptr(6622,6655,1),new P.ptr(7009,7018,1),new P.ptr(7028,7036,1),new P.ptr(8125,8127,2),new P.ptr(8128,8129,1),new P.ptr(8141,8143,1),new P.ptr(8157,8159,1),new P.ptr(8173,8175,1),new P.ptr(8189,8190,1),new P.ptr(8260,8274,14),new P.ptr(8314,8316,1),new P.ptr(8330,8332,1),new P.ptr(8352,8383,1),new P.ptr(8448,8449,1),new P.ptr(8451,8454,1),new P.ptr(8456,8457,1),new P.ptr(8468,8470,2),new P.ptr(8471,8472,1),new P.ptr(8478,8483,1),new P.ptr(8485,8489,2),new P.ptr(8494,8506,12),new P.ptr(8507,8512,5),new P.ptr(8513,8516,1),new P.ptr(8522,8525,1),new P.ptr(8527,8586,59),new P.ptr(8587,8592,5),new P.ptr(8593,8967,1),new P.ptr(8972,9000,1),new P.ptr(9003,9254,1),new P.ptr(9280,9290,1),new P.ptr(9372,9449,1),new P.ptr(9472,10087,1),new P.ptr(10132,10180,1),new P.ptr(10183,10213,1),new P.ptr(10224,10626,1),new P.ptr(10649,10711,1),new P.ptr(10716,10747,1),new P.ptr(10750,11123,1),new P.ptr(11126,11157,1),new P.ptr(11160,11193,1),new P.ptr(11197,11208,1),new P.ptr(11210,11218,1),new P.ptr(11244,11247,1),new P.ptr(11493,11498,1),new P.ptr(11904,11929,1),new P.ptr(11931,12019,1),new P.ptr(12032,12245,1),new P.ptr(12272,12283,1),new P.ptr(12292,12306,14),new P.ptr(12307,12320,13),new P.ptr(12342,12343,1),new P.ptr(12350,12351,1),new P.ptr(12443,12444,1),new P.ptr(12688,12689,1),new P.ptr(12694,12703,1),new P.ptr(12736,12771,1),new P.ptr(12800,12830,1),new P.ptr(12842,12871,1),new P.ptr(12880,12896,16),new P.ptr(12897,12927,1),new P.ptr(12938,12976,1),new P.ptr(12992,13054,1),new P.ptr(13056,13311,1),new P.ptr(19904,19967,1),new P.ptr(42128,42182,1),new P.ptr(42752,42774,1),new P.ptr(42784,42785,1),new P.ptr(42889,42890,1),new P.ptr(43048,43051,1),new P.ptr(43062,43065,1),new P.ptr(43639,43641,1),new P.ptr(43867,64297,20430),new P.ptr(64434,64449,1),new P.ptr(65020,65021,1),new P.ptr(65122,65124,2),new P.ptr(65125,65126,1),new P.ptr(65129,65284,155),new P.ptr(65291,65308,17),new P.ptr(65309,65310,1),new P.ptr(65342,65344,2),new P.ptr(65372,65374,2),new P.ptr(65504,65510,1),new P.ptr(65512,65518,1),new P.ptr(65532,65533,1)]),new IZ([new Q.ptr(65847,65855,1),new Q.ptr(65913,65929,1),new Q.ptr(65932,65934,1),new Q.ptr(65936,65947,1),new Q.ptr(65952,66000,48),new Q.ptr(66001,66044,1),new Q.ptr(67703,67704,1),new Q.ptr(68296,71487,3191),new Q.ptr(92988,92991,1),new Q.ptr(92997,113820,20823),new Q.ptr(118784,119029,1),new Q.ptr(119040,119078,1),new Q.ptr(119081,119140,1),new Q.ptr(119146,119148,1),new Q.ptr(119171,119172,1),new Q.ptr(119180,119209,1),new Q.ptr(119214,119272,1),new Q.ptr(119296,119361,1),new Q.ptr(119365,119552,187),new Q.ptr(119553,119638,1),new Q.ptr(120513,120539,26),new Q.ptr(120571,120597,26),new Q.ptr(120629,120655,26),new Q.ptr(120687,120713,26),new Q.ptr(120745,120771,26),new Q.ptr(120832,121343,1),new Q.ptr(121399,121402,1),new Q.ptr(121453,121460,1),new Q.ptr(121462,121475,1),new Q.ptr(121477,121478,1),new Q.ptr(126704,126705,1),new Q.ptr(126976,127019,1),new Q.ptr(127024,127123,1),new Q.ptr(127136,127150,1),new Q.ptr(127153,127167,1),new Q.ptr(127169,127183,1),new Q.ptr(127185,127221,1),new Q.ptr(127248,127278,1),new Q.ptr(127280,127339,1),new Q.ptr(127344,127404,1),new Q.ptr(127462,127490,1),new Q.ptr(127504,127547,1),new Q.ptr(127552,127560,1),new Q.ptr(127568,127569,1),new Q.ptr(127584,127589,1),new Q.ptr(127744,128724,1),new Q.ptr(128736,128748,1),new Q.ptr(128752,128760,1),new Q.ptr(128768,128883,1),new Q.ptr(128896,128980,1),new Q.ptr(129024,129035,1),new Q.ptr(129040,129095,1),new Q.ptr(129104,129113,1),new Q.ptr(129120,129159,1),new Q.ptr(129168,129197,1),new Q.ptr(129280,129291,1),new Q.ptr(129296,129342,1),new Q.ptr(129344,129356,1),new Q.ptr(129360,129387,1),new Q.ptr(129408,129431,1),new Q.ptr(129472,129488,16),new Q.ptr(129489,129510,1)]),10);BJ=new O.ptr(new IY([new P.ptr(36,162,126),new P.ptr(163,165,1),new P.ptr(1423,1547,124),new P.ptr(2546,2547,1),new P.ptr(2555,2801,246),new P.ptr(3065,3647,582),new P.ptr(6107,8352,2245),new P.ptr(8353,8383,1),new P.ptr(43064,65020,21956),new P.ptr(65129,65284,155),new P.ptr(65504,65505,1),new P.ptr(65509,65510,1)]),IZ.nil,2);BK=new O.ptr(new IY([new P.ptr(94,96,2),new P.ptr(168,175,7),new P.ptr(180,184,4),new P.ptr(706,709,1),new P.ptr(722,735,1),new P.ptr(741,747,1),new P.ptr(749,751,2),new P.ptr(752,767,1),new P.ptr(885,900,15),new P.ptr(901,8125,7224),new P.ptr(8127,8129,1),new P.ptr(8141,8143,1),new P.ptr(8157,8159,1),new P.ptr(8173,8175,1),new P.ptr(8189,8190,1),new P.ptr(12443,12444,1),new P.ptr(42752,42774,1),new P.ptr(42784,42785,1),new P.ptr(42889,42890,1),new P.ptr(43867,64434,20567),new P.ptr(64435,64449,1),new P.ptr(65342,65344,2),new P.ptr(65507,65507,1)]),new IZ([new Q.ptr(127995,127995,1),new Q.ptr(127996,127999,1)]),3);BL=new O.ptr(new IY([new P.ptr(43,60,17),new P.ptr(61,62,1),new P.ptr(124,126,2),new P.ptr(172,177,5),new P.ptr(215,247,32),new P.ptr(1014,1542,528),new P.ptr(1543,1544,1),new P.ptr(8260,8274,14),new P.ptr(8314,8316,1),new P.ptr(8330,8332,1),new P.ptr(8472,8512,40),new P.ptr(8513,8516,1),new P.ptr(8523,8592,69),new P.ptr(8593,8596,1),new P.ptr(8602,8603,1),new P.ptr(8608,8614,3),new P.ptr(8622,8654,32),new P.ptr(8655,8658,3),new P.ptr(8660,8692,32),new P.ptr(8693,8959,1),new P.ptr(8992,8993,1),new P.ptr(9084,9115,31),new P.ptr(9116,9139,1),new P.ptr(9180,9185,1),new P.ptr(9655,9665,10),new P.ptr(9720,9727,1),new P.ptr(9839,10176,337),new P.ptr(10177,10180,1),new P.ptr(10183,10213,1),new P.ptr(10224,10239,1),new P.ptr(10496,10626,1),new P.ptr(10649,10711,1),new P.ptr(10716,10747,1),new P.ptr(10750,11007,1),new P.ptr(11056,11076,1),new P.ptr(11079,11084,1),new P.ptr(64297,65122,825),new P.ptr(65124,65126,1),new P.ptr(65291,65308,17),new P.ptr(65309,65310,1),new P.ptr(65372,65374,2),new P.ptr(65506,65513,7),new P.ptr(65514,65516,1)]),new IZ([new Q.ptr(120513,120539,26),new Q.ptr(120571,120597,26),new Q.ptr(120629,120655,26),new Q.ptr(120687,120713,26),new Q.ptr(120745,120771,26),new Q.ptr(126704,126705,1)]),5);BM=new O.ptr(new IY([new P.ptr(166,169,3),new P.ptr(174,176,2),new P.ptr(1154,1421,267),new P.ptr(1422,1550,128),new P.ptr(1551,1758,207),new P.ptr(1769,1789,20),new P.ptr(1790,2038,248),new P.ptr(2554,2928,374),new P.ptr(3059,3064,1),new P.ptr(3066,3199,133),new P.ptr(3407,3449,42),new P.ptr(3841,3843,1),new P.ptr(3859,3861,2),new P.ptr(3862,3863,1),new P.ptr(3866,3871,1),new P.ptr(3892,3896,2),new P.ptr(4030,4037,1),new P.ptr(4039,4044,1),new P.ptr(4046,4047,1),new P.ptr(4053,4056,1),new P.ptr(4254,4255,1),new P.ptr(5008,5017,1),new P.ptr(6464,6622,158),new P.ptr(6623,6655,1),new P.ptr(7009,7018,1),new P.ptr(7028,7036,1),new P.ptr(8448,8449,1),new P.ptr(8451,8454,1),new P.ptr(8456,8457,1),new P.ptr(8468,8470,2),new P.ptr(8471,8478,7),new P.ptr(8479,8483,1),new P.ptr(8485,8489,2),new P.ptr(8494,8506,12),new P.ptr(8507,8522,15),new P.ptr(8524,8525,1),new P.ptr(8527,8586,59),new P.ptr(8587,8597,10),new P.ptr(8598,8601,1),new P.ptr(8604,8607,1),new P.ptr(8609,8610,1),new P.ptr(8612,8613,1),new P.ptr(8615,8621,1),new P.ptr(8623,8653,1),new P.ptr(8656,8657,1),new P.ptr(8659,8661,2),new P.ptr(8662,8691,1),new P.ptr(8960,8967,1),new P.ptr(8972,8991,1),new P.ptr(8994,9000,1),new P.ptr(9003,9083,1),new P.ptr(9085,9114,1),new P.ptr(9140,9179,1),new P.ptr(9186,9254,1),new P.ptr(9280,9290,1),new P.ptr(9372,9449,1),new P.ptr(9472,9654,1),new P.ptr(9656,9664,1),new P.ptr(9666,9719,1),new P.ptr(9728,9838,1),new P.ptr(9840,10087,1),new P.ptr(10132,10175,1),new P.ptr(10240,10495,1),new P.ptr(11008,11055,1),new P.ptr(11077,11078,1),new P.ptr(11085,11123,1),new P.ptr(11126,11157,1),new P.ptr(11160,11193,1),new P.ptr(11197,11208,1),new P.ptr(11210,11218,1),new P.ptr(11244,11247,1),new P.ptr(11493,11498,1),new P.ptr(11904,11929,1),new P.ptr(11931,12019,1),new P.ptr(12032,12245,1),new P.ptr(12272,12283,1),new P.ptr(12292,12306,14),new P.ptr(12307,12320,13),new P.ptr(12342,12343,1),new P.ptr(12350,12351,1),new P.ptr(12688,12689,1),new P.ptr(12694,12703,1),new P.ptr(12736,12771,1),new P.ptr(12800,12830,1),new P.ptr(12842,12871,1),new P.ptr(12880,12896,16),new P.ptr(12897,12927,1),new P.ptr(12938,12976,1),new P.ptr(12992,13054,1),new P.ptr(13056,13311,1),new P.ptr(19904,19967,1),new P.ptr(42128,42182,1),new P.ptr(43048,43051,1),new P.ptr(43062,43063,1),new P.ptr(43065,43639,574),new P.ptr(43640,43641,1),new P.ptr(65021,65508,487),new P.ptr(65512,65517,5),new P.ptr(65518,65532,14),new P.ptr(65533,65533,1)]),new IZ([new Q.ptr(65847,65847,1),new Q.ptr(65848,65855,1),new Q.ptr(65913,65929,1),new Q.ptr(65932,65934,1),new Q.ptr(65936,65947,1),new Q.ptr(65952,66000,48),new Q.ptr(66001,66044,1),new Q.ptr(67703,67704,1),new Q.ptr(68296,71487,3191),new Q.ptr(92988,92991,1),new Q.ptr(92997,113820,20823),new Q.ptr(118784,119029,1),new Q.ptr(119040,119078,1),new Q.ptr(119081,119140,1),new Q.ptr(119146,119148,1),new Q.ptr(119171,119172,1),new Q.ptr(119180,119209,1),new Q.ptr(119214,119272,1),new Q.ptr(119296,119361,1),new Q.ptr(119365,119552,187),new Q.ptr(119553,119638,1),new Q.ptr(120832,121343,1),new Q.ptr(121399,121402,1),new Q.ptr(121453,121460,1),new Q.ptr(121462,121475,1),new Q.ptr(121477,121478,1),new Q.ptr(126976,127019,1),new Q.ptr(127024,127123,1),new Q.ptr(127136,127150,1),new Q.ptr(127153,127167,1),new Q.ptr(127169,127183,1),new Q.ptr(127185,127221,1),new Q.ptr(127248,127278,1),new Q.ptr(127280,127339,1),new Q.ptr(127344,127404,1),new Q.ptr(127462,127490,1),new Q.ptr(127504,127547,1),new Q.ptr(127552,127560,1),new Q.ptr(127568,127569,1),new Q.ptr(127584,127589,1),new Q.ptr(127744,127994,1),new Q.ptr(128000,128724,1),new Q.ptr(128736,128748,1),new Q.ptr(128752,128760,1),new Q.ptr(128768,128883,1),new Q.ptr(128896,128980,1),new Q.ptr(129024,129035,1),new Q.ptr(129040,129095,1),new Q.ptr(129104,129113,1),new Q.ptr(129120,129159,1),new Q.ptr(129168,129197,1),new Q.ptr(129280,129291,1),new Q.ptr(129296,129342,1),new Q.ptr(129344,129356,1),new Q.ptr(129360,129387,1),new Q.ptr(129408,129431,1),new Q.ptr(129472,129488,16),new Q.ptr(129489,129510,1)]),2);BN=new O.ptr(new IY([new P.ptr(32,160,128),new P.ptr(5760,8192,2432),new P.ptr(8193,8202,1),new P.ptr(8232,8233,1),new P.ptr(8239,8287,48),new P.ptr(12288,12288,1)]),IZ.nil,1);BO=new O.ptr(new IY([new P.ptr(8232,8232,1)]),IZ.nil,0);BP=new O.ptr(new IY([new P.ptr(8233,8233,1)]),IZ.nil,0);BQ=new O.ptr(new IY([new P.ptr(32,160,128),new P.ptr(5760,8192,2432),new P.ptr(8193,8202,1),new P.ptr(8239,8287,48),new P.ptr(12288,12288,1)]),IZ.nil,1);$pkg.Cc=AI;$pkg.Cf=AJ;$pkg.Co=AK;$pkg.Cs=AL;$pkg.Digit=AX;$pkg.Nd=AX;$pkg.Letter=AM;$pkg.L=AM;$pkg.Lm=AO;$pkg.Lo=AP;$pkg.Ll=AN;$pkg.M=AS;$pkg.Mc=AT;$pkg.Me=AU;$pkg.Mn=AV;$pkg.Nl=AY;$pkg.No=AZ;$pkg.N=AW;$pkg.C=AH;$pkg.Pc=BB;$pkg.Pd=BC;$pkg.Pe=BD;$pkg.Pf=BE;$pkg.Pi=BF;$pkg.Po=BG;$pkg.Ps=BH;$pkg.P=BA;$pkg.Sc=BJ;$pkg.Sk=BK;$pkg.Sm=BL;$pkg.So=BM;$pkg.Z=BN;$pkg.S=BI;$pkg.PrintRanges=new JB([$pkg.L,$pkg.M,$pkg.N,$pkg.P,$pkg.S]);$pkg.Lt=AQ;$pkg.Lu=AR;$pkg.Zl=BO;$pkg.Zp=BP;$pkg.Zs=BQ;$pkg.Categories=$makeMap($String.keyFor,[{k:"C",v:$pkg.C},{k:"Cc",v:$pkg.Cc},{k:"Cf",v:$pkg.Cf},{k:"Co",v:$pkg.Co},{k:"Cs",v:$pkg.Cs},{k:"L",v:$pkg.L},{k:"Ll",v:$pkg.Ll},{k:"Lm",v:$pkg.Lm},{k:"Lo",v:$pkg.Lo},{k:"Lt",v:$pkg.Lt},{k:"Lu",v:$pkg.Lu},{k:"M",v:$pkg.M},{k:"Mc",v:$pkg.Mc},{k:"Me",v:$pkg.Me},{k:"Mn",v:$pkg.Mn},{k:"N",v:$pkg.N},{k:"Nd",v:$pkg.Nd},{k:"Nl",v:$pkg.Nl},{k:"No",v:$pkg.No},{k:"P",v:$pkg.P},{k:"Pc",v:$pkg.Pc},{k:"Pd",v:$pkg.Pd},{k:"Pe",v:$pkg.Pe},{k:"Pf",v:$pkg.Pf},{k:"Pi",v:$pkg.Pi},{k:"Po",v:$pkg.Po},{k:"Ps",v:$pkg.Ps},{k:"S",v:$pkg.S},{k:"Sc",v:$pkg.Sc},{k:"Sk",v:$pkg.Sk},{k:"Sm",v:$pkg.Sm},{k:"So",v:$pkg.So},{k:"Z",v:$pkg.Z},{k:"Zl",v:$pkg.Zl},{k:"Zp",v:$pkg.Zp},{k:"Zs",v:$pkg.Zs}]);BR=new O.ptr(new IY([]),new IZ([new Q.ptr(125184,125258,1),new Q.ptr(125264,125273,1),new Q.ptr(125278,125279,1)]),0);BS=new O.ptr(new IY([]),new IZ([new Q.ptr(71424,71449,1),new Q.ptr(71453,71467,1),new Q.ptr(71472,71487,1)]),0);BT=new O.ptr(new IY([]),new IZ([new Q.ptr(82944,83526,1)]),0);BU=new O.ptr(new IY([new P.ptr(1536,1540,1),new P.ptr(1542,1547,1),new P.ptr(1549,1562,1),new P.ptr(1564,1564,1),new P.ptr(1566,1566,1),new P.ptr(1568,1599,1),new P.ptr(1601,1610,1),new P.ptr(1622,1647,1),new P.ptr(1649,1756,1),new P.ptr(1758,1791,1),new P.ptr(1872,1919,1),new P.ptr(2208,2228,1),new P.ptr(2230,2237,1),new P.ptr(2260,2273,1),new P.ptr(2275,2303,1),new P.ptr(64336,64449,1),new P.ptr(64467,64829,1),new P.ptr(64848,64911,1),new P.ptr(64914,64967,1),new P.ptr(65008,65021,1),new P.ptr(65136,65140,1),new P.ptr(65142,65276,1)]),new IZ([new Q.ptr(69216,69246,1),new Q.ptr(126464,126467,1),new Q.ptr(126469,126495,1),new Q.ptr(126497,126498,1),new Q.ptr(126500,126500,1),new Q.ptr(126503,126503,1),new Q.ptr(126505,126514,1),new Q.ptr(126516,126519,1),new Q.ptr(126521,126521,1),new Q.ptr(126523,126523,1),new Q.ptr(126530,126530,1),new Q.ptr(126535,126535,1),new Q.ptr(126537,126537,1),new Q.ptr(126539,126539,1),new Q.ptr(126541,126543,1),new Q.ptr(126545,126546,1),new Q.ptr(126548,126548,1),new Q.ptr(126551,126551,1),new Q.ptr(126553,126553,1),new Q.ptr(126555,126555,1),new Q.ptr(126557,126557,1),new Q.ptr(126559,126559,1),new Q.ptr(126561,126562,1),new Q.ptr(126564,126564,1),new Q.ptr(126567,126570,1),new Q.ptr(126572,126578,1),new Q.ptr(126580,126583,1),new Q.ptr(126585,126588,1),new Q.ptr(126590,126590,1),new Q.ptr(126592,126601,1),new Q.ptr(126603,126619,1),new Q.ptr(126625,126627,1),new Q.ptr(126629,126633,1),new Q.ptr(126635,126651,1),new Q.ptr(126704,126705,1)]),0);BV=new O.ptr(new IY([new P.ptr(1329,1366,1),new P.ptr(1369,1375,1),new P.ptr(1377,1415,1),new P.ptr(1418,1418,1),new P.ptr(1421,1423,1),new P.ptr(64275,64279,1)]),IZ.nil,0);BW=new O.ptr(new IY([]),new IZ([new Q.ptr(68352,68405,1),new Q.ptr(68409,68415,1)]),0);BX=new O.ptr(new IY([new P.ptr(6912,6987,1),new P.ptr(6992,7036,1)]),IZ.nil,0);BY=new O.ptr(new IY([new P.ptr(42656,42743,1)]),new IZ([new Q.ptr(92160,92728,1)]),0);BZ=new O.ptr(new IY([]),new IZ([new Q.ptr(92880,92909,1),new Q.ptr(92912,92917,1)]),0);CA=new O.ptr(new IY([new P.ptr(7104,7155,1),new P.ptr(7164,7167,1)]),IZ.nil,0);CB=new O.ptr(new IY([new P.ptr(2432,2435,1),new P.ptr(2437,2444,1),new P.ptr(2447,2448,1),new P.ptr(2451,2472,1),new P.ptr(2474,2480,1),new P.ptr(2482,2482,1),new P.ptr(2486,2489,1),new P.ptr(2492,2500,1),new P.ptr(2503,2504,1),new P.ptr(2507,2510,1),new P.ptr(2519,2519,1),new P.ptr(2524,2525,1),new P.ptr(2527,2531,1),new P.ptr(2534,2557,1)]),IZ.nil,0);CC=new O.ptr(new IY([]),new IZ([new Q.ptr(72704,72712,1),new Q.ptr(72714,72758,1),new Q.ptr(72760,72773,1),new Q.ptr(72784,72812,1)]),0);CD=new O.ptr(new IY([new P.ptr(746,747,1),new P.ptr(12549,12590,1),new P.ptr(12704,12730,1)]),IZ.nil,0);CE=new O.ptr(new IY([]),new IZ([new Q.ptr(69632,69709,1),new Q.ptr(69714,69743,1),new Q.ptr(69759,69759,1)]),0);CF=new O.ptr(new IY([new P.ptr(10240,10495,1)]),IZ.nil,0);CG=new O.ptr(new IY([new P.ptr(6656,6683,1),new P.ptr(6686,6687,1)]),IZ.nil,0);CH=new O.ptr(new IY([new P.ptr(5952,5971,1)]),IZ.nil,0);CI=new O.ptr(new IY([new P.ptr(5120,5759,1),new P.ptr(6320,6389,1)]),IZ.nil,0);CJ=new O.ptr(new IY([]),new IZ([new Q.ptr(66208,66256,1)]),0);CK=new O.ptr(new IY([]),new IZ([new Q.ptr(66864,66915,1),new Q.ptr(66927,66927,1)]),0);CL=new O.ptr(new IY([]),new IZ([new Q.ptr(69888,69940,1),new Q.ptr(69942,69955,1)]),0);CM=new O.ptr(new IY([new P.ptr(43520,43574,1),new P.ptr(43584,43597,1),new P.ptr(43600,43609,1),new P.ptr(43612,43615,1)]),IZ.nil,0);CN=new O.ptr(new IY([new P.ptr(5024,5109,1),new P.ptr(5112,5117,1),new P.ptr(43888,43967,1)]),IZ.nil,0);CO=new O.ptr(new IY([new P.ptr(0,64,1),new P.ptr(91,96,1),new P.ptr(123,169,1),new P.ptr(171,185,1),new P.ptr(187,191,1),new P.ptr(215,215,1),new P.ptr(247,247,1),new P.ptr(697,735,1),new P.ptr(741,745,1),new P.ptr(748,767,1),new P.ptr(884,884,1),new P.ptr(894,894,1),new P.ptr(901,901,1),new P.ptr(903,903,1),new P.ptr(1417,1417,1),new P.ptr(1541,1541,1),new P.ptr(1548,1548,1),new P.ptr(1563,1563,1),new P.ptr(1567,1567,1),new P.ptr(1600,1600,1),new P.ptr(1757,1757,1),new P.ptr(2274,2274,1),new P.ptr(2404,2405,1),new P.ptr(3647,3647,1),new P.ptr(4053,4056,1),new P.ptr(4347,4347,1),new P.ptr(5867,5869,1),new P.ptr(5941,5942,1),new P.ptr(6146,6147,1),new P.ptr(6149,6149,1),new P.ptr(7379,7379,1),new P.ptr(7393,7393,1),new P.ptr(7401,7404,1),new P.ptr(7406,7411,1),new P.ptr(7413,7415,1),new P.ptr(8192,8203,1),new P.ptr(8206,8292,1),new P.ptr(8294,8304,1),new P.ptr(8308,8318,1),new P.ptr(8320,8334,1),new P.ptr(8352,8383,1),new P.ptr(8448,8485,1),new P.ptr(8487,8489,1),new P.ptr(8492,8497,1),new P.ptr(8499,8525,1),new P.ptr(8527,8543,1),new P.ptr(8585,8587,1),new P.ptr(8592,9254,1),new P.ptr(9280,9290,1),new P.ptr(9312,10239,1),new P.ptr(10496,11123,1),new P.ptr(11126,11157,1),new P.ptr(11160,11193,1),new P.ptr(11197,11208,1),new P.ptr(11210,11218,1),new P.ptr(11244,11247,1),new P.ptr(11776,11849,1),new P.ptr(12272,12283,1),new P.ptr(12288,12292,1),new P.ptr(12294,12294,1),new P.ptr(12296,12320,1),new P.ptr(12336,12343,1),new P.ptr(12348,12351,1),new P.ptr(12443,12444,1),new P.ptr(12448,12448,1),new P.ptr(12539,12540,1),new P.ptr(12688,12703,1),new P.ptr(12736,12771,1),new P.ptr(12832,12895,1),new P.ptr(12927,13007,1),new P.ptr(13144,13311,1),new P.ptr(19904,19967,1),new P.ptr(42752,42785,1),new P.ptr(42888,42890,1),new P.ptr(43056,43065,1),new P.ptr(43310,43310,1),new P.ptr(43471,43471,1),new P.ptr(43867,43867,1),new P.ptr(64830,64831,1),new P.ptr(65040,65049,1),new P.ptr(65072,65106,1),new P.ptr(65108,65126,1),new P.ptr(65128,65131,1),new P.ptr(65279,65279,1),new P.ptr(65281,65312,1),new P.ptr(65339,65344,1),new P.ptr(65371,65381,1),new P.ptr(65392,65392,1),new P.ptr(65438,65439,1),new P.ptr(65504,65510,1),new P.ptr(65512,65518,1),new P.ptr(65529,65533,1)]),new IZ([new Q.ptr(65792,65794,1),new Q.ptr(65799,65843,1),new Q.ptr(65847,65855,1),new Q.ptr(65936,65947,1),new Q.ptr(66000,66044,1),new Q.ptr(66273,66299,1),new Q.ptr(113824,113827,1),new Q.ptr(118784,119029,1),new Q.ptr(119040,119078,1),new Q.ptr(119081,119142,1),new Q.ptr(119146,119162,1),new Q.ptr(119171,119172,1),new Q.ptr(119180,119209,1),new Q.ptr(119214,119272,1),new Q.ptr(119552,119638,1),new Q.ptr(119648,119665,1),new Q.ptr(119808,119892,1),new Q.ptr(119894,119964,1),new Q.ptr(119966,119967,1),new Q.ptr(119970,119970,1),new Q.ptr(119973,119974,1),new Q.ptr(119977,119980,1),new Q.ptr(119982,119993,1),new Q.ptr(119995,119995,1),new Q.ptr(119997,120003,1),new Q.ptr(120005,120069,1),new Q.ptr(120071,120074,1),new Q.ptr(120077,120084,1),new Q.ptr(120086,120092,1),new Q.ptr(120094,120121,1),new Q.ptr(120123,120126,1),new Q.ptr(120128,120132,1),new Q.ptr(120134,120134,1),new Q.ptr(120138,120144,1),new Q.ptr(120146,120485,1),new Q.ptr(120488,120779,1),new Q.ptr(120782,120831,1),new Q.ptr(126976,127019,1),new Q.ptr(127024,127123,1),new Q.ptr(127136,127150,1),new Q.ptr(127153,127167,1),new Q.ptr(127169,127183,1),new Q.ptr(127185,127221,1),new Q.ptr(127232,127244,1),new Q.ptr(127248,127278,1),new Q.ptr(127280,127339,1),new Q.ptr(127344,127404,1),new Q.ptr(127462,127487,1),new Q.ptr(127489,127490,1),new Q.ptr(127504,127547,1),new Q.ptr(127552,127560,1),new Q.ptr(127568,127569,1),new Q.ptr(127584,127589,1),new Q.ptr(127744,128724,1),new Q.ptr(128736,128748,1),new Q.ptr(128752,128760,1),new Q.ptr(128768,128883,1),new Q.ptr(128896,128980,1),new Q.ptr(129024,129035,1),new Q.ptr(129040,129095,1),new Q.ptr(129104,129113,1),new Q.ptr(129120,129159,1),new Q.ptr(129168,129197,1),new Q.ptr(129280,129291,1),new Q.ptr(129296,129342,1),new Q.ptr(129344,129356,1),new Q.ptr(129360,129387,1),new Q.ptr(129408,129431,1),new Q.ptr(129472,129472,1),new Q.ptr(129488,129510,1),new Q.ptr(917505,917505,1),new Q.ptr(917536,917631,1)]),7);CP=new O.ptr(new IY([new P.ptr(994,1007,1),new P.ptr(11392,11507,1),new P.ptr(11513,11519,1)]),IZ.nil,0);CQ=new O.ptr(new IY([]),new IZ([new Q.ptr(73728,74649,1),new Q.ptr(74752,74862,1),new Q.ptr(74864,74868,1),new Q.ptr(74880,75075,1)]),0);CR=new O.ptr(new IY([]),new IZ([new Q.ptr(67584,67589,1),new Q.ptr(67592,67592,1),new Q.ptr(67594,67637,1),new Q.ptr(67639,67640,1),new Q.ptr(67644,67644,1),new Q.ptr(67647,67647,1)]),0);CS=new O.ptr(new IY([new P.ptr(1024,1156,1),new P.ptr(1159,1327,1),new P.ptr(7296,7304,1),new P.ptr(7467,7467,1),new P.ptr(7544,7544,1),new P.ptr(11744,11775,1),new P.ptr(42560,42655,1),new P.ptr(65070,65071,1)]),IZ.nil,0);CT=new O.ptr(new IY([]),new IZ([new Q.ptr(66560,66639,1)]),0);CU=new O.ptr(new IY([new P.ptr(2304,2384,1),new P.ptr(2387,2403,1),new P.ptr(2406,2431,1),new P.ptr(43232,43261,1)]),IZ.nil,0);CV=new O.ptr(new IY([]),new IZ([new Q.ptr(113664,113770,1),new Q.ptr(113776,113788,1),new Q.ptr(113792,113800,1),new Q.ptr(113808,113817,1),new Q.ptr(113820,113823,1)]),0);CW=new O.ptr(new IY([]),new IZ([new Q.ptr(77824,78894,1)]),0);CX=new O.ptr(new IY([]),new IZ([new Q.ptr(66816,66855,1)]),0);CY=new O.ptr(new IY([new P.ptr(4608,4680,1),new P.ptr(4682,4685,1),new P.ptr(4688,4694,1),new P.ptr(4696,4696,1),new P.ptr(4698,4701,1),new P.ptr(4704,4744,1),new P.ptr(4746,4749,1),new P.ptr(4752,4784,1),new P.ptr(4786,4789,1),new P.ptr(4792,4798,1),new P.ptr(4800,4800,1),new P.ptr(4802,4805,1),new P.ptr(4808,4822,1),new P.ptr(4824,4880,1),new P.ptr(4882,4885,1),new P.ptr(4888,4954,1),new P.ptr(4957,4988,1),new P.ptr(4992,5017,1),new P.ptr(11648,11670,1),new P.ptr(11680,11686,1),new P.ptr(11688,11694,1),new P.ptr(11696,11702,1),new P.ptr(11704,11710,1),new P.ptr(11712,11718,1),new P.ptr(11720,11726,1),new P.ptr(11728,11734,1),new P.ptr(11736,11742,1),new P.ptr(43777,43782,1),new P.ptr(43785,43790,1),new P.ptr(43793,43798,1),new P.ptr(43808,43814,1),new P.ptr(43816,43822,1)]),IZ.nil,0);CZ=new O.ptr(new IY([new P.ptr(4256,4293,1),new P.ptr(4295,4295,1),new P.ptr(4301,4301,1),new P.ptr(4304,4346,1),new P.ptr(4348,4351,1),new P.ptr(11520,11557,1),new P.ptr(11559,11559,1),new P.ptr(11565,11565,1)]),IZ.nil,0);DA=new O.ptr(new IY([new P.ptr(11264,11310,1),new P.ptr(11312,11358,1)]),new IZ([new Q.ptr(122880,122886,1),new Q.ptr(122888,122904,1),new Q.ptr(122907,122913,1),new Q.ptr(122915,122916,1),new Q.ptr(122918,122922,1)]),0);DB=new O.ptr(new IY([]),new IZ([new Q.ptr(66352,66378,1)]),0);DC=new O.ptr(new IY([]),new IZ([new Q.ptr(70400,70403,1),new Q.ptr(70405,70412,1),new Q.ptr(70415,70416,1),new Q.ptr(70419,70440,1),new Q.ptr(70442,70448,1),new Q.ptr(70450,70451,1),new Q.ptr(70453,70457,1),new Q.ptr(70460,70468,1),new Q.ptr(70471,70472,1),new Q.ptr(70475,70477,1),new Q.ptr(70480,70480,1),new Q.ptr(70487,70487,1),new Q.ptr(70493,70499,1),new Q.ptr(70502,70508,1),new Q.ptr(70512,70516,1)]),0);DD=new O.ptr(new IY([new P.ptr(880,883,1),new P.ptr(885,887,1),new P.ptr(890,893,1),new P.ptr(895,895,1),new P.ptr(900,900,1),new P.ptr(902,902,1),new P.ptr(904,906,1),new P.ptr(908,908,1),new P.ptr(910,929,1),new P.ptr(931,993,1),new P.ptr(1008,1023,1),new P.ptr(7462,7466,1),new P.ptr(7517,7521,1),new P.ptr(7526,7530,1),new P.ptr(7615,7615,1),new P.ptr(7936,7957,1),new P.ptr(7960,7965,1),new P.ptr(7968,8005,1),new P.ptr(8008,8013,1),new P.ptr(8016,8023,1),new P.ptr(8025,8025,1),new P.ptr(8027,8027,1),new P.ptr(8029,8029,1),new P.ptr(8031,8061,1),new P.ptr(8064,8116,1),new P.ptr(8118,8132,1),new P.ptr(8134,8147,1),new P.ptr(8150,8155,1),new P.ptr(8157,8175,1),new P.ptr(8178,8180,1),new P.ptr(8182,8190,1),new P.ptr(8486,8486,1),new P.ptr(43877,43877,1)]),new IZ([new Q.ptr(65856,65934,1),new Q.ptr(65952,65952,1),new Q.ptr(119296,119365,1)]),0);DE=new O.ptr(new IY([new P.ptr(2689,2691,1),new P.ptr(2693,2701,1),new P.ptr(2703,2705,1),new P.ptr(2707,2728,1),new P.ptr(2730,2736,1),new P.ptr(2738,2739,1),new P.ptr(2741,2745,1),new P.ptr(2748,2757,1),new P.ptr(2759,2761,1),new P.ptr(2763,2765,1),new P.ptr(2768,2768,1),new P.ptr(2784,2787,1),new P.ptr(2790,2801,1),new P.ptr(2809,2815,1)]),IZ.nil,0);DF=new O.ptr(new IY([new P.ptr(2561,2563,1),new P.ptr(2565,2570,1),new P.ptr(2575,2576,1),new P.ptr(2579,2600,1),new P.ptr(2602,2608,1),new P.ptr(2610,2611,1),new P.ptr(2613,2614,1),new P.ptr(2616,2617,1),new P.ptr(2620,2620,1),new P.ptr(2622,2626,1),new P.ptr(2631,2632,1),new P.ptr(2635,2637,1),new P.ptr(2641,2641,1),new P.ptr(2649,2652,1),new P.ptr(2654,2654,1),new P.ptr(2662,2677,1)]),IZ.nil,0);DG=new O.ptr(new IY([new P.ptr(11904,11929,1),new P.ptr(11931,12019,1),new P.ptr(12032,12245,1),new P.ptr(12293,12293,1),new P.ptr(12295,12295,1),new P.ptr(12321,12329,1),new P.ptr(12344,12347,1),new P.ptr(13312,19893,1),new P.ptr(19968,40938,1),new P.ptr(63744,64109,1),new P.ptr(64112,64217,1)]),new IZ([new Q.ptr(131072,173782,1),new Q.ptr(173824,177972,1),new Q.ptr(177984,178205,1),new Q.ptr(178208,183969,1),new Q.ptr(183984,191456,1),new Q.ptr(194560,195101,1)]),0);DH=new O.ptr(new IY([new P.ptr(4352,4607,1),new P.ptr(12334,12335,1),new P.ptr(12593,12686,1),new P.ptr(12800,12830,1),new P.ptr(12896,12926,1),new P.ptr(43360,43388,1),new P.ptr(44032,55203,1),new P.ptr(55216,55238,1),new P.ptr(55243,55291,1),new P.ptr(65440,65470,1),new P.ptr(65474,65479,1),new P.ptr(65482,65487,1),new P.ptr(65490,65495,1),new P.ptr(65498,65500,1)]),IZ.nil,0);DI=new O.ptr(new IY([new P.ptr(5920,5940,1)]),IZ.nil,0);DJ=new O.ptr(new IY([]),new IZ([new Q.ptr(67808,67826,1),new Q.ptr(67828,67829,1),new Q.ptr(67835,67839,1)]),0);DK=new O.ptr(new IY([new P.ptr(1425,1479,1),new P.ptr(1488,1514,1),new P.ptr(1520,1524,1),new P.ptr(64285,64310,1),new P.ptr(64312,64316,1),new P.ptr(64318,64318,1),new P.ptr(64320,64321,1),new P.ptr(64323,64324,1),new P.ptr(64326,64335,1)]),IZ.nil,0);DL=new O.ptr(new IY([new P.ptr(12353,12438,1),new P.ptr(12445,12447,1)]),new IZ([new Q.ptr(110593,110878,1),new Q.ptr(127488,127488,1)]),0);DM=new O.ptr(new IY([]),new IZ([new Q.ptr(67648,67669,1),new Q.ptr(67671,67679,1)]),0);DN=new O.ptr(new IY([new P.ptr(768,879,1),new P.ptr(1157,1158,1),new P.ptr(1611,1621,1),new P.ptr(1648,1648,1),new P.ptr(2385,2386,1),new P.ptr(6832,6846,1),new P.ptr(7376,7378,1),new P.ptr(7380,7392,1),new P.ptr(7394,7400,1),new P.ptr(7405,7405,1),new P.ptr(7412,7412,1),new P.ptr(7416,7417,1),new P.ptr(7616,7673,1),new P.ptr(7675,7679,1),new P.ptr(8204,8205,1),new P.ptr(8400,8432,1),new P.ptr(12330,12333,1),new P.ptr(12441,12442,1),new P.ptr(65024,65039,1),new P.ptr(65056,65069,1)]),new IZ([new Q.ptr(66045,66045,1),new Q.ptr(66272,66272,1),new Q.ptr(119143,119145,1),new Q.ptr(119163,119170,1),new Q.ptr(119173,119179,1),new Q.ptr(119210,119213,1),new Q.ptr(917760,917999,1)]),0);DO=new O.ptr(new IY([]),new IZ([new Q.ptr(68448,68466,1),new Q.ptr(68472,68479,1)]),0);DP=new O.ptr(new IY([]),new IZ([new Q.ptr(68416,68437,1),new Q.ptr(68440,68447,1)]),0);DQ=new O.ptr(new IY([new P.ptr(43392,43469,1),new P.ptr(43472,43481,1),new P.ptr(43486,43487,1)]),IZ.nil,0);DR=new O.ptr(new IY([]),new IZ([new Q.ptr(69760,69825,1)]),0);DS=new O.ptr(new IY([new P.ptr(3200,3203,1),new P.ptr(3205,3212,1),new P.ptr(3214,3216,1),new P.ptr(3218,3240,1),new P.ptr(3242,3251,1),new P.ptr(3253,3257,1),new P.ptr(3260,3268,1),new P.ptr(3270,3272,1),new P.ptr(3274,3277,1),new P.ptr(3285,3286,1),new P.ptr(3294,3294,1),new P.ptr(3296,3299,1),new P.ptr(3302,3311,1),new P.ptr(3313,3314,1)]),IZ.nil,0);DT=new O.ptr(new IY([new P.ptr(12449,12538,1),new P.ptr(12541,12543,1),new P.ptr(12784,12799,1),new P.ptr(13008,13054,1),new P.ptr(13056,13143,1),new P.ptr(65382,65391,1),new P.ptr(65393,65437,1)]),new IZ([new Q.ptr(110592,110592,1)]),0);DU=new O.ptr(new IY([new P.ptr(43264,43309,1),new P.ptr(43311,43311,1)]),IZ.nil,0);DV=new O.ptr(new IY([]),new IZ([new Q.ptr(68096,68099,1),new Q.ptr(68101,68102,1),new Q.ptr(68108,68115,1),new Q.ptr(68117,68119,1),new Q.ptr(68121,68147,1),new Q.ptr(68152,68154,1),new Q.ptr(68159,68167,1),new Q.ptr(68176,68184,1)]),0);DW=new O.ptr(new IY([new P.ptr(6016,6109,1),new P.ptr(6112,6121,1),new P.ptr(6128,6137,1),new P.ptr(6624,6655,1)]),IZ.nil,0);DX=new O.ptr(new IY([]),new IZ([new Q.ptr(70144,70161,1),new Q.ptr(70163,70206,1)]),0);DY=new O.ptr(new IY([]),new IZ([new Q.ptr(70320,70378,1),new Q.ptr(70384,70393,1)]),0);DZ=new O.ptr(new IY([new P.ptr(3713,3714,1),new P.ptr(3716,3716,1),new P.ptr(3719,3720,1),new P.ptr(3722,3722,1),new P.ptr(3725,3725,1),new P.ptr(3732,3735,1),new P.ptr(3737,3743,1),new P.ptr(3745,3747,1),new P.ptr(3749,3749,1),new P.ptr(3751,3751,1),new P.ptr(3754,3755,1),new P.ptr(3757,3769,1),new P.ptr(3771,3773,1),new P.ptr(3776,3780,1),new P.ptr(3782,3782,1),new P.ptr(3784,3789,1),new P.ptr(3792,3801,1),new P.ptr(3804,3807,1)]),IZ.nil,0);EA=new O.ptr(new IY([new P.ptr(65,90,1),new P.ptr(97,122,1),new P.ptr(170,170,1),new P.ptr(186,186,1),new P.ptr(192,214,1),new P.ptr(216,246,1),new P.ptr(248,696,1),new P.ptr(736,740,1),new P.ptr(7424,7461,1),new P.ptr(7468,7516,1),new P.ptr(7522,7525,1),new P.ptr(7531,7543,1),new P.ptr(7545,7614,1),new P.ptr(7680,7935,1),new P.ptr(8305,8305,1),new P.ptr(8319,8319,1),new P.ptr(8336,8348,1),new P.ptr(8490,8491,1),new P.ptr(8498,8498,1),new P.ptr(8526,8526,1),new P.ptr(8544,8584,1),new P.ptr(11360,11391,1),new P.ptr(42786,42887,1),new P.ptr(42891,42926,1),new P.ptr(42928,42935,1),new P.ptr(42999,43007,1),new P.ptr(43824,43866,1),new P.ptr(43868,43876,1),new P.ptr(64256,64262,1),new P.ptr(65313,65338,1),new P.ptr(65345,65370,1)]),IZ.nil,6);EB=new O.ptr(new IY([new P.ptr(7168,7223,1),new P.ptr(7227,7241,1),new P.ptr(7245,7247,1)]),IZ.nil,0);EC=new O.ptr(new IY([new P.ptr(6400,6430,1),new P.ptr(6432,6443,1),new P.ptr(6448,6459,1),new P.ptr(6464,6464,1),new P.ptr(6468,6479,1)]),IZ.nil,0);ED=new O.ptr(new IY([]),new IZ([new Q.ptr(67072,67382,1),new Q.ptr(67392,67413,1),new Q.ptr(67424,67431,1)]),0);EE=new O.ptr(new IY([]),new IZ([new Q.ptr(65536,65547,1),new Q.ptr(65549,65574,1),new Q.ptr(65576,65594,1),new Q.ptr(65596,65597,1),new Q.ptr(65599,65613,1),new Q.ptr(65616,65629,1),new Q.ptr(65664,65786,1)]),0);EF=new O.ptr(new IY([new P.ptr(42192,42239,1)]),IZ.nil,0);EG=new O.ptr(new IY([]),new IZ([new Q.ptr(66176,66204,1)]),0);EH=new O.ptr(new IY([]),new IZ([new Q.ptr(67872,67897,1),new Q.ptr(67903,67903,1)]),0);EI=new O.ptr(new IY([]),new IZ([new Q.ptr(69968,70006,1)]),0);EJ=new O.ptr(new IY([new P.ptr(3328,3331,1),new P.ptr(3333,3340,1),new P.ptr(3342,3344,1),new P.ptr(3346,3396,1),new P.ptr(3398,3400,1),new P.ptr(3402,3407,1),new P.ptr(3412,3427,1),new P.ptr(3430,3455,1)]),IZ.nil,0);EK=new O.ptr(new IY([new P.ptr(2112,2139,1),new P.ptr(2142,2142,1)]),IZ.nil,0);EL=new O.ptr(new IY([]),new IZ([new Q.ptr(68288,68326,1),new Q.ptr(68331,68342,1)]),0);EM=new O.ptr(new IY([]),new IZ([new Q.ptr(72816,72847,1),new Q.ptr(72850,72871,1),new Q.ptr(72873,72886,1)]),0);EN=new O.ptr(new IY([]),new IZ([new Q.ptr(72960,72966,1),new Q.ptr(72968,72969,1),new Q.ptr(72971,73014,1),new Q.ptr(73018,73018,1),new Q.ptr(73020,73021,1),new Q.ptr(73023,73031,1),new Q.ptr(73040,73049,1)]),0);EO=new O.ptr(new IY([new P.ptr(43744,43766,1),new P.ptr(43968,44013,1),new P.ptr(44016,44025,1)]),IZ.nil,0);EP=new O.ptr(new IY([]),new IZ([new Q.ptr(124928,125124,1),new Q.ptr(125127,125142,1)]),0);EQ=new O.ptr(new IY([]),new IZ([new Q.ptr(68000,68023,1),new Q.ptr(68028,68047,1),new Q.ptr(68050,68095,1)]),0);ER=new O.ptr(new IY([]),new IZ([new Q.ptr(67968,67999,1)]),0);ES=new O.ptr(new IY([]),new IZ([new Q.ptr(93952,94020,1),new Q.ptr(94032,94078,1),new Q.ptr(94095,94111,1)]),0);ET=new O.ptr(new IY([]),new IZ([new Q.ptr(71168,71236,1),new Q.ptr(71248,71257,1)]),0);EU=new O.ptr(new IY([new P.ptr(6144,6145,1),new P.ptr(6148,6148,1),new P.ptr(6150,6158,1),new P.ptr(6160,6169,1),new P.ptr(6176,6263,1),new P.ptr(6272,6314,1)]),new IZ([new Q.ptr(71264,71276,1)]),0);EV=new O.ptr(new IY([]),new IZ([new Q.ptr(92736,92766,1),new Q.ptr(92768,92777,1),new Q.ptr(92782,92783,1)]),0);EW=new O.ptr(new IY([]),new IZ([new Q.ptr(70272,70278,1),new Q.ptr(70280,70280,1),new Q.ptr(70282,70285,1),new Q.ptr(70287,70301,1),new Q.ptr(70303,70313,1)]),0);EX=new O.ptr(new IY([new P.ptr(4096,4255,1),new P.ptr(43488,43518,1),new P.ptr(43616,43647,1)]),IZ.nil,0);EY=new O.ptr(new IY([]),new IZ([new Q.ptr(67712,67742,1),new Q.ptr(67751,67759,1)]),0);EZ=new O.ptr(new IY([new P.ptr(6528,6571,1),new P.ptr(6576,6601,1),new P.ptr(6608,6618,1),new P.ptr(6622,6623,1)]),IZ.nil,0);FA=new O.ptr(new IY([]),new IZ([new Q.ptr(70656,70745,1),new Q.ptr(70747,70747,1),new Q.ptr(70749,70749,1)]),0);FB=new O.ptr(new IY([new P.ptr(1984,2042,1)]),IZ.nil,0);FC=new O.ptr(new IY([]),new IZ([new Q.ptr(94177,94177,1),new Q.ptr(110960,111355,1)]),0);FD=new O.ptr(new IY([new P.ptr(5760,5788,1)]),IZ.nil,0);FE=new O.ptr(new IY([new P.ptr(7248,7295,1)]),IZ.nil,0);FF=new O.ptr(new IY([]),new IZ([new Q.ptr(68736,68786,1),new Q.ptr(68800,68850,1),new Q.ptr(68858,68863,1)]),0);FG=new O.ptr(new IY([]),new IZ([new Q.ptr(66304,66339,1),new Q.ptr(66349,66351,1)]),0);FH=new O.ptr(new IY([]),new IZ([new Q.ptr(68224,68255,1)]),0);FI=new O.ptr(new IY([]),new IZ([new Q.ptr(66384,66426,1)]),0);FJ=new O.ptr(new IY([]),new IZ([new Q.ptr(66464,66499,1),new Q.ptr(66504,66517,1)]),0);FK=new O.ptr(new IY([]),new IZ([new Q.ptr(68192,68223,1)]),0);FL=new O.ptr(new IY([]),new IZ([new Q.ptr(68608,68680,1)]),0);FM=new O.ptr(new IY([new P.ptr(2817,2819,1),new P.ptr(2821,2828,1),new P.ptr(2831,2832,1),new P.ptr(2835,2856,1),new P.ptr(2858,2864,1),new P.ptr(2866,2867,1),new P.ptr(2869,2873,1),new P.ptr(2876,2884,1),new P.ptr(2887,2888,1),new P.ptr(2891,2893,1),new P.ptr(2902,2903,1),new P.ptr(2908,2909,1),new P.ptr(2911,2915,1),new P.ptr(2918,2935,1)]),IZ.nil,0);FN=new O.ptr(new IY([]),new IZ([new Q.ptr(66736,66771,1),new Q.ptr(66776,66811,1)]),0);FO=new O.ptr(new IY([]),new IZ([new Q.ptr(66688,66717,1),new Q.ptr(66720,66729,1)]),0);FP=new O.ptr(new IY([]),new IZ([new Q.ptr(92928,92997,1),new Q.ptr(93008,93017,1),new Q.ptr(93019,93025,1),new Q.ptr(93027,93047,1),new Q.ptr(93053,93071,1)]),0);FQ=new O.ptr(new IY([]),new IZ([new Q.ptr(67680,67711,1)]),0);FR=new O.ptr(new IY([]),new IZ([new Q.ptr(72384,72440,1)]),0);FS=new O.ptr(new IY([new P.ptr(43072,43127,1)]),IZ.nil,0);FT=new O.ptr(new IY([]),new IZ([new Q.ptr(67840,67867,1),new Q.ptr(67871,67871,1)]),0);FU=new O.ptr(new IY([]),new IZ([new Q.ptr(68480,68497,1),new Q.ptr(68505,68508,1),new Q.ptr(68521,68527,1)]),0);FV=new O.ptr(new IY([new P.ptr(43312,43347,1),new P.ptr(43359,43359,1)]),IZ.nil,0);FW=new O.ptr(new IY([new P.ptr(5792,5866,1),new P.ptr(5870,5880,1)]),IZ.nil,0);FX=new O.ptr(new IY([new P.ptr(2048,2093,1),new P.ptr(2096,2110,1)]),IZ.nil,0);FY=new O.ptr(new IY([new P.ptr(43136,43205,1),new P.ptr(43214,43225,1)]),IZ.nil,0);FZ=new O.ptr(new IY([]),new IZ([new Q.ptr(70016,70093,1),new Q.ptr(70096,70111,1)]),0);GA=new O.ptr(new IY([]),new IZ([new Q.ptr(66640,66687,1)]),0);GB=new O.ptr(new IY([]),new IZ([new Q.ptr(71040,71093,1),new Q.ptr(71096,71133,1)]),0);GC=new O.ptr(new IY([]),new IZ([new Q.ptr(120832,121483,1),new Q.ptr(121499,121503,1),new Q.ptr(121505,121519,1)]),0);GD=new O.ptr(new IY([new P.ptr(3458,3459,1),new P.ptr(3461,3478,1),new P.ptr(3482,3505,1),new P.ptr(3507,3515,1),new P.ptr(3517,3517,1),new P.ptr(3520,3526,1),new P.ptr(3530,3530,1),new P.ptr(3535,3540,1),new P.ptr(3542,3542,1),new P.ptr(3544,3551,1),new P.ptr(3558,3567,1),new P.ptr(3570,3572,1)]),new IZ([new Q.ptr(70113,70132,1)]),0);GE=new O.ptr(new IY([]),new IZ([new Q.ptr(69840,69864,1),new Q.ptr(69872,69881,1)]),0);GF=new O.ptr(new IY([]),new IZ([new Q.ptr(72272,72323,1),new Q.ptr(72326,72348,1),new Q.ptr(72350,72354,1)]),0);GG=new O.ptr(new IY([new P.ptr(7040,7103,1),new P.ptr(7360,7367,1)]),IZ.nil,0);GH=new O.ptr(new IY([new P.ptr(43008,43051,1)]),IZ.nil,0);GI=new O.ptr(new IY([new P.ptr(1792,1805,1),new P.ptr(1807,1866,1),new P.ptr(1869,1871,1),new P.ptr(2144,2154,1)]),IZ.nil,0);GJ=new O.ptr(new IY([new P.ptr(5888,5900,1),new P.ptr(5902,5908,1)]),IZ.nil,0);GK=new O.ptr(new IY([new P.ptr(5984,5996,1),new P.ptr(5998,6000,1),new P.ptr(6002,6003,1)]),IZ.nil,0);GL=new O.ptr(new IY([new P.ptr(6480,6509,1),new P.ptr(6512,6516,1)]),IZ.nil,0);GM=new O.ptr(new IY([new P.ptr(6688,6750,1),new P.ptr(6752,6780,1),new P.ptr(6783,6793,1),new P.ptr(6800,6809,1),new P.ptr(6816,6829,1)]),IZ.nil,0);GN=new O.ptr(new IY([new P.ptr(43648,43714,1),new P.ptr(43739,43743,1)]),IZ.nil,0);GO=new O.ptr(new IY([]),new IZ([new Q.ptr(71296,71351,1),new Q.ptr(71360,71369,1)]),0);GP=new O.ptr(new IY([new P.ptr(2946,2947,1),new P.ptr(2949,2954,1),new P.ptr(2958,2960,1),new P.ptr(2962,2965,1),new P.ptr(2969,2970,1),new P.ptr(2972,2972,1),new P.ptr(2974,2975,1),new P.ptr(2979,2980,1),new P.ptr(2984,2986,1),new P.ptr(2990,3001,1),new P.ptr(3006,3010,1),new P.ptr(3014,3016,1),new P.ptr(3018,3021,1),new P.ptr(3024,3024,1),new P.ptr(3031,3031,1),new P.ptr(3046,3066,1)]),IZ.nil,0);GQ=new O.ptr(new IY([]),new IZ([new Q.ptr(94176,94176,1),new Q.ptr(94208,100332,1),new Q.ptr(100352,101106,1)]),0);GR=new O.ptr(new IY([new P.ptr(3072,3075,1),new P.ptr(3077,3084,1),new P.ptr(3086,3088,1),new P.ptr(3090,3112,1),new P.ptr(3114,3129,1),new P.ptr(3133,3140,1),new P.ptr(3142,3144,1),new P.ptr(3146,3149,1),new P.ptr(3157,3158,1),new P.ptr(3160,3162,1),new P.ptr(3168,3171,1),new P.ptr(3174,3183,1),new P.ptr(3192,3199,1)]),IZ.nil,0);GS=new O.ptr(new IY([new P.ptr(1920,1969,1)]),IZ.nil,0);GT=new O.ptr(new IY([new P.ptr(3585,3642,1),new P.ptr(3648,3675,1)]),IZ.nil,0);GU=new O.ptr(new IY([new P.ptr(3840,3911,1),new P.ptr(3913,3948,1),new P.ptr(3953,3991,1),new P.ptr(3993,4028,1),new P.ptr(4030,4044,1),new P.ptr(4046,4052,1),new P.ptr(4057,4058,1)]),IZ.nil,0);GV=new O.ptr(new IY([new P.ptr(11568,11623,1),new P.ptr(11631,11632,1),new P.ptr(11647,11647,1)]),IZ.nil,0);GW=new O.ptr(new IY([]),new IZ([new Q.ptr(70784,70855,1),new Q.ptr(70864,70873,1)]),0);GX=new O.ptr(new IY([]),new IZ([new Q.ptr(66432,66461,1),new Q.ptr(66463,66463,1)]),0);GY=new O.ptr(new IY([new P.ptr(42240,42539,1)]),IZ.nil,0);GZ=new O.ptr(new IY([]),new IZ([new Q.ptr(71840,71922,1),new Q.ptr(71935,71935,1)]),0);HA=new O.ptr(new IY([new P.ptr(40960,42124,1),new P.ptr(42128,42182,1)]),IZ.nil,0);HB=new O.ptr(new IY([]),new IZ([new Q.ptr(72192,72263,1)]),0);$pkg.Adlam=BR;$pkg.Ahom=BS;$pkg.Anatolian_Hieroglyphs=BT;$pkg.Arabic=BU;$pkg.Armenian=BV;$pkg.Avestan=BW;$pkg.Balinese=BX;$pkg.Bamum=BY;$pkg.Bassa_Vah=BZ;$pkg.Batak=CA;$pkg.Bengali=CB;$pkg.Bhaiksuki=CC;$pkg.Bopomofo=CD;$pkg.Brahmi=CE;$pkg.Braille=CF;$pkg.Buginese=CG;$pkg.Buhid=CH;$pkg.Canadian_Aboriginal=CI;$pkg.Carian=CJ;$pkg.Caucasian_Albanian=CK;$pkg.Chakma=CL;$pkg.Cham=CM;$pkg.Cherokee=CN;$pkg.Common=CO;$pkg.Coptic=CP;$pkg.Cuneiform=CQ;$pkg.Cypriot=CR;$pkg.Cyrillic=CS;$pkg.Deseret=CT;$pkg.Devanagari=CU;$pkg.Duployan=CV;$pkg.Egyptian_Hieroglyphs=CW;$pkg.Elbasan=CX;$pkg.Ethiopic=CY;$pkg.Georgian=CZ;$pkg.Glagolitic=DA;$pkg.Gothic=DB;$pkg.Grantha=DC;$pkg.Greek=DD;$pkg.Gujarati=DE;$pkg.Gurmukhi=DF;$pkg.Han=DG;$pkg.Hangul=DH;$pkg.Hanunoo=DI;$pkg.Hatran=DJ;$pkg.Hebrew=DK;$pkg.Hiragana=DL;$pkg.Imperial_Aramaic=DM;$pkg.Inherited=DN;$pkg.Inscriptional_Pahlavi=DO;$pkg.Inscriptional_Parthian=DP;$pkg.Javanese=DQ;$pkg.Kaithi=DR;$pkg.Kannada=DS;$pkg.Katakana=DT;$pkg.Kayah_Li=DU;$pkg.Kharoshthi=DV;$pkg.Khmer=DW;$pkg.Khojki=DX;$pkg.Khudawadi=DY;$pkg.Lao=DZ;$pkg.Latin=EA;$pkg.Lepcha=EB;$pkg.Limbu=EC;$pkg.Linear_A=ED;$pkg.Linear_B=EE;$pkg.Lisu=EF;$pkg.Lycian=EG;$pkg.Lydian=EH;$pkg.Mahajani=EI;$pkg.Malayalam=EJ;$pkg.Mandaic=EK;$pkg.Manichaean=EL;$pkg.Marchen=EM;$pkg.Masaram_Gondi=EN;$pkg.Meetei_Mayek=EO;$pkg.Mende_Kikakui=EP;$pkg.Meroitic_Cursive=EQ;$pkg.Meroitic_Hieroglyphs=ER;$pkg.Miao=ES;$pkg.Modi=ET;$pkg.Mongolian=EU;$pkg.Mro=EV;$pkg.Multani=EW;$pkg.Myanmar=EX;$pkg.Nabataean=EY;$pkg.New_Tai_Lue=EZ;$pkg.Newa=FA;$pkg.Nko=FB;$pkg.Nushu=FC;$pkg.Ogham=FD;$pkg.Ol_Chiki=FE;$pkg.Old_Hungarian=FF;$pkg.Old_Italic=FG;$pkg.Old_North_Arabian=FH;$pkg.Old_Permic=FI;$pkg.Old_Persian=FJ;$pkg.Old_South_Arabian=FK;$pkg.Old_Turkic=FL;$pkg.Oriya=FM;$pkg.Osage=FN;$pkg.Osmanya=FO;$pkg.Pahawh_Hmong=FP;$pkg.Palmyrene=FQ;$pkg.Pau_Cin_Hau=FR;$pkg.Phags_Pa=FS;$pkg.Phoenician=FT;$pkg.Psalter_Pahlavi=FU;$pkg.Rejang=FV;$pkg.Runic=FW;$pkg.Samaritan=FX;$pkg.Saurashtra=FY;$pkg.Sharada=FZ;$pkg.Shavian=GA;$pkg.Siddham=GB;$pkg.SignWriting=GC;$pkg.Sinhala=GD;$pkg.Sora_Sompeng=GE;$pkg.Soyombo=GF;$pkg.Sundanese=GG;$pkg.Syloti_Nagri=GH;$pkg.Syriac=GI;$pkg.Tagalog=GJ;$pkg.Tagbanwa=GK;$pkg.Tai_Le=GL;$pkg.Tai_Tham=GM;$pkg.Tai_Viet=GN;$pkg.Takri=GO;$pkg.Tamil=GP;$pkg.Tangut=GQ;$pkg.Telugu=GR;$pkg.Thaana=GS;$pkg.Thai=GT;$pkg.Tibetan=GU;$pkg.Tifinagh=GV;$pkg.Tirhuta=GW;$pkg.Ugaritic=GX;$pkg.Vai=GY;$pkg.Warang_Citi=GZ;$pkg.Yi=HA;$pkg.Zanabazar_Square=HB;$pkg.Scripts=$makeMap($String.keyFor,[{k:"Adlam",v:$pkg.Adlam},{k:"Ahom",v:$pkg.Ahom},{k:"Anatolian_Hieroglyphs",v:$pkg.Anatolian_Hieroglyphs},{k:"Arabic",v:$pkg.Arabic},{k:"Armenian",v:$pkg.Armenian},{k:"Avestan",v:$pkg.Avestan},{k:"Balinese",v:$pkg.Balinese},{k:"Bamum",v:$pkg.Bamum},{k:"Bassa_Vah",v:$pkg.Bassa_Vah},{k:"Batak",v:$pkg.Batak},{k:"Bengali",v:$pkg.Bengali},{k:"Bhaiksuki",v:$pkg.Bhaiksuki},{k:"Bopomofo",v:$pkg.Bopomofo},{k:"Brahmi",v:$pkg.Brahmi},{k:"Braille",v:$pkg.Braille},{k:"Buginese",v:$pkg.Buginese},{k:"Buhid",v:$pkg.Buhid},{k:"Canadian_Aboriginal",v:$pkg.Canadian_Aboriginal},{k:"Carian",v:$pkg.Carian},{k:"Caucasian_Albanian",v:$pkg.Caucasian_Albanian},{k:"Chakma",v:$pkg.Chakma},{k:"Cham",v:$pkg.Cham},{k:"Cherokee",v:$pkg.Cherokee},{k:"Common",v:$pkg.Common},{k:"Coptic",v:$pkg.Coptic},{k:"Cuneiform",v:$pkg.Cuneiform},{k:"Cypriot",v:$pkg.Cypriot},{k:"Cyrillic",v:$pkg.Cyrillic},{k:"Deseret",v:$pkg.Deseret},{k:"Devanagari",v:$pkg.Devanagari},{k:"Duployan",v:$pkg.Duployan},{k:"Egyptian_Hieroglyphs",v:$pkg.Egyptian_Hieroglyphs},{k:"Elbasan",v:$pkg.Elbasan},{k:"Ethiopic",v:$pkg.Ethiopic},{k:"Georgian",v:$pkg.Georgian},{k:"Glagolitic",v:$pkg.Glagolitic},{k:"Gothic",v:$pkg.Gothic},{k:"Grantha",v:$pkg.Grantha},{k:"Greek",v:$pkg.Greek},{k:"Gujarati",v:$pkg.Gujarati},{k:"Gurmukhi",v:$pkg.Gurmukhi},{k:"Han",v:$pkg.Han},{k:"Hangul",v:$pkg.Hangul},{k:"Hanunoo",v:$pkg.Hanunoo},{k:"Hatran",v:$pkg.Hatran},{k:"Hebrew",v:$pkg.Hebrew},{k:"Hiragana",v:$pkg.Hiragana},{k:"Imperial_Aramaic",v:$pkg.Imperial_Aramaic},{k:"Inherited",v:$pkg.Inherited},{k:"Inscriptional_Pahlavi",v:$pkg.Inscriptional_Pahlavi},{k:"Inscriptional_Parthian",v:$pkg.Inscriptional_Parthian},{k:"Javanese",v:$pkg.Javanese},{k:"Kaithi",v:$pkg.Kaithi},{k:"Kannada",v:$pkg.Kannada},{k:"Katakana",v:$pkg.Katakana},{k:"Kayah_Li",v:$pkg.Kayah_Li},{k:"Kharoshthi",v:$pkg.Kharoshthi},{k:"Khmer",v:$pkg.Khmer},{k:"Khojki",v:$pkg.Khojki},{k:"Khudawadi",v:$pkg.Khudawadi},{k:"Lao",v:$pkg.Lao},{k:"Latin",v:$pkg.Latin},{k:"Lepcha",v:$pkg.Lepcha},{k:"Limbu",v:$pkg.Limbu},{k:"Linear_A",v:$pkg.Linear_A},{k:"Linear_B",v:$pkg.Linear_B},{k:"Lisu",v:$pkg.Lisu},{k:"Lycian",v:$pkg.Lycian},{k:"Lydian",v:$pkg.Lydian},{k:"Mahajani",v:$pkg.Mahajani},{k:"Malayalam",v:$pkg.Malayalam},{k:"Mandaic",v:$pkg.Mandaic},{k:"Manichaean",v:$pkg.Manichaean},{k:"Marchen",v:$pkg.Marchen},{k:"Masaram_Gondi",v:$pkg.Masaram_Gondi},{k:"Meetei_Mayek",v:$pkg.Meetei_Mayek},{k:"Mende_Kikakui",v:$pkg.Mende_Kikakui},{k:"Meroitic_Cursive",v:$pkg.Meroitic_Cursive},{k:"Meroitic_Hieroglyphs",v:$pkg.Meroitic_Hieroglyphs},{k:"Miao",v:$pkg.Miao},{k:"Modi",v:$pkg.Modi},{k:"Mongolian",v:$pkg.Mongolian},{k:"Mro",v:$pkg.Mro},{k:"Multani",v:$pkg.Multani},{k:"Myanmar",v:$pkg.Myanmar},{k:"Nabataean",v:$pkg.Nabataean},{k:"New_Tai_Lue",v:$pkg.New_Tai_Lue},{k:"Newa",v:$pkg.Newa},{k:"Nko",v:$pkg.Nko},{k:"Nushu",v:$pkg.Nushu},{k:"Ogham",v:$pkg.Ogham},{k:"Ol_Chiki",v:$pkg.Ol_Chiki},{k:"Old_Hungarian",v:$pkg.Old_Hungarian},{k:"Old_Italic",v:$pkg.Old_Italic},{k:"Old_North_Arabian",v:$pkg.Old_North_Arabian},{k:"Old_Permic",v:$pkg.Old_Permic},{k:"Old_Persian",v:$pkg.Old_Persian},{k:"Old_South_Arabian",v:$pkg.Old_South_Arabian},{k:"Old_Turkic",v:$pkg.Old_Turkic},{k:"Oriya",v:$pkg.Oriya},{k:"Osage",v:$pkg.Osage},{k:"Osmanya",v:$pkg.Osmanya},{k:"Pahawh_Hmong",v:$pkg.Pahawh_Hmong},{k:"Palmyrene",v:$pkg.Palmyrene},{k:"Pau_Cin_Hau",v:$pkg.Pau_Cin_Hau},{k:"Phags_Pa",v:$pkg.Phags_Pa},{k:"Phoenician",v:$pkg.Phoenician},{k:"Psalter_Pahlavi",v:$pkg.Psalter_Pahlavi},{k:"Rejang",v:$pkg.Rejang},{k:"Runic",v:$pkg.Runic},{k:"Samaritan",v:$pkg.Samaritan},{k:"Saurashtra",v:$pkg.Saurashtra},{k:"Sharada",v:$pkg.Sharada},{k:"Shavian",v:$pkg.Shavian},{k:"Siddham",v:$pkg.Siddham},{k:"SignWriting",v:$pkg.SignWriting},{k:"Sinhala",v:$pkg.Sinhala},{k:"Sora_Sompeng",v:$pkg.Sora_Sompeng},{k:"Soyombo",v:$pkg.Soyombo},{k:"Sundanese",v:$pkg.Sundanese},{k:"Syloti_Nagri",v:$pkg.Syloti_Nagri},{k:"Syriac",v:$pkg.Syriac},{k:"Tagalog",v:$pkg.Tagalog},{k:"Tagbanwa",v:$pkg.Tagbanwa},{k:"Tai_Le",v:$pkg.Tai_Le},{k:"Tai_Tham",v:$pkg.Tai_Tham},{k:"Tai_Viet",v:$pkg.Tai_Viet},{k:"Takri",v:$pkg.Takri},{k:"Tamil",v:$pkg.Tamil},{k:"Tangut",v:$pkg.Tangut},{k:"Telugu",v:$pkg.Telugu},{k:"Thaana",v:$pkg.Thaana},{k:"Thai",v:$pkg.Thai},{k:"Tibetan",v:$pkg.Tibetan},{k:"Tifinagh",v:$pkg.Tifinagh},{k:"Tirhuta",v:$pkg.Tirhuta},{k:"Ugaritic",v:$pkg.Ugaritic},{k:"Vai",v:$pkg.Vai},{k:"Warang_Citi",v:$pkg.Warang_Citi},{k:"Yi",v:$pkg.Yi},{k:"Zanabazar_Square",v:$pkg.Zanabazar_Square}]);IK=new JC([new R.ptr(65,90,$toNativeArray($kindInt32,[0,32,0])),new R.ptr(97,122,$toNativeArray($kindInt32,[-32,0,-32])),new R.ptr(181,181,$toNativeArray($kindInt32,[743,0,743])),new R.ptr(192,214,$toNativeArray($kindInt32,[0,32,0])),new R.ptr(216,222,$toNativeArray($kindInt32,[0,32,0])),new R.ptr(224,246,$toNativeArray($kindInt32,[-32,0,-32])),new R.ptr(248,254,$toNativeArray($kindInt32,[-32,0,-32])),new R.ptr(255,255,$toNativeArray($kindInt32,[121,0,121])),new R.ptr(256,303,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(304,304,$toNativeArray($kindInt32,[0,-199,0])),new R.ptr(305,305,$toNativeArray($kindInt32,[-232,0,-232])),new R.ptr(306,311,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(313,328,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(330,375,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(376,376,$toNativeArray($kindInt32,[0,-121,0])),new R.ptr(377,382,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(383,383,$toNativeArray($kindInt32,[-300,0,-300])),new R.ptr(384,384,$toNativeArray($kindInt32,[195,0,195])),new R.ptr(385,385,$toNativeArray($kindInt32,[0,210,0])),new R.ptr(386,389,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(390,390,$toNativeArray($kindInt32,[0,206,0])),new R.ptr(391,392,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(393,394,$toNativeArray($kindInt32,[0,205,0])),new R.ptr(395,396,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(398,398,$toNativeArray($kindInt32,[0,79,0])),new R.ptr(399,399,$toNativeArray($kindInt32,[0,202,0])),new R.ptr(400,400,$toNativeArray($kindInt32,[0,203,0])),new R.ptr(401,402,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(403,403,$toNativeArray($kindInt32,[0,205,0])),new R.ptr(404,404,$toNativeArray($kindInt32,[0,207,0])),new R.ptr(405,405,$toNativeArray($kindInt32,[97,0,97])),new R.ptr(406,406,$toNativeArray($kindInt32,[0,211,0])),new R.ptr(407,407,$toNativeArray($kindInt32,[0,209,0])),new R.ptr(408,409,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(410,410,$toNativeArray($kindInt32,[163,0,163])),new R.ptr(412,412,$toNativeArray($kindInt32,[0,211,0])),new R.ptr(413,413,$toNativeArray($kindInt32,[0,213,0])),new R.ptr(414,414,$toNativeArray($kindInt32,[130,0,130])),new R.ptr(415,415,$toNativeArray($kindInt32,[0,214,0])),new R.ptr(416,421,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(422,422,$toNativeArray($kindInt32,[0,218,0])),new R.ptr(423,424,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(425,425,$toNativeArray($kindInt32,[0,218,0])),new R.ptr(428,429,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(430,430,$toNativeArray($kindInt32,[0,218,0])),new R.ptr(431,432,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(433,434,$toNativeArray($kindInt32,[0,217,0])),new R.ptr(435,438,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(439,439,$toNativeArray($kindInt32,[0,219,0])),new R.ptr(440,441,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(444,445,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(447,447,$toNativeArray($kindInt32,[56,0,56])),new R.ptr(452,452,$toNativeArray($kindInt32,[0,2,1])),new R.ptr(453,453,$toNativeArray($kindInt32,[-1,1,0])),new R.ptr(454,454,$toNativeArray($kindInt32,[-2,0,-1])),new R.ptr(455,455,$toNativeArray($kindInt32,[0,2,1])),new R.ptr(456,456,$toNativeArray($kindInt32,[-1,1,0])),new R.ptr(457,457,$toNativeArray($kindInt32,[-2,0,-1])),new R.ptr(458,458,$toNativeArray($kindInt32,[0,2,1])),new R.ptr(459,459,$toNativeArray($kindInt32,[-1,1,0])),new R.ptr(460,460,$toNativeArray($kindInt32,[-2,0,-1])),new R.ptr(461,476,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(477,477,$toNativeArray($kindInt32,[-79,0,-79])),new R.ptr(478,495,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(497,497,$toNativeArray($kindInt32,[0,2,1])),new R.ptr(498,498,$toNativeArray($kindInt32,[-1,1,0])),new R.ptr(499,499,$toNativeArray($kindInt32,[-2,0,-1])),new R.ptr(500,501,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(502,502,$toNativeArray($kindInt32,[0,-97,0])),new R.ptr(503,503,$toNativeArray($kindInt32,[0,-56,0])),new R.ptr(504,543,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(544,544,$toNativeArray($kindInt32,[0,-130,0])),new R.ptr(546,563,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(570,570,$toNativeArray($kindInt32,[0,10795,0])),new R.ptr(571,572,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(573,573,$toNativeArray($kindInt32,[0,-163,0])),new R.ptr(574,574,$toNativeArray($kindInt32,[0,10792,0])),new R.ptr(575,576,$toNativeArray($kindInt32,[10815,0,10815])),new R.ptr(577,578,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(579,579,$toNativeArray($kindInt32,[0,-195,0])),new R.ptr(580,580,$toNativeArray($kindInt32,[0,69,0])),new R.ptr(581,581,$toNativeArray($kindInt32,[0,71,0])),new R.ptr(582,591,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(592,592,$toNativeArray($kindInt32,[10783,0,10783])),new R.ptr(593,593,$toNativeArray($kindInt32,[10780,0,10780])),new R.ptr(594,594,$toNativeArray($kindInt32,[10782,0,10782])),new R.ptr(595,595,$toNativeArray($kindInt32,[-210,0,-210])),new R.ptr(596,596,$toNativeArray($kindInt32,[-206,0,-206])),new R.ptr(598,599,$toNativeArray($kindInt32,[-205,0,-205])),new R.ptr(601,601,$toNativeArray($kindInt32,[-202,0,-202])),new R.ptr(603,603,$toNativeArray($kindInt32,[-203,0,-203])),new R.ptr(604,604,$toNativeArray($kindInt32,[42319,0,42319])),new R.ptr(608,608,$toNativeArray($kindInt32,[-205,0,-205])),new R.ptr(609,609,$toNativeArray($kindInt32,[42315,0,42315])),new R.ptr(611,611,$toNativeArray($kindInt32,[-207,0,-207])),new R.ptr(613,613,$toNativeArray($kindInt32,[42280,0,42280])),new R.ptr(614,614,$toNativeArray($kindInt32,[42308,0,42308])),new R.ptr(616,616,$toNativeArray($kindInt32,[-209,0,-209])),new R.ptr(617,617,$toNativeArray($kindInt32,[-211,0,-211])),new R.ptr(618,618,$toNativeArray($kindInt32,[42308,0,42308])),new R.ptr(619,619,$toNativeArray($kindInt32,[10743,0,10743])),new R.ptr(620,620,$toNativeArray($kindInt32,[42305,0,42305])),new R.ptr(623,623,$toNativeArray($kindInt32,[-211,0,-211])),new R.ptr(625,625,$toNativeArray($kindInt32,[10749,0,10749])),new R.ptr(626,626,$toNativeArray($kindInt32,[-213,0,-213])),new R.ptr(629,629,$toNativeArray($kindInt32,[-214,0,-214])),new R.ptr(637,637,$toNativeArray($kindInt32,[10727,0,10727])),new R.ptr(640,640,$toNativeArray($kindInt32,[-218,0,-218])),new R.ptr(643,643,$toNativeArray($kindInt32,[-218,0,-218])),new R.ptr(647,647,$toNativeArray($kindInt32,[42282,0,42282])),new R.ptr(648,648,$toNativeArray($kindInt32,[-218,0,-218])),new R.ptr(649,649,$toNativeArray($kindInt32,[-69,0,-69])),new R.ptr(650,651,$toNativeArray($kindInt32,[-217,0,-217])),new R.ptr(652,652,$toNativeArray($kindInt32,[-71,0,-71])),new R.ptr(658,658,$toNativeArray($kindInt32,[-219,0,-219])),new R.ptr(669,669,$toNativeArray($kindInt32,[42261,0,42261])),new R.ptr(670,670,$toNativeArray($kindInt32,[42258,0,42258])),new R.ptr(837,837,$toNativeArray($kindInt32,[84,0,84])),new R.ptr(880,883,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(886,887,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(891,893,$toNativeArray($kindInt32,[130,0,130])),new R.ptr(895,895,$toNativeArray($kindInt32,[0,116,0])),new R.ptr(902,902,$toNativeArray($kindInt32,[0,38,0])),new R.ptr(904,906,$toNativeArray($kindInt32,[0,37,0])),new R.ptr(908,908,$toNativeArray($kindInt32,[0,64,0])),new R.ptr(910,911,$toNativeArray($kindInt32,[0,63,0])),new R.ptr(913,929,$toNativeArray($kindInt32,[0,32,0])),new R.ptr(931,939,$toNativeArray($kindInt32,[0,32,0])),new R.ptr(940,940,$toNativeArray($kindInt32,[-38,0,-38])),new R.ptr(941,943,$toNativeArray($kindInt32,[-37,0,-37])),new R.ptr(945,961,$toNativeArray($kindInt32,[-32,0,-32])),new R.ptr(962,962,$toNativeArray($kindInt32,[-31,0,-31])),new R.ptr(963,971,$toNativeArray($kindInt32,[-32,0,-32])),new R.ptr(972,972,$toNativeArray($kindInt32,[-64,0,-64])),new R.ptr(973,974,$toNativeArray($kindInt32,[-63,0,-63])),new R.ptr(975,975,$toNativeArray($kindInt32,[0,8,0])),new R.ptr(976,976,$toNativeArray($kindInt32,[-62,0,-62])),new R.ptr(977,977,$toNativeArray($kindInt32,[-57,0,-57])),new R.ptr(981,981,$toNativeArray($kindInt32,[-47,0,-47])),new R.ptr(982,982,$toNativeArray($kindInt32,[-54,0,-54])),new R.ptr(983,983,$toNativeArray($kindInt32,[-8,0,-8])),new R.ptr(984,1007,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(1008,1008,$toNativeArray($kindInt32,[-86,0,-86])),new R.ptr(1009,1009,$toNativeArray($kindInt32,[-80,0,-80])),new R.ptr(1010,1010,$toNativeArray($kindInt32,[7,0,7])),new R.ptr(1011,1011,$toNativeArray($kindInt32,[-116,0,-116])),new R.ptr(1012,1012,$toNativeArray($kindInt32,[0,-60,0])),new R.ptr(1013,1013,$toNativeArray($kindInt32,[-96,0,-96])),new R.ptr(1015,1016,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(1017,1017,$toNativeArray($kindInt32,[0,-7,0])),new R.ptr(1018,1019,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(1021,1023,$toNativeArray($kindInt32,[0,-130,0])),new R.ptr(1024,1039,$toNativeArray($kindInt32,[0,80,0])),new R.ptr(1040,1071,$toNativeArray($kindInt32,[0,32,0])),new R.ptr(1072,1103,$toNativeArray($kindInt32,[-32,0,-32])),new R.ptr(1104,1119,$toNativeArray($kindInt32,[-80,0,-80])),new R.ptr(1120,1153,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(1162,1215,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(1216,1216,$toNativeArray($kindInt32,[0,15,0])),new R.ptr(1217,1230,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(1231,1231,$toNativeArray($kindInt32,[-15,0,-15])),new R.ptr(1232,1327,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(1329,1366,$toNativeArray($kindInt32,[0,48,0])),new R.ptr(1377,1414,$toNativeArray($kindInt32,[-48,0,-48])),new R.ptr(4256,4293,$toNativeArray($kindInt32,[0,7264,0])),new R.ptr(4295,4295,$toNativeArray($kindInt32,[0,7264,0])),new R.ptr(4301,4301,$toNativeArray($kindInt32,[0,7264,0])),new R.ptr(5024,5103,$toNativeArray($kindInt32,[0,38864,0])),new R.ptr(5104,5109,$toNativeArray($kindInt32,[0,8,0])),new R.ptr(5112,5117,$toNativeArray($kindInt32,[-8,0,-8])),new R.ptr(7296,7296,$toNativeArray($kindInt32,[-6254,0,-6254])),new R.ptr(7297,7297,$toNativeArray($kindInt32,[-6253,0,-6253])),new R.ptr(7298,7298,$toNativeArray($kindInt32,[-6244,0,-6244])),new R.ptr(7299,7300,$toNativeArray($kindInt32,[-6242,0,-6242])),new R.ptr(7301,7301,$toNativeArray($kindInt32,[-6243,0,-6243])),new R.ptr(7302,7302,$toNativeArray($kindInt32,[-6236,0,-6236])),new R.ptr(7303,7303,$toNativeArray($kindInt32,[-6181,0,-6181])),new R.ptr(7304,7304,$toNativeArray($kindInt32,[35266,0,35266])),new R.ptr(7545,7545,$toNativeArray($kindInt32,[35332,0,35332])),new R.ptr(7549,7549,$toNativeArray($kindInt32,[3814,0,3814])),new R.ptr(7680,7829,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(7835,7835,$toNativeArray($kindInt32,[-59,0,-59])),new R.ptr(7838,7838,$toNativeArray($kindInt32,[0,-7615,0])),new R.ptr(7840,7935,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(7936,7943,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(7944,7951,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(7952,7957,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(7960,7965,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(7968,7975,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(7976,7983,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(7984,7991,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(7992,7999,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8000,8005,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8008,8013,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8017,8017,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8019,8019,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8021,8021,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8023,8023,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8025,8025,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8027,8027,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8029,8029,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8031,8031,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8032,8039,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8040,8047,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8048,8049,$toNativeArray($kindInt32,[74,0,74])),new R.ptr(8050,8053,$toNativeArray($kindInt32,[86,0,86])),new R.ptr(8054,8055,$toNativeArray($kindInt32,[100,0,100])),new R.ptr(8056,8057,$toNativeArray($kindInt32,[128,0,128])),new R.ptr(8058,8059,$toNativeArray($kindInt32,[112,0,112])),new R.ptr(8060,8061,$toNativeArray($kindInt32,[126,0,126])),new R.ptr(8064,8071,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8072,8079,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8080,8087,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8088,8095,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8096,8103,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8104,8111,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8112,8113,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8115,8115,$toNativeArray($kindInt32,[9,0,9])),new R.ptr(8120,8121,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8122,8123,$toNativeArray($kindInt32,[0,-74,0])),new R.ptr(8124,8124,$toNativeArray($kindInt32,[0,-9,0])),new R.ptr(8126,8126,$toNativeArray($kindInt32,[-7205,0,-7205])),new R.ptr(8131,8131,$toNativeArray($kindInt32,[9,0,9])),new R.ptr(8136,8139,$toNativeArray($kindInt32,[0,-86,0])),new R.ptr(8140,8140,$toNativeArray($kindInt32,[0,-9,0])),new R.ptr(8144,8145,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8152,8153,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8154,8155,$toNativeArray($kindInt32,[0,-100,0])),new R.ptr(8160,8161,$toNativeArray($kindInt32,[8,0,8])),new R.ptr(8165,8165,$toNativeArray($kindInt32,[7,0,7])),new R.ptr(8168,8169,$toNativeArray($kindInt32,[0,-8,0])),new R.ptr(8170,8171,$toNativeArray($kindInt32,[0,-112,0])),new R.ptr(8172,8172,$toNativeArray($kindInt32,[0,-7,0])),new R.ptr(8179,8179,$toNativeArray($kindInt32,[9,0,9])),new R.ptr(8184,8185,$toNativeArray($kindInt32,[0,-128,0])),new R.ptr(8186,8187,$toNativeArray($kindInt32,[0,-126,0])),new R.ptr(8188,8188,$toNativeArray($kindInt32,[0,-9,0])),new R.ptr(8486,8486,$toNativeArray($kindInt32,[0,-7517,0])),new R.ptr(8490,8490,$toNativeArray($kindInt32,[0,-8383,0])),new R.ptr(8491,8491,$toNativeArray($kindInt32,[0,-8262,0])),new R.ptr(8498,8498,$toNativeArray($kindInt32,[0,28,0])),new R.ptr(8526,8526,$toNativeArray($kindInt32,[-28,0,-28])),new R.ptr(8544,8559,$toNativeArray($kindInt32,[0,16,0])),new R.ptr(8560,8575,$toNativeArray($kindInt32,[-16,0,-16])),new R.ptr(8579,8580,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(9398,9423,$toNativeArray($kindInt32,[0,26,0])),new R.ptr(9424,9449,$toNativeArray($kindInt32,[-26,0,-26])),new R.ptr(11264,11310,$toNativeArray($kindInt32,[0,48,0])),new R.ptr(11312,11358,$toNativeArray($kindInt32,[-48,0,-48])),new R.ptr(11360,11361,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(11362,11362,$toNativeArray($kindInt32,[0,-10743,0])),new R.ptr(11363,11363,$toNativeArray($kindInt32,[0,-3814,0])),new R.ptr(11364,11364,$toNativeArray($kindInt32,[0,-10727,0])),new R.ptr(11365,11365,$toNativeArray($kindInt32,[-10795,0,-10795])),new R.ptr(11366,11366,$toNativeArray($kindInt32,[-10792,0,-10792])),new R.ptr(11367,11372,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(11373,11373,$toNativeArray($kindInt32,[0,-10780,0])),new R.ptr(11374,11374,$toNativeArray($kindInt32,[0,-10749,0])),new R.ptr(11375,11375,$toNativeArray($kindInt32,[0,-10783,0])),new R.ptr(11376,11376,$toNativeArray($kindInt32,[0,-10782,0])),new R.ptr(11378,11379,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(11381,11382,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(11390,11391,$toNativeArray($kindInt32,[0,-10815,0])),new R.ptr(11392,11491,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(11499,11502,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(11506,11507,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(11520,11557,$toNativeArray($kindInt32,[-7264,0,-7264])),new R.ptr(11559,11559,$toNativeArray($kindInt32,[-7264,0,-7264])),new R.ptr(11565,11565,$toNativeArray($kindInt32,[-7264,0,-7264])),new R.ptr(42560,42605,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42624,42651,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42786,42799,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42802,42863,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42873,42876,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42877,42877,$toNativeArray($kindInt32,[0,-35332,0])),new R.ptr(42878,42887,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42891,42892,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42893,42893,$toNativeArray($kindInt32,[0,-42280,0])),new R.ptr(42896,42899,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42902,42921,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(42922,42922,$toNativeArray($kindInt32,[0,-42308,0])),new R.ptr(42923,42923,$toNativeArray($kindInt32,[0,-42319,0])),new R.ptr(42924,42924,$toNativeArray($kindInt32,[0,-42315,0])),new R.ptr(42925,42925,$toNativeArray($kindInt32,[0,-42305,0])),new R.ptr(42926,42926,$toNativeArray($kindInt32,[0,-42308,0])),new R.ptr(42928,42928,$toNativeArray($kindInt32,[0,-42258,0])),new R.ptr(42929,42929,$toNativeArray($kindInt32,[0,-42282,0])),new R.ptr(42930,42930,$toNativeArray($kindInt32,[0,-42261,0])),new R.ptr(42931,42931,$toNativeArray($kindInt32,[0,928,0])),new R.ptr(42932,42935,$toNativeArray($kindInt32,[1114112,1114112,1114112])),new R.ptr(43859,43859,$toNativeArray($kindInt32,[-928,0,-928])),new R.ptr(43888,43967,$toNativeArray($kindInt32,[-38864,0,-38864])),new R.ptr(65313,65338,$toNativeArray($kindInt32,[0,32,0])),new R.ptr(65345,65370,$toNativeArray($kindInt32,[-32,0,-32])),new R.ptr(66560,66599,$toNativeArray($kindInt32,[0,40,0])),new R.ptr(66600,66639,$toNativeArray($kindInt32,[-40,0,-40])),new R.ptr(66736,66771,$toNativeArray($kindInt32,[0,40,0])),new R.ptr(66776,66811,$toNativeArray($kindInt32,[-40,0,-40])),new R.ptr(68736,68786,$toNativeArray($kindInt32,[0,64,0])),new R.ptr(68800,68850,$toNativeArray($kindInt32,[-64,0,-64])),new R.ptr(71840,71871,$toNativeArray($kindInt32,[0,32,0])),new R.ptr(71872,71903,$toNativeArray($kindInt32,[-32,0,-32])),new R.ptr(125184,125217,$toNativeArray($kindInt32,[0,34,0])),new R.ptr(125218,125251,$toNativeArray($kindInt32,[-34,0,-34]))]);$pkg.CaseRanges=IK;IL=$toNativeArray($kindUint8,[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,144,130,130,130,136,130,130,130,130,130,130,136,130,130,130,130,132,132,132,132,132,132,132,132,132,132,130,130,136,136,136,130,130,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,130,130,130,136,130,136,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,130,136,130,136,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,16,130,136,136,136,136,136,130,136,136,224,130,136,0,136,136,136,136,132,132,136,192,130,130,136,132,224,130,132,132,132,130,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,136,160,160,160,160,160,160,160,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,136,192,192,192,192,192,192,192,192]);IM=$toNativeArray($kindUint16,[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95,96,65,66,67,68,69,70,71,72,73,74,8490,76,77,78,79,80,81,82,383,84,85,86,87,88,89,90,123,124,125,126,127]);IN=new JD([new AF.ptr(75,107),new AF.ptr(83,115),new AF.ptr(107,8490),new AF.ptr(115,383),new AF.ptr(181,924),new AF.ptr(197,229),new AF.ptr(223,7838),new AF.ptr(229,8491),new AF.ptr(304,304),new AF.ptr(305,305),new AF.ptr(383,83),new AF.ptr(452,453),new AF.ptr(453,454),new AF.ptr(454,452),new AF.ptr(455,456),new AF.ptr(456,457),new AF.ptr(457,455),new AF.ptr(458,459),new AF.ptr(459,460),new AF.ptr(460,458),new AF.ptr(497,498),new AF.ptr(498,499),new AF.ptr(499,497),new AF.ptr(837,921),new AF.ptr(914,946),new AF.ptr(917,949),new AF.ptr(920,952),new AF.ptr(921,953),new AF.ptr(922,954),new AF.ptr(924,956),new AF.ptr(928,960),new AF.ptr(929,961),new AF.ptr(931,962),new AF.ptr(934,966),new AF.ptr(937,969),new AF.ptr(946,976),new AF.ptr(949,1013),new AF.ptr(952,977),new AF.ptr(953,8126),new AF.ptr(954,1008),new AF.ptr(956,181),new AF.ptr(960,982),new AF.ptr(961,1009),new AF.ptr(962,963),new AF.ptr(963,931),new AF.ptr(966,981),new AF.ptr(969,8486),new AF.ptr(976,914),new AF.ptr(977,1012),new AF.ptr(981,934),new AF.ptr(982,928),new AF.ptr(1008,922),new AF.ptr(1009,929),new AF.ptr(1012,920),new AF.ptr(1013,917),new AF.ptr(1042,1074),new AF.ptr(1044,1076),new AF.ptr(1054,1086),new AF.ptr(1057,1089),new AF.ptr(1058,1090),new AF.ptr(1066,1098),new AF.ptr(1074,7296),new AF.ptr(1076,7297),new AF.ptr(1086,7298),new AF.ptr(1089,7299),new AF.ptr(1090,7300),new AF.ptr(1098,7302),new AF.ptr(1122,1123),new AF.ptr(1123,7303),new AF.ptr(7296,1042),new AF.ptr(7297,1044),new AF.ptr(7298,1054),new AF.ptr(7299,1057),new AF.ptr(7300,7301),new AF.ptr(7301,1058),new AF.ptr(7302,1066),new AF.ptr(7303,1122),new AF.ptr(7304,42570),new AF.ptr(7776,7777),new AF.ptr(7777,7835),new AF.ptr(7835,7776),new AF.ptr(7838,223),new AF.ptr(8126,837),new AF.ptr(8486,937),new AF.ptr(8490,75),new AF.ptr(8491,197),new AF.ptr(42570,42571),new AF.ptr(42571,7304)]);IO=new O.ptr(new IY([new P.ptr(837,837,1)]),IZ.nil,0);IP=new O.ptr(new IY([new P.ptr(65,90,1),new P.ptr(192,214,1),new P.ptr(216,222,1),new P.ptr(256,302,2),new P.ptr(306,310,2),new P.ptr(313,327,2),new P.ptr(330,376,2),new P.ptr(377,381,2),new P.ptr(385,386,1),new P.ptr(388,390,2),new P.ptr(391,393,2),new P.ptr(394,395,1),new P.ptr(398,401,1),new P.ptr(403,404,1),new P.ptr(406,408,1),new P.ptr(412,413,1),new P.ptr(415,416,1),new P.ptr(418,422,2),new P.ptr(423,425,2),new P.ptr(428,430,2),new P.ptr(431,433,2),new P.ptr(434,435,1),new P.ptr(437,439,2),new P.ptr(440,444,4),new P.ptr(452,453,1),new P.ptr(455,456,1),new P.ptr(458,459,1),new P.ptr(461,475,2),new P.ptr(478,494,2),new P.ptr(497,498,1),new P.ptr(500,502,2),new P.ptr(503,504,1),new P.ptr(506,562,2),new P.ptr(570,571,1),new P.ptr(573,574,1),new P.ptr(577,579,2),new P.ptr(580,582,1),new P.ptr(584,590,2),new P.ptr(837,880,43),new P.ptr(882,886,4),new P.ptr(895,902,7),new P.ptr(904,906,1),new P.ptr(908,910,2),new P.ptr(911,913,2),new P.ptr(914,929,1),new P.ptr(931,939,1),new P.ptr(975,984,9),new P.ptr(986,1006,2),new P.ptr(1012,1015,3),new P.ptr(1017,1018,1),new P.ptr(1021,1071,1),new P.ptr(1120,1152,2),new P.ptr(1162,1216,2),new P.ptr(1217,1229,2),new P.ptr(1232,1326,2),new P.ptr(1329,1366,1),new P.ptr(4256,4293,1),new P.ptr(4295,4301,6),new P.ptr(5024,5109,1),new P.ptr(7680,7828,2),new P.ptr(7838,7934,2),new P.ptr(7944,7951,1),new P.ptr(7960,7965,1),new P.ptr(7976,7983,1),new P.ptr(7992,7999,1),new P.ptr(8008,8013,1),new P.ptr(8025,8031,2),new P.ptr(8040,8047,1),new P.ptr(8072,8079,1),new P.ptr(8088,8095,1),new P.ptr(8104,8111,1),new P.ptr(8120,8124,1),new P.ptr(8136,8140,1),new P.ptr(8152,8155,1),new P.ptr(8168,8172,1),new P.ptr(8184,8188,1),new P.ptr(8486,8490,4),new P.ptr(8491,8498,7),new P.ptr(8579,11264,2685),new P.ptr(11265,11310,1),new P.ptr(11360,11362,2),new P.ptr(11363,11364,1),new P.ptr(11367,11373,2),new P.ptr(11374,11376,1),new P.ptr(11378,11381,3),new P.ptr(11390,11392,1),new P.ptr(11394,11490,2),new P.ptr(11499,11501,2),new P.ptr(11506,42560,31054),new P.ptr(42562,42604,2),new P.ptr(42624,42650,2),new P.ptr(42786,42798,2),new P.ptr(42802,42862,2),new P.ptr(42873,42877,2),new P.ptr(42878,42886,2),new P.ptr(42891,42893,2),new P.ptr(42896,42898,2),new P.ptr(42902,42922,2),new P.ptr(42923,42926,1),new P.ptr(42928,42932,1),new P.ptr(42934,65313,22379),new P.ptr(65314,65338,1)]),new IZ([new Q.ptr(66560,66599,1),new Q.ptr(66736,66771,1),new Q.ptr(68736,68786,1),new Q.ptr(71840,71871,1),new Q.ptr(125184,125217,1)]),3);IQ=new O.ptr(new IY([new P.ptr(452,454,2),new P.ptr(455,457,2),new P.ptr(458,460,2),new P.ptr(497,499,2),new P.ptr(8064,8071,1),new P.ptr(8080,8087,1),new P.ptr(8096,8103,1),new P.ptr(8115,8131,16),new P.ptr(8179,8179,1)]),IZ.nil,0);IR=new O.ptr(new IY([new P.ptr(97,122,1),new P.ptr(181,223,42),new P.ptr(224,246,1),new P.ptr(248,255,1),new P.ptr(257,303,2),new P.ptr(307,311,2),new P.ptr(314,328,2),new P.ptr(331,375,2),new P.ptr(378,382,2),new P.ptr(383,384,1),new P.ptr(387,389,2),new P.ptr(392,396,4),new P.ptr(402,405,3),new P.ptr(409,410,1),new P.ptr(414,417,3),new P.ptr(419,421,2),new P.ptr(424,429,5),new P.ptr(432,436,4),new P.ptr(438,441,3),new P.ptr(445,447,2),new P.ptr(453,454,1),new P.ptr(456,457,1),new P.ptr(459,460,1),new P.ptr(462,476,2),new P.ptr(477,495,2),new P.ptr(498,499,1),new P.ptr(501,505,4),new P.ptr(507,543,2),new P.ptr(547,563,2),new P.ptr(572,575,3),new P.ptr(576,578,2),new P.ptr(583,591,2),new P.ptr(592,596,1),new P.ptr(598,599,1),new P.ptr(601,603,2),new P.ptr(604,608,4),new P.ptr(609,613,2),new P.ptr(614,616,2),new P.ptr(617,620,1),new P.ptr(623,625,2),new P.ptr(626,629,3),new P.ptr(637,643,3),new P.ptr(647,652,1),new P.ptr(658,669,11),new P.ptr(670,837,167),new P.ptr(881,883,2),new P.ptr(887,891,4),new P.ptr(892,893,1),new P.ptr(940,943,1),new P.ptr(945,974,1),new P.ptr(976,977,1),new P.ptr(981,983,1),new P.ptr(985,1007,2),new P.ptr(1008,1011,1),new P.ptr(1013,1019,3),new P.ptr(1072,1119,1),new P.ptr(1121,1153,2),new P.ptr(1163,1215,2),new P.ptr(1218,1230,2),new P.ptr(1231,1327,2),new P.ptr(1377,1414,1),new P.ptr(5112,5117,1),new P.ptr(7296,7304,1),new P.ptr(7545,7549,4),new P.ptr(7681,7829,2),new P.ptr(7835,7841,6),new P.ptr(7843,7935,2),new P.ptr(7936,7943,1),new P.ptr(7952,7957,1),new P.ptr(7968,7975,1),new P.ptr(7984,7991,1),new P.ptr(8000,8005,1),new P.ptr(8017,8023,2),new P.ptr(8032,8039,1),new P.ptr(8048,8061,1),new P.ptr(8112,8113,1),new P.ptr(8126,8144,18),new P.ptr(8145,8160,15),new P.ptr(8161,8165,4),new P.ptr(8526,8580,54),new P.ptr(11312,11358,1),new P.ptr(11361,11365,4),new P.ptr(11366,11372,2),new P.ptr(11379,11382,3),new P.ptr(11393,11491,2),new P.ptr(11500,11502,2),new P.ptr(11507,11520,13),new P.ptr(11521,11557,1),new P.ptr(11559,11565,6),new P.ptr(42561,42605,2),new P.ptr(42625,42651,2),new P.ptr(42787,42799,2),new P.ptr(42803,42863,2),new P.ptr(42874,42876,2),new P.ptr(42879,42887,2),new P.ptr(42892,42897,5),new P.ptr(42899,42903,4),new P.ptr(42905,42921,2),new P.ptr(42933,42935,2),new P.ptr(43859,43888,29),new P.ptr(43889,43967,1),new P.ptr(65345,65370,1)]),new IZ([new Q.ptr(66600,66639,1),new Q.ptr(66776,66811,1),new Q.ptr(68800,68850,1),new Q.ptr(71872,71903,1),new Q.ptr(125218,125251,1)]),4);IS=new O.ptr(new IY([new P.ptr(921,953,32),new P.ptr(8126,8126,1)]),IZ.nil,0);IT=new O.ptr(new IY([new P.ptr(921,953,32),new P.ptr(8126,8126,1)]),IZ.nil,0);$pkg.FoldCategory=$makeMap($String.keyFor,[{k:"L",v:IO},{k:"Ll",v:IP},{k:"Lt",v:IQ},{k:"Lu",v:IR},{k:"M",v:IS},{k:"Mn",v:IT}]);IU=new O.ptr(new IY([new P.ptr(924,956,32)]),IZ.nil,0);IV=new O.ptr(new IY([new P.ptr(181,837,656)]),IZ.nil,0);IW=new O.ptr(new IY([new P.ptr(921,953,32),new P.ptr(8126,8126,1)]),IZ.nil,0);$pkg.FoldScript=$makeMap($String.keyFor,[{k:"Common",v:IU},{k:"Greek",v:IV},{k:"Inherited",v:IW}]);}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["reflect"]=(function(){var $pkg={},$init,A,C,H,D,B,E,F,G,N,P,Q,R,AW,CB,CC,CD,CE,CF,CG,CH,CI,CJ,CK,CL,CM,CN,CO,CP,CQ,CS,DB,DC,DD,DG,DH,DI,EY,EZ,FC,FM,HQ,HR,HS,HV,HW,HX,HY,HZ,IA,IB,IC,ID,IE,IF,IG,IH,II,IJ,IK,IL,IM,IP,IQ,IR,IS,IT,IU,JC,JE,JG,JH,JI,JJ,JQ,JR,JS,I,O,S,U,W,BJ,BK,BO,CT,FL,J,K,L,M,T,V,X,Y,Z,AA,AB,AC,AD,AE,AH,AJ,AK,AL,AM,AO,AS,AT,AU,AV,AX,AY,AZ,BA,BB,BC,BE,BF,BG,BH,BI,BL,BM,BN,BP,BQ,DK,DM,DN,DO,DP,EQ,EV,FN,FO,GG,GH,GJ,GK,GL,GM,GN,GO,GP,GQ,GR,GS,GT,GU,GV,GW,GX,GY,GZ,HA,HB,HC,HD,HE;A=$packages["errors"];C=$packages["github.com/gopherjs/gopherjs/js"];H=$packages["math"];D=$packages["runtime"];B=$packages["strconv"];E=$packages["sync"];F=$packages["unicode"];G=$packages["unicode/utf8"];N=$pkg.uncommonType=$newType(0,$kindStruct,"reflect.uncommonType",true,"reflect",false,function(pkgPath_,mcount_,xcount_,moff_,_methods_){this.$val=this;if(arguments.length===0){this.pkgPath=0;this.mcount=0;this.xcount=0;this.moff=0;this._methods=IB.nil;return;}this.pkgPath=pkgPath_;this.mcount=mcount_;this.xcount=xcount_;this.moff=moff_;this._methods=_methods_;});P=$pkg.funcType=$newType(0,$kindStruct,"reflect.funcType",true,"reflect",false,function(rtype_,inCount_,outCount_,_in_,_out_){this.$val=this;if(arguments.length===0){this.rtype=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);this.inCount=0;this.outCount=0;this._in=HS.nil;this._out=HS.nil;return;}this.rtype=rtype_;this.inCount=inCount_;this.outCount=outCount_;this._in=_in_;this._out=_out_;});Q=$pkg.name=$newType(0,$kindStruct,"reflect.name",true,"reflect",false,function(bytes_){this.$val=this;if(arguments.length===0){this.bytes=IA.nil;return;}this.bytes=bytes_;});R=$pkg.nameData=$newType(0,$kindStruct,"reflect.nameData",true,"reflect",false,function(name_,tag_,exported_){this.$val=this;if(arguments.length===0){this.name="";this.tag="";this.exported=false;return;}this.name=name_;this.tag=tag_;this.exported=exported_;});AW=$pkg.mapIter=$newType(0,$kindStruct,"reflect.mapIter",true,"reflect",false,function(t_,m_,keys_,i_,last_){this.$val=this;if(arguments.length===0){this.t=$ifaceNil;this.m=null;this.keys=null;this.i=0;this.last=null;return;}this.t=t_;this.m=m_;this.keys=keys_;this.i=i_;this.last=last_;});CB=$pkg.Type=$newType(8,$kindInterface,"reflect.Type",true,"reflect",true,null);CC=$pkg.Kind=$newType(4,$kindUint,"reflect.Kind",true,"reflect",true,null);CD=$pkg.tflag=$newType(1,$kindUint8,"reflect.tflag",true,"reflect",false,null);CE=$pkg.rtype=$newType(0,$kindStruct,"reflect.rtype",true,"reflect",false,function(size_,ptrdata_,hash_,tflag_,align_,fieldAlign_,kind_,alg_,gcdata_,str_,ptrToThis_){this.$val=this;if(arguments.length===0){this.size=0;this.ptrdata=0;this.hash=0;this.tflag=0;this.align=0;this.fieldAlign=0;this.kind=0;this.alg=HZ.nil;this.gcdata=IA.nil;this.str=0;this.ptrToThis=0;return;}this.size=size_;this.ptrdata=ptrdata_;this.hash=hash_;this.tflag=tflag_;this.align=align_;this.fieldAlign=fieldAlign_;this.kind=kind_;this.alg=alg_;this.gcdata=gcdata_;this.str=str_;this.ptrToThis=ptrToThis_;});CF=$pkg.typeAlg=$newType(0,$kindStruct,"reflect.typeAlg",true,"reflect",false,function(hash_,equal_){this.$val=this;if(arguments.length===0){this.hash=$throwNilPointerError;this.equal=$throwNilPointerError;return;}this.hash=hash_;this.equal=equal_;});CG=$pkg.method=$newType(0,$kindStruct,"reflect.method",true,"reflect",false,function(name_,mtyp_,ifn_,tfn_){this.$val=this;if(arguments.length===0){this.name=0;this.mtyp=0;this.ifn=0;this.tfn=0;return;}this.name=name_;this.mtyp=mtyp_;this.ifn=ifn_;this.tfn=tfn_;});CH=$pkg.ChanDir=$newType(4,$kindInt,"reflect.ChanDir",true,"reflect",true,null);CI=$pkg.arrayType=$newType(0,$kindStruct,"reflect.arrayType",true,"reflect",false,function(rtype_,elem_,slice_,len_){this.$val=this;if(arguments.length===0){this.rtype=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);this.elem=HR.nil;this.slice=HR.nil;this.len=0;return;}this.rtype=rtype_;this.elem=elem_;this.slice=slice_;this.len=len_;});CJ=$pkg.chanType=$newType(0,$kindStruct,"reflect.chanType",true,"reflect",false,function(rtype_,elem_,dir_){this.$val=this;if(arguments.length===0){this.rtype=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);this.elem=HR.nil;this.dir=0;return;}this.rtype=rtype_;this.elem=elem_;this.dir=dir_;});CK=$pkg.imethod=$newType(0,$kindStruct,"reflect.imethod",true,"reflect",false,function(name_,typ_){this.$val=this;if(arguments.length===0){this.name=0;this.typ=0;return;}this.name=name_;this.typ=typ_;});CL=$pkg.interfaceType=$newType(0,$kindStruct,"reflect.interfaceType",true,"reflect",false,function(rtype_,pkgPath_,methods_){this.$val=this;if(arguments.length===0){this.rtype=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);this.pkgPath=new Q.ptr(IA.nil);this.methods=IC.nil;return;}this.rtype=rtype_;this.pkgPath=pkgPath_;this.methods=methods_;});CM=$pkg.mapType=$newType(0,$kindStruct,"reflect.mapType",true,"reflect",false,function(rtype_,key_,elem_,bucket_,keysize_,valuesize_,bucketsize_,flags_){this.$val=this;if(arguments.length===0){this.rtype=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);this.key=HR.nil;this.elem=HR.nil;this.bucket=HR.nil;this.keysize=0;this.valuesize=0;this.bucketsize=0;this.flags=0;return;}this.rtype=rtype_;this.key=key_;this.elem=elem_;this.bucket=bucket_;this.keysize=keysize_;this.valuesize=valuesize_;this.bucketsize=bucketsize_;this.flags=flags_;});CN=$pkg.ptrType=$newType(0,$kindStruct,"reflect.ptrType",true,"reflect",false,function(rtype_,elem_){this.$val=this;if(arguments.length===0){this.rtype=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);this.elem=HR.nil;return;}this.rtype=rtype_;this.elem=elem_;});CO=$pkg.sliceType=$newType(0,$kindStruct,"reflect.sliceType",true,"reflect",false,function(rtype_,elem_){this.$val=this;if(arguments.length===0){this.rtype=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);this.elem=HR.nil;return;}this.rtype=rtype_;this.elem=elem_;});CP=$pkg.structField=$newType(0,$kindStruct,"reflect.structField",true,"reflect",false,function(name_,typ_,offsetEmbed_){this.$val=this;if(arguments.length===0){this.name=new Q.ptr(IA.nil);this.typ=HR.nil;this.offsetEmbed=0;return;}this.name=name_;this.typ=typ_;this.offsetEmbed=offsetEmbed_;});CQ=$pkg.structType=$newType(0,$kindStruct,"reflect.structType",true,"reflect",false,function(rtype_,pkgPath_,fields_){this.$val=this;if(arguments.length===0){this.rtype=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);this.pkgPath=new Q.ptr(IA.nil);this.fields=ID.nil;return;}this.rtype=rtype_;this.pkgPath=pkgPath_;this.fields=fields_;});CS=$pkg.Method=$newType(0,$kindStruct,"reflect.Method",true,"reflect",true,function(Name_,PkgPath_,Type_,Func_,Index_){this.$val=this;if(arguments.length===0){this.Name="";this.PkgPath="";this.Type=$ifaceNil;this.Func=new EY.ptr(HR.nil,0,0);this.Index=0;return;}this.Name=Name_;this.PkgPath=PkgPath_;this.Type=Type_;this.Func=Func_;this.Index=Index_;});DB=$pkg.nameOff=$newType(4,$kindInt32,"reflect.nameOff",true,"reflect",false,null);DC=$pkg.typeOff=$newType(4,$kindInt32,"reflect.typeOff",true,"reflect",false,null);DD=$pkg.textOff=$newType(4,$kindInt32,"reflect.textOff",true,"reflect",false,null);DG=$pkg.StructField=$newType(0,$kindStruct,"reflect.StructField",true,"reflect",true,function(Name_,PkgPath_,Type_,Tag_,Offset_,Index_,Anonymous_){this.$val=this;if(arguments.length===0){this.Name="";this.PkgPath="";this.Type=$ifaceNil;this.Tag="";this.Offset=0;this.Index=IR.nil;this.Anonymous=false;return;}this.Name=Name_;this.PkgPath=PkgPath_;this.Type=Type_;this.Tag=Tag_;this.Offset=Offset_;this.Index=Index_;this.Anonymous=Anonymous_;});DH=$pkg.StructTag=$newType(8,$kindString,"reflect.StructTag",true,"reflect",true,null);DI=$pkg.fieldScan=$newType(0,$kindStruct,"reflect.fieldScan",true,"reflect",false,function(typ_,index_){this.$val=this;if(arguments.length===0){this.typ=IT.nil;this.index=IR.nil;return;}this.typ=typ_;this.index=index_;});EY=$pkg.Value=$newType(0,$kindStruct,"reflect.Value",true,"reflect",true,function(typ_,ptr_,flag_){this.$val=this;if(arguments.length===0){this.typ=HR.nil;this.ptr=0;this.flag=0;return;}this.typ=typ_;this.ptr=ptr_;this.flag=flag_;});EZ=$pkg.flag=$newType(4,$kindUintptr,"reflect.flag",true,"reflect",false,null);FC=$pkg.ValueError=$newType(0,$kindStruct,"reflect.ValueError",true,"reflect",true,function(Method_,Kind_){this.$val=this;if(arguments.length===0){this.Method="";this.Kind=0;return;}this.Method=Method_;this.Kind=Kind_;});FM=$pkg.MapIter=$newType(0,$kindStruct,"reflect.MapIter",true,"reflect",true,function(m_,it_){this.$val=this;if(arguments.length===0){this.m=new EY.ptr(HR.nil,0,0);this.it=0;return;}this.m=m_;this.it=it_;});HQ=$sliceType(Q);HR=$ptrType(CE);HS=$sliceType(HR);HV=$sliceType($emptyInterface);HW=$ptrType(C.Object);HX=$funcType([HV],[HW],true);HY=$sliceType($String);HZ=$ptrType(CF);IA=$ptrType($Uint8);IB=$sliceType(CG);IC=$sliceType(CK);ID=$sliceType(CP);IE=$ptrType(N);IF=$ptrType(R);IG=$structType("reflect",[{prop:"str",name:"str",embedded:false,exported:false,typ:$String,tag:""}]);IH=$sliceType(HW);II=$sliceType(EY);IJ=$ptrType(AW);IK=$ptrType(P);IL=$sliceType(CB);IM=$sliceType(IH);IP=$ptrType(CL);IQ=$ptrType(CK);IR=$sliceType($Int);IS=$sliceType(DI);IT=$ptrType(CQ);IU=$sliceType($Uint8);JC=$ptrType($UnsafePointer);JE=$sliceType($Int32);JG=$funcType([$String],[$Bool],false);JH=$funcType([$UnsafePointer,$Uintptr],[$Uintptr],false);JI=$funcType([$UnsafePointer,$UnsafePointer],[$Bool],false);JJ=$ptrType(CP);JQ=$arrayType($Uintptr,2);JR=$ptrType(FM);JS=$ptrType(FC);J=function(){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=(function(an){var an;});$r=an((ao=new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),new ao.constructor.elem(ao)));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((ap=new N.ptr(0,0,0,0,IB.nil),new ap.constructor.elem(ap)));$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((aq=new CG.ptr(0,0,0,0),new aq.constructor.elem(aq)));$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((ar=new CI.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),HR.nil,HR.nil,0),new ar.constructor.elem(ar)));$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((as=new CJ.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),HR.nil,0),new as.constructor.elem(as)));$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((at=new P.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),0,0,HS.nil,HS.nil),new at.constructor.elem(at)));$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((au=new CL.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),new Q.ptr(IA.nil),IC.nil),new au.constructor.elem(au)));$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((av=new CM.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),HR.nil,HR.nil,HR.nil,0,0,0,0),new av.constructor.elem(av)));$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((aw=new CN.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),HR.nil),new aw.constructor.elem(aw)));$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((ax=new CO.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),HR.nil),new ax.constructor.elem(ax)));$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((ay=new CQ.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),new Q.ptr(IA.nil),ID.nil),new ay.constructor.elem(ay)));$s=11;case 11:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((az=new CK.ptr(0,0),new az.constructor.elem(az)));$s=12;case 12:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=an((ba=new CP.ptr(new Q.ptr(IA.nil),HR.nil,0),new ba.constructor.elem(ba)));$s=13;case 13:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}I=true;FL=$assertType(AD(new $Uint8(0)),HR);$s=-1;return;}return;}if($f===undefined){$f={$blk:J};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.$s=$s;$f.$r=$r;return $f;};K=function(an){var an;return an.jsType;};L=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,bx,by,bz;if(an.reflectType===undefined){ao=new CE.ptr(((($parseInt(an.size)>>0)>>>0)),0,0,0,0,0,((($parseInt(an.kind)>>0)<<24>>>24)),HZ.nil,IA.nil,V($clone(T(Y(an.string),"",!!(an.exported)),Q)),0);ao.jsType=an;an.reflectType=ao;ap=$methodSet(an);if(!(($parseInt(ap.length)===0))||!!(an.named)){ao.tflag=(ao.tflag|(1))>>>0;if(!!(an.named)){ao.tflag=(ao.tflag|(4))>>>0;}aq=IB.nil;ar=0;while(true){if(!(ar<$parseInt(ap.length))){break;}as=ap[ar];at=Y(as.pkg)==="";if(!at){ar=ar+(1)>>0;continue;}aq=$append(aq,new CG.ptr(V($clone(T(Y(as.name),"",at),Q)),X(L(as.typ)),0,0));ar=ar+(1)>>0;}au=((aq.$length<<16>>>16));av=0;while(true){if(!(av<$parseInt(ap.length))){break;}aw=ap[av];ax=Y(aw.pkg)==="";if(ax){av=av+(1)>>0;continue;}aq=$append(aq,new CG.ptr(V($clone(T(Y(aw.name),"",ax),Q)),X(L(aw.typ)),0,0));av=av+(1)>>0;}ay=new N.ptr(V($clone(T(Y(an.pkg),"",false),Q)),(($parseInt(ap.length)<<16>>>16)),au,0,aq);az=ao;(O||$throwRuntimeError("assignment to entry in nil map"))[HR.keyFor(az)]={k:az,v:ay};ay.jsType=an;}ba=ao.Kind();if(ba===(17)){M(ao,new CI.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),L(an.elem),HR.nil,((($parseInt(an.len)>>0)>>>0))));}else if(ba===(18)){bb=3;if(!!(an.sendOnly)){bb=2;}if(!!(an.recvOnly)){bb=1;}M(ao,new CJ.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),L(an.elem),((bb>>>0))));}else if(ba===(19)){bc=an.params;bd=$makeSlice(HS,$parseInt(bc.length));be=bd;bf=0;while(true){if(!(bf=bd.$length)?($throwRuntimeError("index out of range"),undefined):bd.$array[bd.$offset+bg]=L(bc[bg]));bf++;}bh=an.results;bi=$makeSlice(HS,$parseInt(bh.length));bj=bi;bk=0;while(true){if(!(bk=bi.$length)?($throwRuntimeError("index out of range"),undefined):bi.$array[bi.$offset+bl]=L(bh[bl]));bk++;}bm=(($parseInt(bh.length)<<16>>>16));if(!!(an.variadic)){bm=(bm|(32768))>>>0;}M(ao,new P.ptr($clone(ao,CE),(($parseInt(bc.length)<<16>>>16)),bm,bd,bi));}else if(ba===(20)){bn=an.methods;bo=$makeSlice(IC,$parseInt(bn.length));bp=bo;bq=0;while(true){if(!(bq=bo.$length)?($throwRuntimeError("index out of range"),undefined):bo.$array[bo.$offset+br]),new CK.ptr(V($clone(T(Y(bs.name),"",Y(bs.pkg)===""),Q)),X(L(bs.typ))));bq++;}M(ao,new CL.ptr($clone(ao,CE),$clone(T(Y(an.pkg),"",false),Q),bo));}else if(ba===(21)){M(ao,new CM.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),L(an.key),L(an.elem),HR.nil,0,0,0,0));}else if(ba===(22)){M(ao,new CN.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),L(an.elem)));}else if(ba===(23)){M(ao,new CO.ptr(new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0),L(an.elem)));}else if(ba===(25)){bt=an.fields;bu=$makeSlice(ID,$parseInt(bt.length));bv=bu;bw=0;while(true){if(!(bw>>0))<<1>>>0;if(!!(by.embedded)){bz=(bz|(1))>>>0;}CP.copy(((bx<0||bx>=bu.$length)?($throwRuntimeError("index out of range"),undefined):bu.$array[bu.$offset+bx]),new CP.ptr($clone(T(Y(by.name),Y(by.tag),!!(by.exported)),Q),L(by.typ),bz));bw++;}M(ao,new CQ.ptr($clone(ao,CE),$clone(T(Y(an.pkgPath),"",false),Q),bu));}}return((an.reflectType));};M=function(an,ao){var an,ao;an.kindType=ao;ao.rtype=an;};N.ptr.prototype.methods=function(){var an;an=this;return an._methods;};N.prototype.methods=function(){return this.$val.methods();};N.ptr.prototype.exportedMethods=function(){var an;an=this;return $subslice(an._methods,0,an.xcount,an.xcount);};N.prototype.exportedMethods=function(){return this.$val.exportedMethods();};CE.ptr.prototype.uncommon=function(){var an,ao;an=this;return(ao=O[HR.keyFor(an)],ao!==undefined?ao.v:IE.nil);};CE.prototype.uncommon=function(){return this.$val.uncommon();};P.ptr.prototype.in$=function(){var an;an=this;return an._in;};P.prototype.in$=function(){return this.$val.in$();};P.ptr.prototype.out=function(){var an;an=this;return an._out;};P.prototype.out=function(){return this.$val.out();};Q.ptr.prototype.name=function(){var an,ao,ap;an="";ao=this;an=(ap=S[IA.keyFor(ao.bytes)],ap!==undefined?ap.v:IF.nil).name;return an;};Q.prototype.name=function(){return this.$val.name();};Q.ptr.prototype.tag=function(){var an,ao,ap;an="";ao=this;an=(ap=S[IA.keyFor(ao.bytes)],ap!==undefined?ap.v:IF.nil).tag;return an;};Q.prototype.tag=function(){return this.$val.tag();};Q.ptr.prototype.pkgPath=function(){var an;an=this;return"";};Q.prototype.pkgPath=function(){return this.$val.pkgPath();};Q.ptr.prototype.isExported=function(){var an,ao;an=this;return(ao=S[IA.keyFor(an.bytes)],ao!==undefined?ao.v:IF.nil).exported;};Q.prototype.isExported=function(){return this.$val.isExported();};T=function(an,ao,ap){var an,ao,ap,aq,ar;aq=$newDataPointer(0,IA);ar=aq;(S||$throwRuntimeError("assignment to entry in nil map"))[IA.keyFor(ar)]={k:ar,v:new R.ptr(an,ao,ap)};return new Q.ptr(aq);};CE.ptr.prototype.nameOff=function(an){var an,ao,ap;ao=this;return(ap=((an>>0)),((ap<0||ap>=U.$length)?($throwRuntimeError("index out of range"),undefined):U.$array[U.$offset+ap]));};CE.prototype.nameOff=function(an){return this.$val.nameOff(an);};V=function(an){var an,ao;ao=U.$length;U=$append(U,an);return((ao>>0));};CE.ptr.prototype.typeOff=function(an){var an,ao,ap;ao=this;return(ap=((an>>0)),((ap<0||ap>=W.$length)?($throwRuntimeError("index out of range"),undefined):W.$array[W.$offset+ap]));};CE.prototype.typeOff=function(an){return this.$val.typeOff(an);};X=function(an){var an,ao;ao=W.$length;W=$append(W,an);return((ao>>0));};Y=function(an){var an,ao;ao=new IG.ptr("");ao.str=an;return ao.str;};Z=function(an){var an;return!!(K(an).wrapped);};AA=function(an,ao,ap){var an,ao,ap,aq,ar,as;aq=K(ap).fields;ar=0;while(true){if(!(ar<$parseInt(aq.length))){break;}as=$internalize(aq[ar].prop,$String);an[$externalize(as,$String)]=ao[$externalize(as,$String)];ar=ar+(1)>>0;}};AB=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=an.common();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;au=an.Kind();$s=6;case 6:if($c){$c=false;au=au.$blk();}if(au&&au.$blk!==undefined){break s;}if(au===17){at=true;$s=5;continue s;}av=an.Kind();$s=7;case 7:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}at=av===25;case 5:if(at){as=true;$s=4;continue s;}aw=an.Kind();$s=8;case 8:if($c){$c=false;aw=aw.$blk();}if(aw&&aw.$blk!==undefined){break s;}as=aw===22;case 4:if(as){$s=2;continue;}$s=3;continue;case 2:ax=an.Kind();$s=9;case 9:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}$s=-1;return new EY.ptr(ar,(ao),(ap|((ax>>>0)))>>>0);case 3:ay=an.Kind();$s=10;case 10:if($c){$c=false;ay=ay.$blk();}if(ay&&ay.$blk!==undefined){break s;}$s=-1;return new EY.ptr(ar,($newDataPointer(ao,K(ar.ptrTo()))),(((ap|((ay>>>0)))>>>0)|128)>>>0);}return;}if($f===undefined){$f={$blk:AB};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.$s=$s;$f.$r=$r;return $f;};AC=function(an,ao,ap){var an,ao,ap,aq,ar,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=[an];aq=an[0].Kind();$s=3;case 3:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}if(!((aq===23))){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("reflect.MakeSlice of non-slice type"));case 2:if(ao<0){$panic(new $String("reflect.MakeSlice: negative len"));}if(ap<0){$panic(new $String("reflect.MakeSlice: negative cap"));}if(ao>ap){$panic(new $String("reflect.MakeSlice: len > cap"));}ar=AB(an[0],$makeSlice(K(an[0]),ao,ap,(function(an){return function $b(){var ar,as,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;ar=$f.ar;as=$f.as;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ar=an[0].Elem();$s=1;case 1:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}as=K(ar);$s=2;case 2:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}$s=-1;return as.zero();}return;}if($f===undefined){$f={$blk:$b};}$f.ar=ar;$f.as=as;$f.$s=$s;$f.$r=$r;return $f;};})(an)),0);$s=4;case 4:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}$s=-1;return ar;}return;}if($f===undefined){$f={$blk:AC};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.$s=$s;$f.$r=$r;return $f;};$pkg.MakeSlice=AC;AD=function(an){var an;if(!I){return new CE.ptr(0,0,0,0,0,0,0,HZ.nil,IA.nil,0,0);}if($interfaceIsEqual(an,$ifaceNil)){return $ifaceNil;}return L(an.constructor);};$pkg.TypeOf=AD;AE=function(an){var an,ao,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if($interfaceIsEqual(an,$ifaceNil)){$s=-1;return new EY.ptr(HR.nil,0,0);}ao=AB(L(an.constructor),an.$val,0);$s=1;case 1:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}$s=-1;return ao;}return;}if($f===undefined){$f={$blk:AE};}$f.an=an;$f.ao=ao;$f.$s=$s;$f.$r=$r;return $f;};$pkg.ValueOf=AE;AH=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(!(ap)){aq=false;$s=3;continue s;}if(an.$length===0){ar=true;$s=4;continue s;}at=(as=an.$length-1>>0,((as<0||as>=an.$length)?($throwRuntimeError("index out of range"),undefined):an.$array[an.$offset+as])).Kind();$s=5;case 5:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}ar=!((at===23));case 4:aq=ar;case 3:if(aq){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("reflect.FuncOf: last arg of variadic func must be slice"));case 2:au=$makeSlice(IH,an.$length);av=an;aw=0;while(true){if(!(aw=av.$length)?($throwRuntimeError("index out of range"),undefined):av.$array[av.$offset+aw]);((ax<0||ax>=au.$length)?($throwRuntimeError("index out of range"),undefined):au.$array[au.$offset+ax]=K(ay));aw++;}az=$makeSlice(IH,ao.$length);ba=ao;bb=0;while(true){if(!(bb=ba.$length)?($throwRuntimeError("index out of range"),undefined):ba.$array[ba.$offset+bb]);((bc<0||bc>=az.$length)?($throwRuntimeError("index out of range"),undefined):az.$array[az.$offset+bc]=K(bd));bb++;}$s=-1;return L($funcType($externalize(au,IH),$externalize(az,IH),$externalize(ap,$Bool)));}return;}if($f===undefined){$f={$blk:AH};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.$s=$s;$f.$r=$r;return $f;};$pkg.FuncOf=AH;CE.ptr.prototype.ptrTo=function(){var an;an=this;return L($ptrType(K(an)));};CE.prototype.ptrTo=function(){return this.$val.ptrTo();};AJ=function(an){var an;return L($sliceType(K(an)));};$pkg.SliceOf=AJ;AK=function(an){var an,ao,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=AB(an,K(an).zero(),0);$s=1;case 1:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}$s=-1;return ao;}return;}if($f===undefined){$f={$blk:AK};}$f.an=an;$f.ao=ao;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Zero=AK;AL=function(an){var an,ao;ao=an.Kind();if(ao===(25)){return(new(K(an).ptr)());}else if(ao===(17)){return(K(an).zero());}else{return($newDataPointer(K(an).zero(),K(an.ptrTo())));}};AM=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=ap.common();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;as=AL(ar);at=ar.Kind();if(at===(3)){(as).$set(((ao.$low<<24>>24)));}else if(at===(4)){(as).$set(((ao.$low<<16>>16)));}else if((at===(2))||(at===(5))){(as).$set(((ao.$low>>0)));}else if(at===(6)){(as).$set((new $Int64(ao.$high,ao.$low)));}else if(at===(8)){(as).$set(((ao.$low<<24>>>24)));}else if(at===(9)){(as).$set(((ao.$low<<16>>>16)));}else if((at===(7))||(at===(10))||(at===(12))){(as).$set(((ao.$low>>>0)));}else if(at===(11)){(as).$set((ao));}$s=-1;return new EY.ptr(ar,as,(((an|128)>>>0)|((ar.Kind()>>>0)))>>>0);}return;}if($f===undefined){$f={$blk:AM};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};AO=function(an,ao,ap){var an,ao,ap;ao.$set(ap.$get());};AS=function(an,ao){var an,ao,ap,aq;ap=ao;if(!(ap.$get===undefined)){ap=ap.$get();}aq=$internalize(K(an.Key()).keyFor(ap),$String);return[ap,aq];};AT=function(an,ao,ap){var an,ao,ap,aq,ar,as;aq=AS(an,ap);ar=aq[1];as=ao[$externalize(ar,$String)];if(as===undefined){return 0;}return($newDataPointer(as.v,K(DK(an.Elem()))));};AU=function(an,ao,ap,aq){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ar=AS(an,ap);as=ar[0];at=ar[1];au=aq.$get();av=an.Elem();aw=av.Kind();$s=3;case 3:if($c){$c=false;aw=aw.$blk();}if(aw&&aw.$blk!==undefined){break s;}if(aw===25){$s=1;continue;}$s=2;continue;case 1:ax=K(av).zero();AA(ax,au,av);au=ax;case 2:ay=new($global.Object)();ay.k=as;ay.v=au;ao[$externalize(at,$String)]=ay;$s=-1;return;}return;}if($f===undefined){$f={$blk:AU};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.$s=$s;$f.$r=$r;return $f;};AV=function(an,ao,ap){var an,ao,ap,aq,ar;aq=AS(an,ap);ar=aq[1];delete ao[$externalize(ar,$String)];};AW.ptr.prototype.skipUntilValidKey=function(){var an,ao;an=this;while(true){if(!(an.i<$parseInt(an.keys.length))){break;}ao=an.keys[an.i];if(!(an.m[$externalize($internalize(ao,$String),$String)]===undefined)){break;}an.i=an.i+(1)>>0;}};AW.prototype.skipUntilValidKey=function(){return this.$val.skipUntilValidKey();};AX=function(an,ao){var an,ao;return(new AW.ptr(an,ao,$keys(ao),0,null));};AY=function(an){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=($pointerOfStructConversion(an,IJ));ap=null;if(!(ao.last===null)){ap=ao.last;}else{ao.skipUntilValidKey();if(ao.i===$parseInt(ao.keys.length)){$s=-1;return 0;}aq=ao.keys[ao.i];ap=ao.m[$externalize($internalize(aq,$String),$String)];ao.last=ap;}ar=ao.t.Key();$s=1;case 1:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}as=DK(ar);$s=2;case 2:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}at=K(as);$s=3;case 3:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return($newDataPointer(ap.k,at));}return;}if($f===undefined){$f={$blk:AY};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};AZ=function(an){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=($pointerOfStructConversion(an,IJ));ap=null;if(!(ao.last===null)){ap=ao.last;}else{ao.skipUntilValidKey();if(ao.i===$parseInt(ao.keys.length)){$s=-1;return 0;}aq=ao.keys[ao.i];ap=ao.m[$externalize($internalize(aq,$String),$String)];ao.last=ap;}ar=ao.t.Elem();$s=1;case 1:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}as=DK(ar);$s=2;case 2:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}at=K(as);$s=3;case 3:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return($newDataPointer(ap.v,at));}return;}if($f===undefined){$f={$blk:AZ};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};BA=function(an){var an,ao;ao=($pointerOfStructConversion(an,IJ));ao.last=null;ao.i=ao.i+(1)>>0;};BB=function(an){var an;return $parseInt($keys(an).length);};BC=function(an,ao){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=$clone(an,EY).object();if(ap===K(an.typ).nil){$s=1;continue;}$s=2;continue;case 1:aq=AB(ao,K(ao).nil,an.flag);$s=3;case 3:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return aq;case 2:ar=null;as=ao.Kind();$s=5;case 5:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}at=as;au=at;if(au===(23)){$s=6;continue;}if(au===(22)){$s=7;continue;}if(au===(25)){$s=8;continue;}if((au===(17))||(au===(1))||(au===(18))||(au===(19))||(au===(20))||(au===(21))||(au===(24))){$s=9;continue;}$s=10;continue;case 6:av=new(K(ao))(ap.$array);av.$offset=ap.$offset;av.$length=ap.$length;av.$capacity=ap.$capacity;ar=$newDataPointer(av,K(DK(ao)));$s=11;continue;case 7:aw=ao.Elem();$s=14;case 14:if($c){$c=false;aw=aw.$blk();}if(aw&&aw.$blk!==undefined){break s;}ax=aw.Kind();$s=15;case 15:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}if(ax===25){$s=12;continue;}$s=13;continue;case 12:ay=ao.Elem();$s=18;case 18:if($c){$c=false;ay=ay.$blk();}if(ay&&ay.$blk!==undefined){break s;}if($interfaceIsEqual(ay,an.typ.Elem())){$s=16;continue;}$s=17;continue;case 16:ar=ap;$s=4;continue;case 17:ar=new(K(ao))();az=ar;ba=ap;bb=ao.Elem();$s=19;case 19:if($c){$c=false;bb=bb.$blk();}if(bb&&bb.$blk!==undefined){break s;}bc=bb;$r=AA(az,ba,bc);$s=20;case 20:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=4;continue;case 13:ar=new(K(ao))(ap.$get,ap.$set);$s=11;continue;case 8:ar=new(K(ao).ptr)();AA(ar,ap,ao);$s=11;continue;case 9:ar=an.ptr;$s=11;continue;case 10:$panic(new FC.ptr("reflect.Convert",at));case 11:case 4:bd=ao.common();$s=21;case 21:if($c){$c=false;bd=bd.$blk();}if(bd&&bd.$blk!==undefined){break s;}be=ao.Kind();$s=22;case 22:if($c){$c=false;be=be.$blk();}if(be&&be.$blk!==undefined){break s;}$s=-1;return new EY.ptr(bd,(ar),(((new EZ(an.flag).ro()|((an.flag&128)>>>0))>>>0)|((be>>>0)))>>>0);}return;}if($f===undefined){$f={$blk:BC};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.$s=$s;$f.$r=$r;return $f;};BE=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az;aq=HR.nil;ar=IK.nil;as=0;at="";if(ao.typ.Kind()===20){au=(ao.typ.kindType);if(ap<0||ap>=au.methods.$length){$panic(new $String("reflect: internal error: invalid method index"));}aw=(av=au.methods,((ap<0||ap>=av.$length)?($throwRuntimeError("index out of range"),undefined):av.$array[av.$offset+ap]));if(!$clone(au.rtype.nameOff(aw.name),Q).isExported()){$panic(new $String("reflect: "+an+" of unexported method"));}ar=(au.rtype.typeOff(aw.typ).kindType);at=$clone(au.rtype.nameOff(aw.name),Q).name();}else{ax=ao.typ.exportedMethods();if(((ap>>>0))>=((ax.$length>>>0))){$panic(new $String("reflect: internal error: invalid method index"));}ay=$clone(((ap<0||ap>=ax.$length)?($throwRuntimeError("index out of range"),undefined):ax.$array[ax.$offset+ap]),CG);if(!$clone(ao.typ.nameOff(ay.name),Q).isExported()){$panic(new $String("reflect: "+an+" of unexported method"));}ar=(ao.typ.typeOff(ay.mtyp).kindType);at=$internalize($methodSet(K(ao.typ))[ap].prop,$String);}az=$clone(ao,EY).object();if(Z(ao.typ)){az=new(K(ao.typ))(az);}as=(az[$externalize(at,$String)]);return[aq,ar,as];};BF=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(an.flag===0){$panic(new FC.ptr("reflect.Value.Interface",0));}if(ao&&!((((an.flag&96)>>>0)===0))){$panic(new $String("reflect.Value.Interface: cannot return value obtained from unexported field or method"));}if(!((((an.flag&512)>>>0)===0))){$s=1;continue;}$s=2;continue;case 1:ap=BI("Interface",$clone(an,EY));$s=3;case 3:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}an=ap;case 2:if(Z(an.typ)){$s=-1;return((new(K(an.typ))($clone(an,EY).object())));}$s=-1;return(($clone(an,EY).object()));}return;}if($f===undefined){$f={$blk:BF};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};BG=function(an,ao,ap){var an,ao,ap;ap.$set(ao);};BH=function(){return"?FIXME?";};BI=function(an,ao){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=[ap];aq=[aq];if(((ao.flag&512)>>>0)===0){$panic(new $String("reflect: internal error: invalid use of makePartialFunc"));}ar=BE(an,$clone(ao,EY),((ao.flag>>0))>>10>>0);ap[0]=ar[2];aq[0]=$clone(ao,EY).object();if(Z(ao.typ)){aq[0]=new(K(ao.typ))(aq[0]);}as=C.MakeFunc((function(ap,aq){return function(as,at){var as,at;return new $jsObjectPtr(ap[0].apply(aq[0],$externalize(at,IH)));};})(ap,aq));at=$clone(ao,EY).Type().common();$s=1;case 1:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return new EY.ptr(at,(as),(new EZ(ao.flag).ro()|19)>>>0);}return;}if($f===undefined){$f={$blk:BI};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};CE.ptr.prototype.pointers=function(){var an,ao;an=this;ao=an.Kind();if((ao===(22))||(ao===(21))||(ao===(18))||(ao===(19))||(ao===(25))||(ao===(17))){return true;}else{return false;}};CE.prototype.pointers=function(){return this.$val.pointers();};CE.ptr.prototype.Comparable=function(){var an,ao,ap,aq,ar,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;ao=an.Kind();if((ao===(19))||(ao===(23))||(ao===(21))){$s=2;continue;}if(ao===(17)){$s=3;continue;}if(ao===(25)){$s=4;continue;}$s=5;continue;case 2:$s=-1;return false;case 3:ap=an.Elem().Comparable();$s=6;case 6:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;case 4:aq=0;case 7:if(!(aq>0;$s=7;continue;case 8:case 5:case 1:$s=-1;return true;}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.Comparable};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.Comparable=function(){return this.$val.Comparable();};CE.ptr.prototype.Method=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=[ao];ap=new CS.ptr("","",$ifaceNil,new EY.ptr(HR.nil,0,0),0);aq=this;if(aq.Kind()===20){ar=(aq.kindType);CS.copy(ap,ar.Method(an));$s=-1;return ap;}as=aq.exportedMethods();if(an<0||an>=as.$length){$panic(new $String("reflect: Method index out of range"));}at=$clone(((an<0||an>=as.$length)?($throwRuntimeError("index out of range"),undefined):as.$array[as.$offset+an]),CG);au=$clone(aq.nameOff(at.name),Q);ap.Name=$clone(au,Q).name();av=19;aw=aq.typeOff(at.mtyp);ax=(aw.kindType);ay=$makeSlice(IL,0,(1+ax.in$().$length>>0));ay=$append(ay,aq);az=ax.in$();ba=0;while(true){if(!(ba=az.$length)?($throwRuntimeError("index out of range"),undefined):az.$array[az.$offset+ba]);ay=$append(ay,bb);ba++;}bc=$makeSlice(IL,0,ax.out().$length);bd=ax.out();be=0;while(true){if(!(be=bd.$length)?($throwRuntimeError("index out of range"),undefined):bd.$array[bd.$offset+be]);bc=$append(bc,bf);be++;}bg=AH(ay,bc,ax.rtype.IsVariadic());$s=1;case 1:if($c){$c=false;bg=bg.$blk();}if(bg&&bg.$blk!==undefined){break s;}bh=bg;ap.Type=bh;ao[0]=$internalize($methodSet(aq.jsType)[an].prop,$String);bi=C.MakeFunc((function(ao){return function(bi,bj){var bi,bj,bk;bk=(0>=bj.$length?($throwRuntimeError("index out of range"),undefined):bj.$array[bj.$offset+0]);return new $jsObjectPtr(bk[$externalize(ao[0],$String)].apply(bk,$externalize($subslice(bj,1),IH)));};})(ao));ap.Func=new EY.ptr($assertType(bh,HR),(bi),av);ap.Index=an;CS.copy(ap,ap);$s=-1;return ap;}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.Method};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.Method=function(an){return this.$val.Method(an);};EY.ptr.prototype.object=function(){var an,ao,ap,aq;an=this;if((an.typ.Kind()===17)||(an.typ.Kind()===25)){return an.ptr;}if(!((((an.flag&128)>>>0)===0))){ao=an.ptr.$get();if(!(ao===$ifaceNil)&&!(ao.constructor===K(an.typ))){switch(0){default:ap=an.typ.Kind();if((ap===(11))||(ap===(6))){ao=new(K(an.typ))(ao.$high,ao.$low);}else if((ap===(15))||(ap===(16))){ao=new(K(an.typ))(ao.$real,ao.$imag);}else if(ap===(23)){if(ao===ao.constructor.nil){ao=K(an.typ).nil;break;}aq=new(K(an.typ))(ao.$array);aq.$offset=ao.$offset;aq.$length=ao.$length;aq.$capacity=ao.$capacity;ao=aq;}}}return ao;}return an.ptr;};EY.prototype.object=function(){return this.$val.object();};EY.ptr.prototype.assignTo=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,au,av,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=this;if(!((((aq.flag&512)>>>0)===0))){$s=1;continue;}$s=2;continue;case 1:ar=BI(an,$clone(aq,EY));$s=3;case 3:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}aq=ar;case 2:as=DN(ao,aq.typ);$s=8;case 8:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}if(as){$s=5;continue;}if(DM(ao,aq.typ)){$s=6;continue;}$s=7;continue;case 5:at=(((aq.flag&384)>>>0)|new EZ(aq.flag).ro())>>>0;at=(at|(((ao.Kind()>>>0))))>>>0;$s=-1;return new EY.ptr(ao,aq.ptr,at);case 6:if(ap===0){ap=AL(ao);}au=BF($clone(aq,EY),false);$s=9;case 9:if($c){$c=false;au=au.$blk();}if(au&&au.$blk!==undefined){break s;}av=au;if(ao.NumMethod()===0){(ap).$set(av);}else{BG(ao,av,ap);}$s=-1;return new EY.ptr(ao,ap,148);case 7:case 4:$panic(new $String(an+": value of type "+aq.typ.String()+" is not assignable to type "+ao.String()));$s=-1;return new EY.ptr(HR.nil,0,0);}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.assignTo};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.assignTo=function(an,ao,ap){return this.$val.assignTo(an,ao,ap);};EY.ptr.prototype.call=function(an,ao){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,bx,by,bz,ca,cb,cc,cd,ce,cf,cg,ch,ci,cj,ck,cl,cm,cn,co,cp,cq,cr,cs,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;bj=$f.bj;bk=$f.bk;bl=$f.bl;bm=$f.bm;bn=$f.bn;bo=$f.bo;bp=$f.bp;bq=$f.bq;br=$f.br;bs=$f.bs;bt=$f.bt;bu=$f.bu;bv=$f.bv;bw=$f.bw;bx=$f.bx;by=$f.by;bz=$f.bz;ca=$f.ca;cb=$f.cb;cc=$f.cc;cd=$f.cd;ce=$f.ce;cf=$f.cf;cg=$f.cg;ch=$f.ch;ci=$f.ci;cj=$f.cj;ck=$f.ck;cl=$f.cl;cm=$f.cm;cn=$f.cn;co=$f.co;cp=$f.cp;cq=$f.cq;cr=$f.cr;cs=$f.cs;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=this;aq=IK.nil;ar=0;as=null;if(!((((ap.flag&512)>>>0)===0))){at=BE(an,$clone(ap,EY),((ap.flag>>0))>>10>>0);aq=at[1];ar=at[2];as=$clone(ap,EY).object();if(Z(ap.typ)){as=new(K(ap.typ))(as);}}else{aq=(ap.typ.kindType);ar=($clone(ap,EY).object());as=undefined;}if(ar===0){$panic(new $String("reflect.Value.Call: call of nil function"));}au=an==="CallSlice";av=aq.rtype.NumIn();if(au){if(!aq.rtype.IsVariadic()){$panic(new $String("reflect: CallSlice of non-variadic function"));}if(ao.$lengthav){$panic(new $String("reflect: CallSlice with too many input arguments"));}}else{if(aq.rtype.IsVariadic()){av=av-(1)>>0;}if(ao.$lengthav){$panic(new $String("reflect: Call with too many input arguments"));}}aw=ao;ax=0;while(true){if(!(ax=aw.$length)?($throwRuntimeError("index out of range"),undefined):aw.$array[aw.$offset+ax]);if($clone(ay,EY).Kind()===0){$panic(new $String("reflect: "+an+" using zero Value argument"));}ax++;}az=0;case 1:if(!(az=ao.$length)?($throwRuntimeError("index out of range"),undefined):ao.$array[ao.$offset+az]),EY).Type();bb=aq.rtype.In(az);bc=ba;bd=bb;be=bc.AssignableTo(bd);$s=5;case 5:if($c){$c=false;be=be.$blk();}if(be&&be.$blk!==undefined){break s;}if(!be){$s=3;continue;}$s=4;continue;case 3:bf=bc.String();$s=6;case 6:if($c){$c=false;bf=bf.$blk();}if(bf&&bf.$blk!==undefined){break s;}bg=bd.String();$s=7;case 7:if($c){$c=false;bg=bg.$blk();}if(bg&&bg.$blk!==undefined){break s;}$panic(new $String("reflect: "+an+" using "+bf+" as type "+bg));case 4:az=az+(1)>>0;$s=1;continue;case 2:if(!au&&aq.rtype.IsVariadic()){$s=8;continue;}$s=9;continue;case 8:bh=ao.$length-av>>0;bi=AC(aq.rtype.In(av),bh,bh);$s=10;case 10:if($c){$c=false;bi=bi.$blk();}if(bi&&bi.$blk!==undefined){break s;}bj=bi;bk=aq.rtype.In(av).Elem();$s=11;case 11:if($c){$c=false;bk=bk.$blk();}if(bk&&bk.$blk!==undefined){break s;}bl=bk;bm=0;case 12:if(!(bm>0,((bn<0||bn>=ao.$length)?($throwRuntimeError("index out of range"),undefined):ao.$array[ao.$offset+bn]));bp=$clone(bo,EY).Type();bq=bp.AssignableTo(bl);$s=16;case 16:if($c){$c=false;bq=bq.$blk();}if(bq&&bq.$blk!==undefined){break s;}if(!bq){$s=14;continue;}$s=15;continue;case 14:br=bp.String();$s=17;case 17:if($c){$c=false;br=br.$blk();}if(br&&br.$blk!==undefined){break s;}bs=bl.String();$s=18;case 18:if($c){$c=false;bs=bs.$blk();}if(bs&&bs.$blk!==undefined){break s;}$panic(new $String("reflect: cannot use "+br+" as type "+bs+" in "+an));case 15:bt=$clone(bj,EY).Index(bm);$s=19;case 19:if($c){$c=false;bt=bt.$blk();}if(bt&&bt.$blk!==undefined){break s;}$r=$clone(bt,EY).Set($clone(bo,EY));$s=20;case 20:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}bm=bm+(1)>>0;$s=12;continue;case 13:bu=ao;ao=$makeSlice(II,(av+1>>0));$copySlice($subslice(ao,0,av),bu);((av<0||av>=ao.$length)?($throwRuntimeError("index out of range"),undefined):ao.$array[ao.$offset+av]=bj);case 9:bv=ao.$length;if(!((bv===aq.rtype.NumIn()))){$panic(new $String("reflect.Value.Call: wrong argument count"));}bw=aq.rtype.NumOut();bx=new($global.Array)(aq.rtype.NumIn());by=ao;bz=0;case 21:if(!(bz=by.$length)?($throwRuntimeError("index out of range"),undefined):by.$array[by.$offset+bz]);cc=aq.rtype.In(ca);cd=aq.rtype.In(ca).common();$s=23;case 23:if($c){$c=false;cd=cd.$blk();}if(cd&&cd.$blk!==undefined){break s;}ce=cd;cf=0;cg=$clone(cb,EY).assignTo("reflect.Value.Call",ce,cf);$s=24;case 24:if($c){$c=false;cg=cg.$blk();}if(cg&&cg.$blk!==undefined){break s;}ch=$clone(cg,EY).object();$s=25;case 25:if($c){$c=false;ch=ch.$blk();}if(ch&&ch.$blk!==undefined){break s;}ci=ch;cj=BM(cc,ci);$s=26;case 26:if($c){$c=false;cj=cj.$blk();}if(cj&&cj.$blk!==undefined){break s;}bx[ca]=cj;bz++;$s=21;continue;case 22:ck=BJ(new HV([new $jsObjectPtr(ar),new $jsObjectPtr(as),new $jsObjectPtr(bx)]));$s=27;case 27:if($c){$c=false;ck=ck.$blk();}if(ck&&ck.$blk!==undefined){break s;}cl=ck;cm=bw;if(cm===(0)){$s=29;continue;}if(cm===(1)){$s=30;continue;}$s=31;continue;case 29:$s=-1;return II.nil;case 30:cn=AB(aq.rtype.Out(0),BL(aq.rtype.Out(0),cl),0);$s=33;case 33:if($c){$c=false;cn=cn.$blk();}if(cn&&cn.$blk!==undefined){break s;}$s=-1;return new II([$clone(cn,EY)]);case 31:co=$makeSlice(II,bw);cp=co;cq=0;case 34:if(!(cq=co.$length)?($throwRuntimeError("index out of range"),undefined):co.$array[co.$offset+cr]=cs);cq++;$s=34;continue;case 35:$s=-1;return co;case 32:case 28:$s=-1;return II.nil;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.call};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.bj=bj;$f.bk=bk;$f.bl=bl;$f.bm=bm;$f.bn=bn;$f.bo=bo;$f.bp=bp;$f.bq=bq;$f.br=br;$f.bs=bs;$f.bt=bt;$f.bu=bu;$f.bv=bv;$f.bw=bw;$f.bx=bx;$f.by=by;$f.bz=bz;$f.ca=ca;$f.cb=cb;$f.cc=cc;$f.cd=cd;$f.ce=ce;$f.cf=cf;$f.cg=cg;$f.ch=ch;$f.ci=ci;$f.cj=cj;$f.ck=ck;$f.cl=cl;$f.cm=cm;$f.cn=cn;$f.co=co;$f.cp=cp;$f.cq=cq;$f.cr=cr;$f.cs=cs;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.call=function(an,ao){return this.$val.call(an,ao);};EY.ptr.prototype.Cap=function(){var an,ao,ap;an=this;ao=new EZ(an.flag).kind();ap=ao;if(ap===(17)){return an.typ.Len();}else if((ap===(18))||(ap===(23))){return $parseInt($clone(an,EY).object().$capacity)>>0;}$panic(new FC.ptr("reflect.Value.Cap",ao));};EY.prototype.Cap=function(){return this.$val.Cap();};BL=function(an,ao){var an,ao;if($interfaceIsEqual(an,BK)){return new(K(BK))(ao);}return ao;};BM=function(an,ao){var an,ao;if($interfaceIsEqual(an,BK)){return ao.object;}return ao;};EY.ptr.prototype.Elem=function(){var an,ao,ap,aq,ar,as,at,au,av,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;ao=new EZ(an.flag).kind();ap=ao;if(ap===(20)){$s=2;continue;}if(ap===(22)){$s=3;continue;}$s=4;continue;case 2:aq=$clone(an,EY).object();if(aq===$ifaceNil){$s=-1;return new EY.ptr(HR.nil,0,0);}ar=L(aq.constructor);as=AB(ar,aq.$val,new EZ(an.flag).ro());$s=6;case 6:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}$s=-1;return as;case 3:if($clone(an,EY).IsNil()){$s=-1;return new EY.ptr(HR.nil,0,0);}at=$clone(an,EY).object();au=(an.typ.kindType);av=(((((an.flag&96)>>>0)|128)>>>0)|256)>>>0;av=(av|(((au.elem.Kind()>>>0))))>>>0;$s=-1;return new EY.ptr(au.elem,(BL(au.elem,at)),av);case 4:$panic(new FC.ptr("reflect.Value.Elem",ao));case 5:case 1:$s=-1;return new EY.ptr(HR.nil,0,0);}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Elem};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Elem=function(){return this.$val.Elem();};EY.ptr.prototype.Field=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=[ao];ap=[ap];aq=[aq];ar=[ar];as=this;if(!((new EZ(as.flag).kind()===25))){$panic(new FC.ptr("reflect.Value.Field",new EZ(as.flag).kind()));}at=(as.typ.kindType);if(((an>>>0))>=((at.fields.$length>>>0))){$panic(new $String("reflect: Field index out of range"));}ap[0]=$internalize(K(as.typ).fields[an].prop,$String);av=(au=at.fields,((an<0||an>=au.$length)?($throwRuntimeError("index out of range"),undefined):au.$array[au.$offset+an]));ar[0]=av.typ;aw=(((as.flag&416)>>>0)|((ar[0].Kind()>>>0)))>>>0;if(!$clone(av.name,Q).isExported()){if(av.embedded()){aw=(aw|(64))>>>0;}else{aw=(aw|(32))>>>0;}}ay=$clone((ax=at.fields,((an<0||an>=ax.$length)?($throwRuntimeError("index out of range"),undefined):ax.$array[ax.$offset+an])).name,Q).tag();if(!(ay==="")&&!((an===0))){$s=1;continue;}$s=2;continue;case 1:ao[0]=BN(ay);if(!(ao[0]==="")){$s=3;continue;}$s=4;continue;case 3:case 5:az=[az];ba=$clone(as,EY).Field(0);$s=7;case 7:if($c){$c=false;ba=ba.$blk();}if(ba&&ba.$blk!==undefined){break s;}as=ba;if(as.typ===BK){$s=8;continue;}$s=9;continue;case 8:az[0]=$clone(as,EY).object().object;$s=-1;return new EY.ptr(ar[0],(new(K(DK(ar[0])))((function(ao,ap,aq,ar,az){return function(){return $internalize(az[0][$externalize(ao[0],$String)],K(ar[0]));};})(ao,ap,aq,ar,az),(function(ao,ap,aq,ar,az){return function(bb){var bb;az[0][$externalize(ao[0],$String)]=$externalize(bb,K(ar[0]));};})(ao,ap,aq,ar,az))),aw);case 9:if(as.typ.Kind()===22){$s=10;continue;}$s=11;continue;case 10:bb=$clone(as,EY).Elem();$s=12;case 12:if($c){$c=false;bb=bb.$blk();}if(bb&&bb.$blk!==undefined){break s;}as=bb;case 11:$s=5;continue;case 6:case 4:case 2:aq[0]=as.ptr;if(!((((aw&128)>>>0)===0))&&!((ar[0].Kind()===17))&&!((ar[0].Kind()===25))){$s=13;continue;}$s=14;continue;case 13:$s=-1;return new EY.ptr(ar[0],(new(K(DK(ar[0])))((function(ao,ap,aq,ar){return function(){return BL(ar[0],aq[0][$externalize(ap[0],$String)]);};})(ao,ap,aq,ar),(function(ao,ap,aq,ar){return function(bc){var bc;aq[0][$externalize(ap[0],$String)]=BM(ar[0],bc);};})(ao,ap,aq,ar))),aw);case 14:bc=AB(ar[0],BL(ar[0],aq[0][$externalize(ap[0],$String)]),aw);$s=15;case 15:if($c){$c=false;bc=bc.$blk();}if(bc&&bc.$blk!==undefined){break s;}$s=-1;return bc;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Field};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Field=function(an){return this.$val.Field(an);};BN=function(an){var an,ao,ap,aq,ar,as;while(true){if(!(!(an===""))){break;}ao=0;while(true){if(!(ao>0;}an=$substring(an,ao);if(an===""){break;}ao=0;while(true){if(!(ao>0;}if((ao+1>>0)>=an.length||!((an.charCodeAt(ao)===58))||!((an.charCodeAt((ao+1>>0))===34))){break;}ap=($substring(an,0,ao));an=$substring(an,(ao+1>>0));ao=1;while(true){if(!(ao>0;}ao=ao+(1)>>0;}if(ao>=an.length){break;}aq=($substring(an,0,(ao+1>>0)));an=$substring(an,(ao+1>>0));if(ap==="js"){ar=B.Unquote(aq);as=ar[0];return as;}}return"";};EY.ptr.prototype.Index=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=[an];ao=[ao];ap=[ap];aq=[aq];ar=[ar];as=[as];at=this;au=new EZ(at.flag).kind();av=au;if(av===(17)){$s=2;continue;}if(av===(23)){$s=3;continue;}if(av===(24)){$s=4;continue;}$s=5;continue;case 2:aw=(at.typ.kindType);if(an[0]<0||an[0]>((aw.len>>0))){$panic(new $String("reflect: array index out of range"));}ar[0]=aw.elem;ax=(((((at.flag&384)>>>0)|new EZ(at.flag).ro())>>>0)|((ar[0].Kind()>>>0)))>>>0;ao[0]=at.ptr;if(!((((ax&128)>>>0)===0))&&!((ar[0].Kind()===17))&&!((ar[0].Kind()===25))){$s=7;continue;}$s=8;continue;case 7:$s=-1;return new EY.ptr(ar[0],(new(K(DK(ar[0])))((function(an,ao,ap,aq,ar,as){return function(){return BL(ar[0],ao[0][an[0]]);};})(an,ao,ap,aq,ar,as),(function(an,ao,ap,aq,ar,as){return function(ay){var ay;ao[0][an[0]]=BM(ar[0],ay);};})(an,ao,ap,aq,ar,as))),ax);case 8:ay=AB(ar[0],BL(ar[0],ao[0][an[0]]),ax);$s=9;case 9:if($c){$c=false;ay=ay.$blk();}if(ay&&ay.$blk!==undefined){break s;}$s=-1;return ay;case 3:az=$clone(at,EY).object();if(an[0]<0||an[0]>=($parseInt(az.$length)>>0)){$panic(new $String("reflect: slice index out of range"));}ba=(at.typ.kindType);as[0]=ba.elem;bb=(((384|new EZ(at.flag).ro())>>>0)|((as[0].Kind()>>>0)))>>>0;an[0]=an[0]+(($parseInt(az.$offset)>>0))>>0;ap[0]=az.$array;if(!((((bb&128)>>>0)===0))&&!((as[0].Kind()===17))&&!((as[0].Kind()===25))){$s=10;continue;}$s=11;continue;case 10:$s=-1;return new EY.ptr(as[0],(new(K(DK(as[0])))((function(an,ao,ap,aq,ar,as){return function(){return BL(as[0],ap[0][an[0]]);};})(an,ao,ap,aq,ar,as),(function(an,ao,ap,aq,ar,as){return function(bc){var bc;ap[0][an[0]]=BM(as[0],bc);};})(an,ao,ap,aq,ar,as))),bb);case 11:bc=AB(as[0],BL(as[0],ap[0][an[0]]),bb);$s=12;case 12:if($c){$c=false;bc=bc.$blk();}if(bc&&bc.$blk!==undefined){break s;}$s=-1;return bc;case 4:bd=(at.ptr).$get();if(an[0]<0||an[0]>=bd.length){$panic(new $String("reflect: string index out of range"));}be=(((new EZ(at.flag).ro()|8)>>>0)|128)>>>0;aq[0]=bd.charCodeAt(an[0]);$s=-1;return new EY.ptr(FL,((aq.$ptr||(aq.$ptr=new IA(function(){return this.$target[0];},function($v){this.$target[0]=$v;},aq)))),be);case 5:$panic(new FC.ptr("reflect.Value.Index",au));case 6:case 1:$s=-1;return new EY.ptr(HR.nil,0,0);}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Index};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Index=function(an){return this.$val.Index(an);};EY.ptr.prototype.InterfaceData=function(){var an;an=this;$panic(A.New("InterfaceData is not supported by GopherJS"));};EY.prototype.InterfaceData=function(){return this.$val.InterfaceData();};EY.ptr.prototype.IsNil=function(){var an,ao,ap;an=this;ao=new EZ(an.flag).kind();ap=ao;if((ap===(22))||(ap===(23))){return $clone(an,EY).object()===K(an.typ).nil;}else if(ap===(18)){return $clone(an,EY).object()===$chanNil;}else if(ap===(19)){return $clone(an,EY).object()===$throwNilPointerError;}else if(ap===(21)){return $clone(an,EY).object()===false;}else if(ap===(20)){return $clone(an,EY).object()===$ifaceNil;}else if(ap===(26)){return $clone(an,EY).object()===0;}else{$panic(new FC.ptr("reflect.Value.IsNil",ao));}};EY.prototype.IsNil=function(){return this.$val.IsNil();};EY.ptr.prototype.Len=function(){var an,ao,ap;an=this;ao=new EZ(an.flag).kind();ap=ao;if((ap===(17))||(ap===(24))){return $parseInt($clone(an,EY).object().length);}else if(ap===(23)){return $parseInt($clone(an,EY).object().$length)>>0;}else if(ap===(18)){return $parseInt($clone(an,EY).object().$buffer.length)>>0;}else if(ap===(21)){return $parseInt($keys($clone(an,EY).object()).length);}else{$panic(new FC.ptr("reflect.Value.Len",ao));}};EY.prototype.Len=function(){return this.$val.Len();};EY.ptr.prototype.Pointer=function(){var an,ao,ap;an=this;ao=new EZ(an.flag).kind();ap=ao;if((ap===(18))||(ap===(21))||(ap===(22))||(ap===(26))){if($clone(an,EY).IsNil()){return 0;}return $clone(an,EY).object();}else if(ap===(19)){if($clone(an,EY).IsNil()){return 0;}return 1;}else if(ap===(23)){if($clone(an,EY).IsNil()){return 0;}return $clone(an,EY).object().$array;}else{$panic(new FC.ptr("reflect.Value.Pointer",ao));}};EY.prototype.Pointer=function(){return this.$val.Pointer();};EY.ptr.prototype.Set=function(an){var an,ao,ap,aq,ar,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBeAssignable();new EZ(an.flag).mustBeExported();ap=$clone(an,EY).assignTo("reflect.Set",ao.typ,0);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}an=ap;if(!((((ao.flag&128)>>>0)===0))){$s=2;continue;}$s=3;continue;case 2:aq=ao.typ.Kind();if(aq===(17)){$s=5;continue;}if(aq===(20)){$s=6;continue;}if(aq===(25)){$s=7;continue;}$s=8;continue;case 5:K(ao.typ).copy(ao.ptr,an.ptr);$s=9;continue;case 6:ar=BF($clone(an,EY),false);$s=10;case 10:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}ao.ptr.$set(ar);$s=9;continue;case 7:AA(ao.ptr,an.ptr,ao.typ);$s=9;continue;case 8:ao.ptr.$set($clone(an,EY).object());case 9:case 4:$s=-1;return;case 3:ao.ptr=an.ptr;$s=-1;return;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Set};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Set=function(an){return this.$val.Set(an);};EY.ptr.prototype.SetBytes=function(an){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBeAssignable();new EZ(ao.flag).mustBe(23);ap=ao.typ.Elem().Kind();$s=3;case 3:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}if(!((ap===8))){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("reflect.Value.SetBytes of non-byte slice"));case 2:aq=an;if(!(ao.typ.Name()==="")){ar=true;$s=6;continue s;}as=ao.typ.Elem().Name();$s=7;case 7:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}ar=!(as==="");case 6:if(ar){$s=4;continue;}$s=5;continue;case 4:at=new(K(ao.typ))(aq.$array);at.$offset=aq.$offset;at.$length=aq.$length;at.$capacity=aq.$capacity;aq=at;case 5:ao.ptr.$set(aq);$s=-1;return;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.SetBytes};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.SetBytes=function(an){return this.$val.SetBytes(an);};EY.ptr.prototype.SetCap=function(an){var an,ao,ap,aq;ao=this;new EZ(ao.flag).mustBeAssignable();new EZ(ao.flag).mustBe(23);ap=ao.ptr.$get();if(an<($parseInt(ap.$length)>>0)||an>($parseInt(ap.$capacity)>>0)){$panic(new $String("reflect: slice capacity out of range in SetCap"));}aq=new(K(ao.typ))(ap.$array);aq.$offset=ap.$offset;aq.$length=ap.$length;aq.$capacity=an;ao.ptr.$set(aq);};EY.prototype.SetCap=function(an){return this.$val.SetCap(an);};EY.ptr.prototype.SetLen=function(an){var an,ao,ap,aq;ao=this;new EZ(ao.flag).mustBeAssignable();new EZ(ao.flag).mustBe(23);ap=ao.ptr.$get();if(an<0||an>($parseInt(ap.$capacity)>>0)){$panic(new $String("reflect: slice length out of range in SetLen"));}aq=new(K(ao.typ))(ap.$array);aq.$offset=ap.$offset;aq.$length=an;aq.$capacity=ap.$capacity;ao.ptr.$set(aq);};EY.prototype.SetLen=function(an){return this.$val.SetLen(an);};EY.ptr.prototype.Slice=function(an,ao){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=this;aq=0;ar=$ifaceNil;as=null;at=new EZ(ap.flag).kind();au=at;if(au===(17)){$s=2;continue;}if(au===(23)){$s=3;continue;}if(au===(24)){$s=4;continue;}$s=5;continue;case 2:if(((ap.flag&256)>>>0)===0){$panic(new $String("reflect.Value.Slice: slice of unaddressable array"));}av=(ap.typ.kindType);aq=((av.len>>0));ar=AJ(av.elem);as=new(K(ar))($clone(ap,EY).object());$s=6;continue;case 3:ar=ap.typ;as=$clone(ap,EY).object();aq=$parseInt(as.$capacity)>>0;$s=6;continue;case 4:aw=(ap.ptr).$get();if(an<0||aoaw.length){$panic(new $String("reflect.Value.Slice: string slice index out of bounds"));}ax=AE(new $String($substring(aw,an,ao)));$s=7;case 7:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}$s=-1;return ax;case 5:$panic(new FC.ptr("reflect.Value.Slice",at));case 6:case 1:if(an<0||aoaq){$panic(new $String("reflect.Value.Slice: slice index out of bounds"));}ay=AB(ar,$subslice(as,an,ao),new EZ(ap.flag).ro());$s=8;case 8:if($c){$c=false;ay=ay.$blk();}if(ay&&ay.$blk!==undefined){break s;}$s=-1;return ay;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Slice};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Slice=function(an,ao){return this.$val.Slice(an,ao);};EY.ptr.prototype.Slice3=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=this;ar=0;as=$ifaceNil;at=null;au=new EZ(aq.flag).kind();av=au;if(av===(17)){if(((aq.flag&256)>>>0)===0){$panic(new $String("reflect.Value.Slice: slice of unaddressable array"));}aw=(aq.typ.kindType);ar=((aw.len>>0));as=AJ(aw.elem);at=new(K(as))($clone(aq,EY).object());}else if(av===(23)){as=aq.typ;at=$clone(aq,EY).object();ar=$parseInt(at.$capacity)>>0;}else{$panic(new FC.ptr("reflect.Value.Slice3",au));}if(an<0||aoar){$panic(new $String("reflect.Value.Slice3: slice index out of bounds"));}ax=AB(as,$subslice(at,an,ao,ap),new EZ(aq.flag).ro());$s=1;case 1:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}$s=-1;return ax;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Slice3};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Slice3=function(an,ao,ap){return this.$val.Slice3(an,ao,ap);};EY.ptr.prototype.Close=function(){var an;an=this;new EZ(an.flag).mustBe(18);new EZ(an.flag).mustBeExported();$close($clone(an,EY).object());};EY.prototype.Close=function(){return this.$val.Close();};BP=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=false;ar=false;as=new IM([new IH([an])]);if(ao){as=$append(as,new IH([]));}at=BO(new HV([as]));$s=1;case 1:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}au=at;if(ao&&(($parseInt(au[0])>>0)===1)){av=false;aw=false;aq=av;ar=aw;$s=-1;return[aq,ar];}ax=au[1];ap.$set(ax[0]);ay=true;az=!!(ax[1]);aq=ay;ar=az;$s=-1;return[aq,ar];}return;}if($f===undefined){$f={$blk:BP};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.$s=$s;$f.$r=$r;return $f;};BQ=function(an,ao,ap){var an,ao,ap,aq,ar,as,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=new IM([new IH([an,ao.$get()])]);if(ap){aq=$append(aq,new IH([]));}ar=BO(new HV([aq]));$s=1;case 1:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}as=ar;if(ap&&(($parseInt(as[0])>>0)===1)){$s=-1;return false;}$s=-1;return true;}return;}if($f===undefined){$f={$blk:BQ};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.$s=$s;$f.$r=$r;return $f;};CP.ptr.prototype.offset=function(){var an;an=this;return an.offsetEmbed>>>1>>>0;};CP.prototype.offset=function(){return this.$val.offset();};CP.ptr.prototype.embedded=function(){var an;an=this;return!((((an.offsetEmbed&1)>>>0)===0));};CP.prototype.embedded=function(){return this.$val.embedded();};CC.prototype.String=function(){var an;an=this.$val;if(((an>>0))=CT.$length)?($throwRuntimeError("index out of range"),undefined):CT.$array[CT.$offset+an]);}return"kind"+B.Itoa(((an>>0)));};$ptrType(CC).prototype.String=function(){return new CC(this.$get()).String();};CE.ptr.prototype.String=function(){var an,ao;an=this;ao=$clone(an.nameOff(an.str),Q).name();if(!((((an.tflag&2)>>>0)===0))){return $substring(ao,1);}return ao;};CE.prototype.String=function(){return this.$val.String();};CE.ptr.prototype.Size=function(){var an;an=this;return an.size;};CE.prototype.Size=function(){return this.$val.Size();};CE.ptr.prototype.Bits=function(){var an,ao;an=this;if(an===HR.nil){$panic(new $String("reflect: Bits of nil Type"));}ao=an.Kind();if(ao<2||ao>16){$panic(new $String("reflect: Bits of non-arithmetic Type "+an.String()));}return $imul(((an.size>>0)),8);};CE.prototype.Bits=function(){return this.$val.Bits();};CE.ptr.prototype.Align=function(){var an;an=this;return((an.align>>0));};CE.prototype.Align=function(){return this.$val.Align();};CE.ptr.prototype.FieldAlign=function(){var an;an=this;return((an.fieldAlign>>0));};CE.prototype.FieldAlign=function(){return this.$val.FieldAlign();};CE.ptr.prototype.Kind=function(){var an;an=this;return((((an.kind&31)>>>0)>>>0));};CE.prototype.Kind=function(){return this.$val.Kind();};CE.ptr.prototype.common=function(){var an;an=this;return an;};CE.prototype.common=function(){return this.$val.common();};CE.ptr.prototype.exportedMethods=function(){var an,ao;an=this;ao=an.uncommon();if(ao===IE.nil){return IB.nil;}return ao.exportedMethods();};CE.prototype.exportedMethods=function(){return this.$val.exportedMethods();};CE.ptr.prototype.NumMethod=function(){var an,ao;an=this;if(an.Kind()===20){ao=(an.kindType);return ao.NumMethod();}return an.exportedMethods().$length;};CE.prototype.NumMethod=function(){return this.$val.NumMethod();};CE.ptr.prototype.MethodByName=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=new CS.ptr("","",$ifaceNil,new EY.ptr(HR.nil,0,0),0);ap=false;aq=this;if(aq.Kind()===20){ar=(aq.kindType);as=ar.MethodByName(an);CS.copy(ao,as[0]);ap=as[1];$s=-1;return[ao,ap];}at=aq.uncommon();if(at===IE.nil){au=new CS.ptr("","",$ifaceNil,new EY.ptr(HR.nil,0,0),0);av=false;CS.copy(ao,au);ap=av;$s=-1;return[ao,ap];}aw=at.exportedMethods();ax=0;case 1:if(!(ax=aw.$length)?($throwRuntimeError("index out of range"),undefined):aw.$array[aw.$offset+ax]),CG);if($clone(aq.nameOff(az.name),Q).name()===an){$s=3;continue;}$s=4;continue;case 3:bb=aq.Method(ay);$s=5;case 5:if($c){$c=false;bb=bb.$blk();}if(bb&&bb.$blk!==undefined){break s;}ba=$clone(bb,CS);bc=true;CS.copy(ao,ba);ap=bc;$s=-1;return[ao,ap];case 4:ax++;$s=1;continue;case 2:bd=new CS.ptr("","",$ifaceNil,new EY.ptr(HR.nil,0,0),0);be=false;CS.copy(ao,bd);ap=be;$s=-1;return[ao,ap];}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.MethodByName};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.MethodByName=function(an){return this.$val.MethodByName(an);};CE.ptr.prototype.PkgPath=function(){var an,ao;an=this;if(((an.tflag&4)>>>0)===0){return"";}ao=an.uncommon();if(ao===IE.nil){return"";}return $clone(an.nameOff(ao.pkgPath),Q).name();};CE.prototype.PkgPath=function(){return this.$val.PkgPath();};CE.ptr.prototype.Name=function(){var an,ao,ap;an=this;if(((an.tflag&4)>>>0)===0){return"";}ao=an.String();ap=ao.length-1>>0;while(true){if(!(ap>=0)){break;}if(ao.charCodeAt(ap)===46){break;}ap=ap-(1)>>0;}return $substring(ao,(ap+1>>0));};CE.prototype.Name=function(){return this.$val.Name();};CE.ptr.prototype.ChanDir=function(){var an,ao;an=this;if(!((an.Kind()===18))){$panic(new $String("reflect: ChanDir of non-chan type"));}ao=(an.kindType);return((ao.dir>>0));};CE.prototype.ChanDir=function(){return this.$val.ChanDir();};CE.ptr.prototype.IsVariadic=function(){var an,ao;an=this;if(!((an.Kind()===19))){$panic(new $String("reflect: IsVariadic of non-func type"));}ao=(an.kindType);return!((((ao.outCount&32768)>>>0)===0));};CE.prototype.IsVariadic=function(){return this.$val.IsVariadic();};CE.ptr.prototype.Elem=function(){var an,ao,ap,aq,ar,as,at;an=this;ao=an.Kind();if(ao===(17)){ap=(an.kindType);return EQ(ap.elem);}else if(ao===(18)){aq=(an.kindType);return EQ(aq.elem);}else if(ao===(21)){ar=(an.kindType);return EQ(ar.elem);}else if(ao===(22)){as=(an.kindType);return EQ(as.elem);}else if(ao===(23)){at=(an.kindType);return EQ(at.elem);}$panic(new $String("reflect: Elem of invalid type"));};CE.prototype.Elem=function(){return this.$val.Elem();};CE.ptr.prototype.Field=function(an){var an,ao,ap;ao=this;if(!((ao.Kind()===25))){$panic(new $String("reflect: Field of non-struct type"));}ap=(ao.kindType);return ap.Field(an);};CE.prototype.Field=function(an){return this.$val.Field(an);};CE.ptr.prototype.FieldByIndex=function(an){var an,ao,ap,aq,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if(!((ao.Kind()===25))){$panic(new $String("reflect: FieldByIndex of non-struct type"));}ap=(ao.kindType);aq=ap.FieldByIndex(an);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return aq;}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.FieldByIndex};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.FieldByIndex=function(an){return this.$val.FieldByIndex(an);};CE.ptr.prototype.FieldByName=function(an){var an,ao,ap,aq,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if(!((ao.Kind()===25))){$panic(new $String("reflect: FieldByName of non-struct type"));}ap=(ao.kindType);aq=ap.FieldByName(an);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return aq;}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.FieldByName};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.FieldByName=function(an){return this.$val.FieldByName(an);};CE.ptr.prototype.FieldByNameFunc=function(an){var an,ao,ap,aq,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if(!((ao.Kind()===25))){$panic(new $String("reflect: FieldByNameFunc of non-struct type"));}ap=(ao.kindType);aq=ap.FieldByNameFunc(an);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return aq;}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.FieldByNameFunc};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.FieldByNameFunc=function(an){return this.$val.FieldByNameFunc(an);};CE.ptr.prototype.In=function(an){var an,ao,ap,aq;ao=this;if(!((ao.Kind()===19))){$panic(new $String("reflect: In of non-func type"));}ap=(ao.kindType);return EQ((aq=ap.in$(),((an<0||an>=aq.$length)?($throwRuntimeError("index out of range"),undefined):aq.$array[aq.$offset+an])));};CE.prototype.In=function(an){return this.$val.In(an);};CE.ptr.prototype.Key=function(){var an,ao;an=this;if(!((an.Kind()===21))){$panic(new $String("reflect: Key of non-map type"));}ao=(an.kindType);return EQ(ao.key);};CE.prototype.Key=function(){return this.$val.Key();};CE.ptr.prototype.Len=function(){var an,ao;an=this;if(!((an.Kind()===17))){$panic(new $String("reflect: Len of non-array type"));}ao=(an.kindType);return((ao.len>>0));};CE.prototype.Len=function(){return this.$val.Len();};CE.ptr.prototype.NumField=function(){var an,ao;an=this;if(!((an.Kind()===25))){$panic(new $String("reflect: NumField of non-struct type"));}ao=(an.kindType);return ao.fields.$length;};CE.prototype.NumField=function(){return this.$val.NumField();};CE.ptr.prototype.NumIn=function(){var an,ao;an=this;if(!((an.Kind()===19))){$panic(new $String("reflect: NumIn of non-func type"));}ao=(an.kindType);return((ao.inCount>>0));};CE.prototype.NumIn=function(){return this.$val.NumIn();};CE.ptr.prototype.NumOut=function(){var an,ao;an=this;if(!((an.Kind()===19))){$panic(new $String("reflect: NumOut of non-func type"));}ao=(an.kindType);return ao.out().$length;};CE.prototype.NumOut=function(){return this.$val.NumOut();};CE.ptr.prototype.Out=function(an){var an,ao,ap,aq;ao=this;if(!((ao.Kind()===19))){$panic(new $String("reflect: Out of non-func type"));}ap=(ao.kindType);return EQ((aq=ap.out(),((an<0||an>=aq.$length)?($throwRuntimeError("index out of range"),undefined):aq.$array[aq.$offset+an])));};CE.prototype.Out=function(an){return this.$val.Out(an);};CH.prototype.String=function(){var an,ao;an=this.$val;ao=an;if(ao===(2)){return"chan<-";}else if(ao===(1)){return"<-chan";}else if(ao===(3)){return"chan";}return"ChanDir"+B.Itoa(((an>>0)));};$ptrType(CH).prototype.String=function(){return new CH(this.$get()).String();};CL.ptr.prototype.Method=function(an){var an,ao,ap,aq,ar,as;ao=new CS.ptr("","",$ifaceNil,new EY.ptr(HR.nil,0,0),0);ap=this;if(an<0||an>=ap.methods.$length){return ao;}ar=(aq=ap.methods,((an<0||an>=aq.$length)?($throwRuntimeError("index out of range"),undefined):aq.$array[aq.$offset+an]));as=$clone(ap.rtype.nameOff(ar.name),Q);ao.Name=$clone(as,Q).name();if(!$clone(as,Q).isExported()){ao.PkgPath=$clone(as,Q).pkgPath();if(ao.PkgPath===""){ao.PkgPath=$clone(ap.pkgPath,Q).name();}}ao.Type=EQ(ap.rtype.typeOff(ar.typ));ao.Index=an;return ao;};CL.prototype.Method=function(an){return this.$val.Method(an);};CL.ptr.prototype.NumMethod=function(){var an;an=this;return an.methods.$length;};CL.prototype.NumMethod=function(){return this.$val.NumMethod();};CL.ptr.prototype.MethodByName=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax;ao=new CS.ptr("","",$ifaceNil,new EY.ptr(HR.nil,0,0),0);ap=false;aq=this;if(aq===IP.nil){return[ao,ap];}ar=IQ.nil;as=aq.methods;at=0;while(true){if(!(at=av.$length)?($throwRuntimeError("index out of range"),undefined):av.$array[av.$offset+au]));if($clone(aq.rtype.nameOff(ar.name),Q).name()===an){aw=$clone(aq.Method(au),CS);ax=true;CS.copy(ao,aw);ap=ax;return[ao,ap];}at++;}return[ao,ap];};CL.prototype.MethodByName=function(an){return this.$val.MethodByName(an);};DH.prototype.Get=function(an){var an,ao,ap,aq;ao=this.$val;ap=new DH(ao).Lookup(an);aq=ap[0];return aq;};$ptrType(DH).prototype.Get=function(an){return new DH(this.$get()).Get(an);};DH.prototype.Lookup=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba;ao="";ap=false;aq=this.$val;while(true){if(!(!(aq===""))){break;}ar=0;while(true){if(!(ar>0;}aq=$substring(aq,ar);if(aq===""){break;}ar=0;while(true){if(!(ar32&&!((aq.charCodeAt(ar)===58))&&!((aq.charCodeAt(ar)===34))&&!((aq.charCodeAt(ar)===127)))){break;}ar=ar+(1)>>0;}if((ar===0)||(ar+1>>0)>=aq.length||!((aq.charCodeAt(ar)===58))||!((aq.charCodeAt((ar+1>>0))===34))){break;}as=($substring(aq,0,ar));aq=$substring(aq,(ar+1>>0));ar=1;while(true){if(!(ar>0;}ar=ar+(1)>>0;}if(ar>=aq.length){break;}at=($substring(aq,0,(ar+1>>0)));aq=$substring(aq,(ar+1>>0));if(an===as){au=B.Unquote(at);av=au[0];aw=au[1];if(!($interfaceIsEqual(aw,$ifaceNil))){break;}ax=av;ay=true;ao=ax;ap=ay;return[ao,ap];}}az="";ba=false;ao=az;ap=ba;return[ao,ap];};$ptrType(DH).prototype.Lookup=function(an){return new DH(this.$get()).Lookup(an);};CQ.ptr.prototype.Field=function(an){var an,ao,ap,aq,ar,as;ao=new DG.ptr("","",$ifaceNil,"",0,IR.nil,false);ap=this;if(an<0||an>=ap.fields.$length){$panic(new $String("reflect: Field index out of bounds"));}ar=(aq=ap.fields,((an<0||an>=aq.$length)?($throwRuntimeError("index out of range"),undefined):aq.$array[aq.$offset+an]));ao.Type=EQ(ar.typ);ao.Name=$clone(ar.name,Q).name();ao.Anonymous=ar.embedded();if(!$clone(ar.name,Q).isExported()){ao.PkgPath=$clone(ap.pkgPath,Q).name();}as=$clone(ar.name,Q).tag();if(!(as==="")){ao.Tag=(as);}ao.Offset=ar.offset();ao.Index=new IR([an]);return ao;};CQ.prototype.Field=function(an){return this.$val.Field(an);};CQ.ptr.prototype.FieldByIndex=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=new DG.ptr("","",$ifaceNil,"",0,IR.nil,false);ap=this;ao.Type=EQ(ap.rtype);aq=an;ar=0;case 1:if(!(ar=aq.$length)?($throwRuntimeError("index out of range"),undefined):aq.$array[aq.$offset+ar]);if(as>0){$s=3;continue;}$s=4;continue;case 3:au=ao.Type;aw=au.Kind();$s=8;case 8:if($c){$c=false;aw=aw.$blk();}if(aw&&aw.$blk!==undefined){break s;}if(!(aw===22)){av=false;$s=7;continue s;}ax=au.Elem();$s=9;case 9:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}ay=ax.Kind();$s=10;case 10:if($c){$c=false;ay=ay.$blk();}if(ay&&ay.$blk!==undefined){break s;}av=ay===25;case 7:if(av){$s=5;continue;}$s=6;continue;case 5:az=au.Elem();$s=11;case 11:if($c){$c=false;az=az.$blk();}if(az&&az.$blk!==undefined){break s;}au=az;case 6:ao.Type=au;case 4:ba=ao.Type.Field(at);$s=12;case 12:if($c){$c=false;ba=ba.$blk();}if(ba&&ba.$blk!==undefined){break s;}DG.copy(ao,ba);ar++;$s=1;continue;case 2:$s=-1;return ao;}return;}if($f===undefined){$f={$blk:CQ.ptr.prototype.FieldByIndex};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.$s=$s;$f.$r=$r;return $f;};CQ.prototype.FieldByIndex=function(an){return this.$val.FieldByIndex(an);};CQ.ptr.prototype.FieldByNameFunc=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;bj=$f.bj;bk=$f.bk;bl=$f.bl;bm=$f.bm;bn=$f.bn;bo=$f.bo;bp=$f.bp;bq=$f.bq;br=$f.br;bs=$f.bs;bt=$f.bt;bu=$f.bu;bv=$f.bv;bw=$f.bw;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=new DG.ptr("","",$ifaceNil,"",0,IR.nil,false);ap=false;aq=this;ar=new IS([]);as=new IS([new DI.ptr(aq,IR.nil)]);at=false;au=$makeMap(IT.keyFor,[]);case 1:if(!(as.$length>0)){$s=2;continue;}av=as;aw=$subslice(ar,0,0);ar=av;as=aw;ax=at;at=false;ay=ar;az=0;case 3:if(!(az=ay.$length)?($throwRuntimeError("index out of range"),undefined):ay.$array[ay.$offset+az]),DI);bb=ba.typ;if((bc=au[IT.keyFor(bb)],bc!==undefined?bc.v:false)){$s=5;continue;}$s=6;continue;case 5:az++;$s=3;continue;case 6:bd=bb;(au||$throwRuntimeError("assignment to entry in nil map"))[IT.keyFor(bd)]={k:bd,v:true};be=bb.fields;bf=0;case 7:if(!(bf=bh.$length)?($throwRuntimeError("index out of range"),undefined):bh.$array[bh.$offset+bg]));bj=$clone(bi.name,Q).name();bk=HR.nil;if(bi.embedded()){$s=9;continue;}$s=10;continue;case 9:bk=bi.typ;if(bk.Kind()===22){$s=11;continue;}$s=12;continue;case 11:bl=bk.Elem().common();$s=13;case 13:if($c){$c=false;bl=bl.$blk();}if(bl&&bl.$blk!==undefined){break s;}bk=bl;case 12:case 10:bm=an(bj);$s=16;case 16:if($c){$c=false;bm=bm.$blk();}if(bm&&bm.$blk!==undefined){break s;}if(bm){$s=14;continue;}$s=15;continue;case 14:if((bn=ax[IT.keyFor(bb)],bn!==undefined?bn.v:0)>1||ap){bo=new DG.ptr("","",$ifaceNil,"",0,IR.nil,false);bp=false;DG.copy(ao,bo);ap=bp;$s=-1;return[ao,ap];}DG.copy(ao,bb.Field(bg));ao.Index=IR.nil;ao.Index=$appendSlice(ao.Index,ba.index);ao.Index=$append(ao.Index,bg);ap=true;bf++;$s=7;continue;case 15:if(ap||bk===HR.nil||!((bk.Kind()===25))){bf++;$s=7;continue;}bq=(bk.kindType);if((br=at[IT.keyFor(bq)],br!==undefined?br.v:0)>0){bs=bq;(at||$throwRuntimeError("assignment to entry in nil map"))[IT.keyFor(bs)]={k:bs,v:2};bf++;$s=7;continue;}if(at===false){at=$makeMap(IT.keyFor,[]);}bt=bq;(at||$throwRuntimeError("assignment to entry in nil map"))[IT.keyFor(bt)]={k:bt,v:1};if((bu=ax[IT.keyFor(bb)],bu!==undefined?bu.v:0)>1){bv=bq;(at||$throwRuntimeError("assignment to entry in nil map"))[IT.keyFor(bv)]={k:bv,v:2};}bw=IR.nil;bw=$appendSlice(bw,ba.index);bw=$append(bw,bg);as=$append(as,new DI.ptr(bq,bw));bf++;$s=7;continue;case 8:az++;$s=3;continue;case 4:if(ap){$s=2;continue;}$s=1;continue;case 2:$s=-1;return[ao,ap];}return;}if($f===undefined){$f={$blk:CQ.ptr.prototype.FieldByNameFunc};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.bj=bj;$f.bk=bk;$f.bl=bl;$f.bm=bm;$f.bn=bn;$f.bo=bo;$f.bp=bp;$f.bq=bq;$f.br=br;$f.bs=bs;$f.bt=bt;$f.bu=bu;$f.bv=bv;$f.bw=bw;$f.$s=$s;$f.$r=$r;return $f;};CQ.prototype.FieldByNameFunc=function(an){return this.$val.FieldByNameFunc(an);};CQ.ptr.prototype.FieldByName=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=[an];ao=new DG.ptr("","",$ifaceNil,"",0,IR.nil,false);ap=false;aq=this;ar=false;if(!(an[0]==="")){as=aq.fields;at=0;while(true){if(!(at=av.$length)?($throwRuntimeError("index out of range"),undefined):av.$array[av.$offset+au]));if($clone(aw.name,Q).name()===an[0]){ax=$clone(aq.Field(au),DG);ay=true;DG.copy(ao,ax);ap=ay;$s=-1;return[ao,ap];}if(aw.embedded()){ar=true;}at++;}}if(!ar){$s=-1;return[ao,ap];}ba=aq.FieldByNameFunc((function(an){return function(ba){var ba;return ba===an[0];};})(an));$s=1;case 1:if($c){$c=false;ba=ba.$blk();}if(ba&&ba.$blk!==undefined){break s;}az=ba;DG.copy(ao,az[0]);ap=az[1];$s=-1;return[ao,ap];}return;}if($f===undefined){$f={$blk:CQ.ptr.prototype.FieldByName};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.$s=$s;$f.$r=$r;return $f;};CQ.prototype.FieldByName=function(an){return this.$val.FieldByName(an);};DK=function(an){var an;return $assertType(an,HR).ptrTo();};$pkg.PtrTo=DK;CE.ptr.prototype.Implements=function(an){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if($interfaceIsEqual(an,$ifaceNil)){$panic(new $String("reflect: nil type passed to Type.Implements"));}ap=an.Kind();$s=3;case 3:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}if(!((ap===20))){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("reflect: non-interface type passed to Type.Implements"));case 2:$s=-1;return DM($assertType(an,HR),ao);}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.Implements};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.Implements=function(an){return this.$val.Implements(an);};CE.ptr.prototype.AssignableTo=function(an){var an,ao,ap,aq,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if($interfaceIsEqual(an,$ifaceNil)){$panic(new $String("reflect: nil type passed to Type.AssignableTo"));}ap=$assertType(an,HR);aq=DN(ap,ao);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return aq||DM(ap,ao);}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.AssignableTo};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.AssignableTo=function(an){return this.$val.AssignableTo(an);};CE.ptr.prototype.ConvertibleTo=function(an){var an,ao,ap,aq,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if($interfaceIsEqual(an,$ifaceNil)){$panic(new $String("reflect: nil type passed to Type.ConvertibleTo"));}ap=$assertType(an,HR);aq=GJ(ap,ao);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return!(aq===$throwNilPointerError);}return;}if($f===undefined){$f={$blk:CE.ptr.prototype.ConvertibleTo};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.$s=$s;$f.$r=$r;return $f;};CE.prototype.ConvertibleTo=function(an){return this.$val.ConvertibleTo(an);};DM=function(an,ao){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl;if(!((an.Kind()===20))){return false;}ap=(an.kindType);if(ap.methods.$length===0){return true;}if(ao.Kind()===20){aq=(ao.kindType);ar=0;as=0;while(true){if(!(as=at.$length)?($throwRuntimeError("index out of range"),undefined):at.$array[at.$offset+ar]));av=$clone(ap.rtype.nameOff(au.name),Q);ax=(aw=aq.methods,((as<0||as>=aw.$length)?($throwRuntimeError("index out of range"),undefined):aw.$array[aw.$offset+as]));ay=$clone(ao.nameOff(ax.name),Q);if($clone(ay,Q).name()===$clone(av,Q).name()&&ao.typeOff(ax.typ)===ap.rtype.typeOff(au.typ)){if(!$clone(av,Q).isExported()){az=$clone(av,Q).pkgPath();if(az===""){az=$clone(ap.pkgPath,Q).name();}ba=$clone(ay,Q).pkgPath();if(ba===""){ba=$clone(aq.pkgPath,Q).name();}if(!(az===ba)){as=as+(1)>>0;continue;}}ar=ar+(1)>>0;if(ar>=ap.methods.$length){return true;}}as=as+(1)>>0;}return false;}bb=ao.uncommon();if(bb===IE.nil){return false;}bc=0;bd=bb.methods();be=0;while(true){if(!(be<((bb.mcount>>0)))){break;}bg=(bf=ap.methods,((bc<0||bc>=bf.$length)?($throwRuntimeError("index out of range"),undefined):bf.$array[bf.$offset+bc]));bh=$clone(ap.rtype.nameOff(bg.name),Q);bi=$clone(((be<0||be>=bd.$length)?($throwRuntimeError("index out of range"),undefined):bd.$array[bd.$offset+be]),CG);bj=$clone(ao.nameOff(bi.name),Q);if($clone(bj,Q).name()===$clone(bh,Q).name()&&ao.typeOff(bi.mtyp)===ap.rtype.typeOff(bg.typ)){if(!$clone(bh,Q).isExported()){bk=$clone(bh,Q).pkgPath();if(bk===""){bk=$clone(ap.pkgPath,Q).name();}bl=$clone(bj,Q).pkgPath();if(bl===""){bl=$clone(ao.nameOff(bb.pkgPath),Q).name();}if(!(bk===bl)){be=be+(1)>>0;continue;}}bc=bc+(1)>>0;if(bc>=ap.methods.$length){return true;}}be=be+(1)>>0;}return false;};DN=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(an===ao){$s=-1;return true;}if(!(an.Name()==="")&&!(ao.Name()==="")||!((an.Kind()===ao.Kind()))){$s=-1;return false;}ap=DP(an,ao,true);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:DN};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};DO=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(ap){$s=-1;return $interfaceIsEqual(an,ao);}ar=an.Name();$s=4;case 4:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}as=ao.Name();$s=5;case 5:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}if(!(ar===as)){aq=true;$s=3;continue s;}at=an.Kind();$s=6;case 6:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}au=ao.Kind();$s=7;case 7:if($c){$c=false;au=au.$blk();}if(au&&au.$blk!==undefined){break s;}aq=!((at===au));case 3:if(aq){$s=1;continue;}$s=2;continue;case 1:$s=-1;return false;case 2:av=an.common();$s=8;case 8:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}aw=av;ax=ao.common();$s=9;case 9:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}ay=ax;az=DP(aw,ay,false);$s=10;case 10:if($c){$c=false;az=az.$blk();}if(az&&az.$blk!==undefined){break s;}$s=-1;return az;}return;}if($f===undefined){$f={$blk:DO};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.$s=$s;$f.$r=$r;return $f;};DP=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;bj=$f.bj;bk=$f.bk;bl=$f.bl;bm=$f.bm;bn=$f.bn;bo=$f.bo;bp=$f.bp;bq=$f.bq;br=$f.br;bs=$f.bs;bt=$f.bt;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(an===ao){$s=-1;return true;}aq=an.Kind();if(!((aq===ao.Kind()))){$s=-1;return false;}if(1<=aq&&aq<=16||(aq===24)||(aq===26)){$s=-1;return true;}ar=aq;if(ar===(17)){$s=2;continue;}if(ar===(18)){$s=3;continue;}if(ar===(19)){$s=4;continue;}if(ar===(20)){$s=5;continue;}if(ar===(21)){$s=6;continue;}if((ar===(22))||(ar===(23))){$s=7;continue;}if(ar===(25)){$s=8;continue;}$s=9;continue;case 2:if(!(an.Len()===ao.Len())){as=false;$s=10;continue s;}at=DO(an.Elem(),ao.Elem(),ap);$s=11;case 11:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}as=at;case 10:$s=-1;return as;case 3:if(!(ao.ChanDir()===3)){au=false;$s=14;continue s;}av=DO(an.Elem(),ao.Elem(),ap);$s=15;case 15:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}au=av;case 14:if(au){$s=12;continue;}$s=13;continue;case 12:$s=-1;return true;case 13:if(!(ao.ChanDir()===an.ChanDir())){aw=false;$s=16;continue s;}ax=DO(an.Elem(),ao.Elem(),ap);$s=17;case 17:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}aw=ax;case 16:$s=-1;return aw;case 4:ay=(an.kindType);az=(ao.kindType);if(!((ay.outCount===az.outCount))||!((ay.inCount===az.inCount))){$s=-1;return false;}ba=0;case 18:if(!(ba>0;$s=18;continue;case 19:bc=0;case 23:if(!(bc>0;$s=23;continue;case 24:$s=-1;return true;case 5:be=(an.kindType);bf=(ao.kindType);if((be.methods.$length===0)&&(bf.methods.$length===0)){$s=-1;return true;}$s=-1;return false;case 6:bh=DO(an.Key(),ao.Key(),ap);$s=29;case 29:if($c){$c=false;bh=bh.$blk();}if(bh&&bh.$blk!==undefined){break s;}if(!(bh)){bg=false;$s=28;continue s;}bi=DO(an.Elem(),ao.Elem(),ap);$s=30;case 30:if($c){$c=false;bi=bi.$blk();}if(bi&&bi.$blk!==undefined){break s;}bg=bi;case 28:$s=-1;return bg;case 7:bj=DO(an.Elem(),ao.Elem(),ap);$s=31;case 31:if($c){$c=false;bj=bj.$blk();}if(bj&&bj.$blk!==undefined){break s;}$s=-1;return bj;case 8:bk=(an.kindType);bl=(ao.kindType);if(!((bk.fields.$length===bl.fields.$length))){$s=-1;return false;}if(!($clone(bk.pkgPath,Q).name()===$clone(bl.pkgPath,Q).name())){$s=-1;return false;}bm=bk.fields;bn=0;case 32:if(!(bn=bp.$length)?($throwRuntimeError("index out of range"),undefined):bp.$array[bp.$offset+bo]));bs=(br=bl.fields,((bo<0||bo>=br.$length)?($throwRuntimeError("index out of range"),undefined):br.$array[br.$offset+bo]));if(!($clone(bq.name,Q).name()===$clone(bs.name,Q).name())){$s=-1;return false;}bt=DO(bq.typ,bs.typ,ap);$s=36;case 36:if($c){$c=false;bt=bt.$blk();}if(bt&&bt.$blk!==undefined){break s;}if(!bt){$s=34;continue;}$s=35;continue;case 34:$s=-1;return false;case 35:if(ap&&!($clone(bq.name,Q).tag()===$clone(bs.name,Q).tag())){$s=-1;return false;}if(!((bq.offsetEmbed===bs.offsetEmbed))){$s=-1;return false;}bn++;$s=32;continue;case 33:$s=-1;return true;case 9:case 1:$s=-1;return false;}return;}if($f===undefined){$f={$blk:DP};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.bj=bj;$f.bk=bk;$f.bl=bl;$f.bm=bm;$f.bn=bn;$f.bo=bo;$f.bp=bp;$f.bq=bq;$f.br=br;$f.bs=bs;$f.bt=bt;$f.$s=$s;$f.$r=$r;return $f;};EQ=function(an){var an;if(an===HR.nil){return $ifaceNil;}return an;};EV=function(an){var an;return((an.kind&32)>>>0)===0;};EZ.prototype.kind=function(){var an;an=this.$val;return((((an&31)>>>0)>>>0));};$ptrType(EZ).prototype.kind=function(){return new EZ(this.$get()).kind();};EZ.prototype.ro=function(){var an;an=this.$val;if(!((((an&96)>>>0)===0))){return 32;}return 0;};$ptrType(EZ).prototype.ro=function(){return new EZ(this.$get()).ro();};EY.ptr.prototype.pointer=function(){var an;an=this;if(!((an.typ.size===4))||!an.typ.pointers()){$panic(new $String("can't call pointer on a non-pointer Value"));}if(!((((an.flag&128)>>>0)===0))){return(an.ptr).$get();}return an.ptr;};EY.prototype.pointer=function(){return this.$val.pointer();};FC.ptr.prototype.Error=function(){var an;an=this;if(an.Kind===0){return"reflect: call of "+an.Method+" on zero Value";}return"reflect: call of "+an.Method+" on "+new CC(an.Kind).String()+" Value";};FC.prototype.Error=function(){return this.$val.Error();};EZ.prototype.mustBe=function(an){var an,ao;ao=this.$val;if(!((new EZ(ao).kind()===an))){$panic(new FC.ptr(BH(),new EZ(ao).kind()));}};$ptrType(EZ).prototype.mustBe=function(an){return new EZ(this.$get()).mustBe(an);};EZ.prototype.mustBeExported=function(){var an;an=this.$val;if(an===0){$panic(new FC.ptr(BH(),0));}if(!((((an&96)>>>0)===0))){$panic(new $String("reflect: "+BH()+" using value obtained using unexported field"));}};$ptrType(EZ).prototype.mustBeExported=function(){return new EZ(this.$get()).mustBeExported();};EZ.prototype.mustBeAssignable=function(){var an;an=this.$val;if(an===0){$panic(new FC.ptr(BH(),0));}if(!((((an&96)>>>0)===0))){$panic(new $String("reflect: "+BH()+" using value obtained using unexported field"));}if(((an&256)>>>0)===0){$panic(new $String("reflect: "+BH()+" using unaddressable value"));}};$ptrType(EZ).prototype.mustBeAssignable=function(){return new EZ(this.$get()).mustBeAssignable();};EY.ptr.prototype.Addr=function(){var an;an=this;if(((an.flag&256)>>>0)===0){$panic(new $String("reflect.Value.Addr of unaddressable value"));}return new EY.ptr(an.typ.ptrTo(),an.ptr,(new EZ(an.flag).ro()|22)>>>0);};EY.prototype.Addr=function(){return this.$val.Addr();};EY.ptr.prototype.Bool=function(){var an;an=this;new EZ(an.flag).mustBe(1);return(an.ptr).$get();};EY.prototype.Bool=function(){return this.$val.Bool();};EY.ptr.prototype.Bytes=function(){var an,ao,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;new EZ(an.flag).mustBe(23);ao=an.typ.Elem().Kind();$s=3;case 3:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}if(!((ao===8))){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("reflect.Value.Bytes of non-byte slice"));case 2:$s=-1;return(an.ptr).$get();}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Bytes};}$f.an=an;$f.ao=ao;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Bytes=function(){return this.$val.Bytes();};EY.ptr.prototype.runes=function(){var an,ao,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;new EZ(an.flag).mustBe(23);ao=an.typ.Elem().Kind();$s=3;case 3:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}if(!((ao===5))){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("reflect.Value.Bytes of non-rune slice"));case 2:$s=-1;return(an.ptr).$get();}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.runes};}$f.an=an;$f.ao=ao;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.runes=function(){return this.$val.runes();};EY.ptr.prototype.CanAddr=function(){var an;an=this;return!((((an.flag&256)>>>0)===0));};EY.prototype.CanAddr=function(){return this.$val.CanAddr();};EY.ptr.prototype.CanSet=function(){var an;an=this;return((an.flag&352)>>>0)===256;};EY.prototype.CanSet=function(){return this.$val.CanSet();};EY.ptr.prototype.Call=function(an){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBe(19);new EZ(ao.flag).mustBeExported();ap=$clone(ao,EY).call("Call",an);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Call};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Call=function(an){return this.$val.Call(an);};EY.ptr.prototype.CallSlice=function(an){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBe(19);new EZ(ao.flag).mustBeExported();ap=$clone(ao,EY).call("CallSlice",an);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.CallSlice};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.CallSlice=function(an){return this.$val.CallSlice(an);};EY.ptr.prototype.Complex=function(){var an,ao,ap,aq;an=this;ao=new EZ(an.flag).kind();ap=ao;if(ap===(15)){return((aq=(an.ptr).$get(),new $Complex128(aq.$real,aq.$imag)));}else if(ap===(16)){return(an.ptr).$get();}$panic(new FC.ptr("reflect.Value.Complex",new EZ(an.flag).kind()));};EY.prototype.Complex=function(){return this.$val.Complex();};EY.ptr.prototype.FieldByIndex=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if(an.$length===1){$s=1;continue;}$s=2;continue;case 1:ap=$clone(ao,EY).Field((0>=an.$length?($throwRuntimeError("index out of range"),undefined):an.$array[an.$offset+0]));$s=3;case 3:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;case 2:new EZ(ao.flag).mustBe(25);aq=an;ar=0;case 4:if(!(ar=aq.$length)?($throwRuntimeError("index out of range"),undefined):aq.$array[aq.$offset+ar]);if(as>0){$s=6;continue;}$s=7;continue;case 6:if(!($clone(ao,EY).Kind()===22)){au=false;$s=10;continue s;}av=ao.typ.Elem().Kind();$s=11;case 11:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}au=av===25;case 10:if(au){$s=8;continue;}$s=9;continue;case 8:if($clone(ao,EY).IsNil()){$panic(new $String("reflect: indirection through nil pointer to embedded struct"));}aw=$clone(ao,EY).Elem();$s=12;case 12:if($c){$c=false;aw=aw.$blk();}if(aw&&aw.$blk!==undefined){break s;}ao=aw;case 9:case 7:ax=$clone(ao,EY).Field(at);$s=13;case 13:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}ao=ax;ar++;$s=4;continue;case 5:$s=-1;return ao;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.FieldByIndex};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.FieldByIndex=function(an){return this.$val.FieldByIndex(an);};EY.ptr.prototype.FieldByName=function(an){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBe(25);aq=ao.typ.FieldByName(an);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ap=aq;ar=$clone(ap[0],DG);as=ap[1];if(as){$s=2;continue;}$s=3;continue;case 2:at=$clone(ao,EY).FieldByIndex(ar.Index);$s=4;case 4:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return at;case 3:$s=-1;return new EY.ptr(HR.nil,0,0);}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.FieldByName};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.FieldByName=function(an){return this.$val.FieldByName(an);};EY.ptr.prototype.FieldByNameFunc=function(an){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;aq=ao.typ.FieldByNameFunc(an);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ap=aq;ar=$clone(ap[0],DG);as=ap[1];if(as){$s=2;continue;}$s=3;continue;case 2:at=$clone(ao,EY).FieldByIndex(ar.Index);$s=4;case 4:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return at;case 3:$s=-1;return new EY.ptr(HR.nil,0,0);}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.FieldByNameFunc};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.FieldByNameFunc=function(an){return this.$val.FieldByNameFunc(an);};EY.ptr.prototype.Float=function(){var an,ao,ap;an=this;ao=new EZ(an.flag).kind();ap=ao;if(ap===(13)){return((an.ptr).$get());}else if(ap===(14)){return(an.ptr).$get();}$panic(new FC.ptr("reflect.Value.Float",new EZ(an.flag).kind()));};EY.prototype.Float=function(){return this.$val.Float();};EY.ptr.prototype.Int=function(){var an,ao,ap,aq;an=this;ao=new EZ(an.flag).kind();ap=an.ptr;aq=ao;if(aq===(2)){return(new $Int64(0,(ap).$get()));}else if(aq===(3)){return(new $Int64(0,(ap).$get()));}else if(aq===(4)){return(new $Int64(0,(ap).$get()));}else if(aq===(5)){return(new $Int64(0,(ap).$get()));}else if(aq===(6)){return(ap).$get();}$panic(new FC.ptr("reflect.Value.Int",new EZ(an.flag).kind()));};EY.prototype.Int=function(){return this.$val.Int();};EY.ptr.prototype.CanInterface=function(){var an;an=this;if(an.flag===0){$panic(new FC.ptr("reflect.Value.CanInterface",0));}return((an.flag&96)>>>0)===0;};EY.prototype.CanInterface=function(){return this.$val.CanInterface();};EY.ptr.prototype.Interface=function(){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=$ifaceNil;ao=this;ap=BF($clone(ao,EY),true);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}an=ap;$s=-1;return an;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Interface};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Interface=function(){return this.$val.Interface();};EY.ptr.prototype.IsValid=function(){var an;an=this;return!((an.flag===0));};EY.prototype.IsValid=function(){return this.$val.IsValid();};EY.ptr.prototype.Kind=function(){var an;an=this;return new EZ(an.flag).kind();};EY.prototype.Kind=function(){return this.$val.Kind();};EY.ptr.prototype.MapIndex=function(an){var an,ao,ap,aq,ar,as,at,au,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBe(21);ap=(ao.typ.kindType);aq=$clone(an,EY).assignTo("reflect.Value.MapIndex",ap.key,0);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}an=aq;ar=0;if(!((((an.flag&128)>>>0)===0))){ar=an.ptr;}else{ar=((an.$ptr_ptr||(an.$ptr_ptr=new JC(function(){return this.$target.ptr;},function($v){this.$target.ptr=$v;},an))));}as=AT(ao.typ,$clone(ao,EY).pointer(),ar);if(as===0){$s=-1;return new EY.ptr(HR.nil,0,0);}at=ap.elem;au=new EZ((((ao.flag|an.flag)>>>0))).ro();au=(au|(((at.Kind()>>>0))))>>>0;$s=-1;return FN(at,au,as);}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.MapIndex};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.MapIndex=function(an){return this.$val.MapIndex(an);};EY.ptr.prototype.MapKeys=function(){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;new EZ(an.flag).mustBe(21);ao=(an.typ.kindType);ap=ao.key;aq=(new EZ(an.flag).ro()|((ap.Kind()>>>0)))>>>0;ar=$clone(an,EY).pointer();as=0;if(!(ar===0)){as=BB(ar);}at=AX(an.typ,ar);au=$makeSlice(II,as);av=0;av=0;case 1:if(!(av=au.$length)?($throwRuntimeError("index out of range"),undefined):au.$array[au.$offset+av]=FN(ap,aq,ax));BA(at);av=av+(1)>>0;$s=1;continue;case 2:$s=-1;return $subslice(au,0,av);}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.MapKeys};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.MapKeys=function(){return this.$val.MapKeys();};FM.ptr.prototype.Key=function(){var an,ao,ap,aq,ar,as,at,au,av,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;if(an.it===0){$panic(new $String("MapIter.Key called before Next"));}ao=AY(an.it);$s=3;case 3:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}if(ao===0){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("MapIter.Key called on exhausted iterator"));case 2:ap=(an.m.typ.kindType);aq=ap.key;ar=aq;as=(new EZ(an.m.flag).ro()|((aq.Kind()>>>0)))>>>0;at=AY(an.it);$s=4;case 4:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}au=at;av=FN(ar,as,au);$s=5;case 5:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}$s=-1;return av;}return;}if($f===undefined){$f={$blk:FM.ptr.prototype.Key};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.$s=$s;$f.$r=$r;return $f;};FM.prototype.Key=function(){return this.$val.Key();};FM.ptr.prototype.Value=function(){var an,ao,ap,aq,ar,as,at,au,av,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;if(an.it===0){$panic(new $String("MapIter.Value called before Next"));}ao=AY(an.it);$s=3;case 3:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}if(ao===0){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("MapIter.Value called on exhausted iterator"));case 2:ap=(an.m.typ.kindType);aq=ap.elem;ar=aq;as=(new EZ(an.m.flag).ro()|((aq.Kind()>>>0)))>>>0;at=AZ(an.it);$s=4;case 4:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}au=at;av=FN(ar,as,au);$s=5;case 5:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}$s=-1;return av;}return;}if($f===undefined){$f={$blk:FM.ptr.prototype.Value};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.$s=$s;$f.$r=$r;return $f;};FM.prototype.Value=function(){return this.$val.Value();};FM.ptr.prototype.Next=function(){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;if(an.it===0){$s=1;continue;}$s=2;continue;case 1:an.it=AX(an.m.typ,$clone(an.m,EY).pointer());$s=3;continue;case 2:ao=AY(an.it);$s=6;case 6:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}if(ao===0){$s=4;continue;}$s=5;continue;case 4:$panic(new $String("MapIter.Next called on exhausted iterator"));case 5:BA(an.it);case 3:ap=AY(an.it);$s=7;case 7:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return!(ap===0);}return;}if($f===undefined){$f={$blk:FM.ptr.prototype.Next};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};FM.prototype.Next=function(){return this.$val.Next();};EY.ptr.prototype.MapRange=function(){var an;an=this;new EZ(an.flag).mustBe(21);return new FM.ptr($clone(an,EY),0);};EY.prototype.MapRange=function(){return this.$val.MapRange();};FN=function(an,ao,ap){var an,ao,ap,aq;if(EV(an)){aq=AL(an);AO(an,aq,ap);return new EY.ptr(an,aq,(ao|128)>>>0);}return new EY.ptr(an,(ap).$get(),ao);};EY.ptr.prototype.Method=function(an){var an,ao,ap;ao=this;if(ao.typ===HR.nil){$panic(new FC.ptr("reflect.Value.Method",0));}if(!((((ao.flag&512)>>>0)===0))||((an>>>0))>=((ao.typ.NumMethod()>>>0))){$panic(new $String("reflect: Method index out of range"));}if((ao.typ.Kind()===20)&&$clone(ao,EY).IsNil()){$panic(new $String("reflect: Method on nil interface value"));}ap=(ao.flag&160)>>>0;ap=(ap|(19))>>>0;ap=(ap|((((((an>>>0))<<10>>>0)|512)>>>0)))>>>0;return new EY.ptr(ao.typ,ao.ptr,ap);};EY.prototype.Method=function(an){return this.$val.Method(an);};EY.ptr.prototype.NumMethod=function(){var an;an=this;if(an.typ===HR.nil){$panic(new FC.ptr("reflect.Value.NumMethod",0));}if(!((((an.flag&512)>>>0)===0))){return 0;}return an.typ.NumMethod();};EY.prototype.NumMethod=function(){return this.$val.NumMethod();};EY.ptr.prototype.MethodByName=function(an){var an,ao,ap,aq,ar,as,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if(ao.typ===HR.nil){$panic(new FC.ptr("reflect.Value.MethodByName",0));}if(!((((ao.flag&512)>>>0)===0))){$s=-1;return new EY.ptr(HR.nil,0,0);}aq=ao.typ.MethodByName(an);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ap=aq;ar=$clone(ap[0],CS);as=ap[1];if(!as){$s=-1;return new EY.ptr(HR.nil,0,0);}$s=-1;return $clone(ao,EY).Method(ar.Index);}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.MethodByName};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.MethodByName=function(an){return this.$val.MethodByName(an);};EY.ptr.prototype.NumField=function(){var an,ao;an=this;new EZ(an.flag).mustBe(25);ao=(an.typ.kindType);return ao.fields.$length;};EY.prototype.NumField=function(){return this.$val.NumField();};EY.ptr.prototype.OverflowComplex=function(an){var an,ao,ap,aq;ao=this;ap=new EZ(ao.flag).kind();aq=ap;if(aq===(15)){return FO(an.$real)||FO(an.$imag);}else if(aq===(16)){return false;}$panic(new FC.ptr("reflect.Value.OverflowComplex",new EZ(ao.flag).kind()));};EY.prototype.OverflowComplex=function(an){return this.$val.OverflowComplex(an);};EY.ptr.prototype.OverflowFloat=function(an){var an,ao,ap,aq;ao=this;ap=new EZ(ao.flag).kind();aq=ap;if(aq===(13)){return FO(an);}else if(aq===(14)){return false;}$panic(new FC.ptr("reflect.Value.OverflowFloat",new EZ(ao.flag).kind()));};EY.prototype.OverflowFloat=function(an){return this.$val.OverflowFloat(an);};FO=function(an){var an;if(an<0){an=-an;}return 3.4028234663852886e+38>>0;as=$shiftRightInt64(($shiftLeft64(an,((64-ar>>>0)))),((64-ar>>>0)));return!((an.$high===as.$high&&an.$low===as.$low));}$panic(new FC.ptr("reflect.Value.OverflowInt",new EZ(ao.flag).kind()));};EY.prototype.OverflowInt=function(an){return this.$val.OverflowInt(an);};EY.ptr.prototype.OverflowUint=function(an){var an,ao,ap,aq,ar,as;ao=this;ap=new EZ(ao.flag).kind();aq=ap;if((aq===(7))||(aq===(12))||(aq===(8))||(aq===(9))||(aq===(10))||(aq===(11))){ar=$imul(ao.typ.size,8)>>>0;as=$shiftRightUint64(($shiftLeft64(an,((64-ar>>>0)))),((64-ar>>>0)));return!((an.$high===as.$high&&an.$low===as.$low));}$panic(new FC.ptr("reflect.Value.OverflowUint",new EZ(ao.flag).kind()));};EY.prototype.OverflowUint=function(an){return this.$val.OverflowUint(an);};EY.ptr.prototype.Recv=function(){var an,ao,ap,aq,ar,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=new EY.ptr(HR.nil,0,0);ao=false;ap=this;new EZ(ap.flag).mustBe(18);new EZ(ap.flag).mustBeExported();ar=$clone(ap,EY).recv(false);$s=1;case 1:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}aq=ar;an=aq[0];ao=aq[1];$s=-1;return[an,ao];}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Recv};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Recv=function(){return this.$val.Recv();};EY.ptr.prototype.recv=function(an){var an,ao,ap,aq,ar,as,at,au,av,aw,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=new EY.ptr(HR.nil,0,0);ap=false;aq=this;ar=(aq.typ.kindType);if((((ar.dir>>0))&1)===0){$panic(new $String("reflect: recv on send-only channel"));}as=ar.elem;ao=new EY.ptr(as,0,((as.Kind()>>>0)));at=0;if(EV(as)){at=AL(as);ao.ptr=at;ao.flag=(ao.flag|(128))>>>0;}else{at=((ao.$ptr_ptr||(ao.$ptr_ptr=new JC(function(){return this.$target.ptr;},function($v){this.$target.ptr=$v;},ao))));}av=BP($clone(aq,EY).pointer(),an,at);$s=1;case 1:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}au=av;aw=au[0];ap=au[1];if(!aw){ao=new EY.ptr(HR.nil,0,0);}$s=-1;return[ao,ap];}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.recv};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.recv=function(an){return this.$val.recv(an);};EY.ptr.prototype.Send=function(an){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBe(18);new EZ(ao.flag).mustBeExported();ap=$clone(ao,EY).send($clone(an,EY),false);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}ap;$s=-1;return;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Send};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Send=function(an){return this.$val.Send(an);};EY.ptr.prototype.send=function(an,ao){var an,ao,ap,aq,ar,as,at,au,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=false;aq=this;ar=(aq.typ.kindType);if((((ar.dir>>0))&2)===0){$panic(new $String("reflect: send on recv-only channel"));}new EZ(an.flag).mustBeExported();as=$clone(an,EY).assignTo("reflect.Value.Send",ar.elem,0);$s=1;case 1:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}an=as;at=0;if(!((((an.flag&128)>>>0)===0))){at=an.ptr;}else{at=((an.$ptr_ptr||(an.$ptr_ptr=new JC(function(){return this.$target.ptr;},function($v){this.$target.ptr=$v;},an))));}au=BQ($clone(aq,EY).pointer(),at,ao);$s=2;case 2:if($c){$c=false;au=au.$blk();}if(au&&au.$blk!==undefined){break s;}ap=au;$s=-1;return ap;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.send};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.send=function(an,ao){return this.$val.send(an,ao);};EY.ptr.prototype.SetBool=function(an){var an,ao;ao=this;new EZ(ao.flag).mustBeAssignable();new EZ(ao.flag).mustBe(1);(ao.ptr).$set(an);};EY.prototype.SetBool=function(an){return this.$val.SetBool(an);};EY.ptr.prototype.setRunes=function(an){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBeAssignable();new EZ(ao.flag).mustBe(23);ap=ao.typ.Elem().Kind();$s=3;case 3:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}if(!((ap===5))){$s=1;continue;}$s=2;continue;case 1:$panic(new $String("reflect.Value.setRunes of non-rune slice"));case 2:(ao.ptr).$set(an);$s=-1;return;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.setRunes};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.setRunes=function(an){return this.$val.setRunes(an);};EY.ptr.prototype.SetComplex=function(an){var an,ao,ap,aq;ao=this;new EZ(ao.flag).mustBeAssignable();ap=new EZ(ao.flag).kind();aq=ap;if(aq===(15)){(ao.ptr).$set((new $Complex64(an.$real,an.$imag)));}else if(aq===(16)){(ao.ptr).$set(an);}else{$panic(new FC.ptr("reflect.Value.SetComplex",new EZ(ao.flag).kind()));}};EY.prototype.SetComplex=function(an){return this.$val.SetComplex(an);};EY.ptr.prototype.SetFloat=function(an){var an,ao,ap,aq;ao=this;new EZ(ao.flag).mustBeAssignable();ap=new EZ(ao.flag).kind();aq=ap;if(aq===(13)){(ao.ptr).$set(($fround(an)));}else if(aq===(14)){(ao.ptr).$set(an);}else{$panic(new FC.ptr("reflect.Value.SetFloat",new EZ(ao.flag).kind()));}};EY.prototype.SetFloat=function(an){return this.$val.SetFloat(an);};EY.ptr.prototype.SetInt=function(an){var an,ao,ap,aq;ao=this;new EZ(ao.flag).mustBeAssignable();ap=new EZ(ao.flag).kind();aq=ap;if(aq===(2)){(ao.ptr).$set((((an.$low+((an.$high>>31)*4294967296))>>0)));}else if(aq===(3)){(ao.ptr).$set((((an.$low+((an.$high>>31)*4294967296))<<24>>24)));}else if(aq===(4)){(ao.ptr).$set((((an.$low+((an.$high>>31)*4294967296))<<16>>16)));}else if(aq===(5)){(ao.ptr).$set((((an.$low+((an.$high>>31)*4294967296))>>0)));}else if(aq===(6)){(ao.ptr).$set(an);}else{$panic(new FC.ptr("reflect.Value.SetInt",new EZ(ao.flag).kind()));}};EY.prototype.SetInt=function(an){return this.$val.SetInt(an);};EY.ptr.prototype.SetMapIndex=function(an,ao){var an,ao,ap,aq,ar,as,at,au,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=this;new EZ(ap.flag).mustBe(21);new EZ(ap.flag).mustBeExported();new EZ(an.flag).mustBeExported();aq=(ap.typ.kindType);ar=$clone(an,EY).assignTo("reflect.Value.SetMapIndex",aq.key,0);$s=1;case 1:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}an=ar;as=0;if(!((((an.flag&128)>>>0)===0))){as=an.ptr;}else{as=((an.$ptr_ptr||(an.$ptr_ptr=new JC(function(){return this.$target.ptr;},function($v){this.$target.ptr=$v;},an))));}if(ao.typ===HR.nil){AV(ap.typ,$clone(ap,EY).pointer(),as);$s=-1;return;}new EZ(ao.flag).mustBeExported();at=$clone(ao,EY).assignTo("reflect.Value.SetMapIndex",aq.elem,0);$s=2;case 2:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}ao=at;au=0;if(!((((ao.flag&128)>>>0)===0))){au=ao.ptr;}else{au=((ao.$ptr_ptr||(ao.$ptr_ptr=new JC(function(){return this.$target.ptr;},function($v){this.$target.ptr=$v;},ao))));}$r=AU(ap.typ,$clone(ap,EY).pointer(),as,au);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.SetMapIndex};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.SetMapIndex=function(an,ao){return this.$val.SetMapIndex(an,ao);};EY.ptr.prototype.SetUint=function(an){var an,ao,ap,aq;ao=this;new EZ(ao.flag).mustBeAssignable();ap=new EZ(ao.flag).kind();aq=ap;if(aq===(7)){(ao.ptr).$set(((an.$low>>>0)));}else if(aq===(8)){(ao.ptr).$set(((an.$low<<24>>>24)));}else if(aq===(9)){(ao.ptr).$set(((an.$low<<16>>>16)));}else if(aq===(10)){(ao.ptr).$set(((an.$low>>>0)));}else if(aq===(11)){(ao.ptr).$set(an);}else if(aq===(12)){(ao.ptr).$set(((an.$low>>>0)));}else{$panic(new FC.ptr("reflect.Value.SetUint",new EZ(ao.flag).kind()));}};EY.prototype.SetUint=function(an){return this.$val.SetUint(an);};EY.ptr.prototype.SetPointer=function(an){var an,ao;ao=this;new EZ(ao.flag).mustBeAssignable();new EZ(ao.flag).mustBe(26);(ao.ptr).$set(an);};EY.prototype.SetPointer=function(an){return this.$val.SetPointer(an);};EY.ptr.prototype.SetString=function(an){var an,ao;ao=this;new EZ(ao.flag).mustBeAssignable();new EZ(ao.flag).mustBe(24);(ao.ptr).$set(an);};EY.prototype.SetString=function(an){return this.$val.SetString(an);};EY.ptr.prototype.String=function(){var an,ao,ap,aq,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=this;ao=new EZ(an.flag).kind();ap=ao;if(ap===(0)){$s=-1;return"";}else if(ap===(24)){$s=-1;return(an.ptr).$get();}aq=$clone(an,EY).Type().String();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return"<"+aq+" Value>";}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.String};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.String=function(){return this.$val.String();};EY.ptr.prototype.TryRecv=function(){var an,ao,ap,aq,ar,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:an=new EY.ptr(HR.nil,0,0);ao=false;ap=this;new EZ(ap.flag).mustBe(18);new EZ(ap.flag).mustBeExported();ar=$clone(ap,EY).recv(true);$s=1;case 1:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}aq=ar;an=aq[0];ao=aq[1];$s=-1;return[an,ao];}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.TryRecv};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.TryRecv=function(){return this.$val.TryRecv();};EY.ptr.prototype.TrySend=function(an){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;new EZ(ao.flag).mustBe(18);new EZ(ao.flag).mustBeExported();ap=$clone(ao,EY).send($clone(an,EY),true);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.TrySend};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.TrySend=function(an){return this.$val.TrySend(an);};EY.ptr.prototype.Type=function(){var an,ao,ap,aq,ar,as,at,au;an=this;ao=an.flag;if(ao===0){$panic(new FC.ptr("reflect.Value.Type",0));}if(((ao&512)>>>0)===0){return an.typ;}ap=((an.flag>>0))>>10>>0;if(an.typ.Kind()===20){aq=(an.typ.kindType);if(((ap>>>0))>=((aq.methods.$length>>>0))){$panic(new $String("reflect: internal error: invalid method index"));}as=(ar=aq.methods,((ap<0||ap>=ar.$length)?($throwRuntimeError("index out of range"),undefined):ar.$array[ar.$offset+ap]));return an.typ.typeOff(as.typ);}at=an.typ.exportedMethods();if(((ap>>>0))>=((at.$length>>>0))){$panic(new $String("reflect: internal error: invalid method index"));}au=$clone(((ap<0||ap>=at.$length)?($throwRuntimeError("index out of range"),undefined):at.$array[at.$offset+ap]),CG);return an.typ.typeOff(au.mtyp);};EY.prototype.Type=function(){return this.$val.Type();};EY.ptr.prototype.Uint=function(){var an,ao,ap,aq,ar;an=this;ao=new EZ(an.flag).kind();ap=an.ptr;aq=ao;if(aq===(7)){return(new $Uint64(0,(ap).$get()));}else if(aq===(8)){return(new $Uint64(0,(ap).$get()));}else if(aq===(9)){return(new $Uint64(0,(ap).$get()));}else if(aq===(10)){return(new $Uint64(0,(ap).$get()));}else if(aq===(11)){return(ap).$get();}else if(aq===(12)){return((ar=(ap).$get(),new $Uint64(0,ar.constructor===Number?ar:1)));}$panic(new FC.ptr("reflect.Value.Uint",new EZ(an.flag).kind()));};EY.prototype.Uint=function(){return this.$val.Uint();};EY.ptr.prototype.UnsafeAddr=function(){var an;an=this;if(an.typ===HR.nil){$panic(new FC.ptr("reflect.Value.UnsafeAddr",0));}if(((an.flag&256)>>>0)===0){$panic(new $String("reflect.Value.UnsafeAddr of unaddressable value"));}return(an.ptr);};EY.prototype.UnsafeAddr=function(){return this.$val.UnsafeAddr();};GG=function(an){var an,ao,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(!(($clone(an,EY).Kind()===22))){$s=-1;return an;}ao=$clone(an,EY).Elem();$s=1;case 1:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}$s=-1;return ao;}return;}if($f===undefined){$f={$blk:GG};}$f.an=an;$f.ao=ao;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Indirect=GG;GH=function(an){var an,ao,ap,aq;if($interfaceIsEqual(an,$ifaceNil)){$panic(new $String("reflect: New(nil)"));}ao=$assertType(an,HR);ap=AL(ao);aq=22;return new EY.ptr(ao.ptrTo(),ap,aq);};$pkg.New=GH;EY.ptr.prototype.Convert=function(an){var an,ao,ap,aq,ar,as,at,au,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ao=this;if(!((((ao.flag&512)>>>0)===0))){$s=1;continue;}$s=2;continue;case 1:ap=BI("Convert",$clone(ao,EY));$s=3;case 3:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}ao=ap;case 2:aq=an.common();$s=4;case 4:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=GJ(aq,ao.typ);$s=5;case 5:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}as=ar;if(as===$throwNilPointerError){$s=6;continue;}$s=7;continue;case 6:at=an.String();$s=8;case 8:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$panic(new $String("reflect.Value.Convert: value of type "+ao.typ.String()+" cannot be converted to type "+at));case 7:au=as($clone(ao,EY),an);$s=9;case 9:if($c){$c=false;au=au.$blk();}if(au&&au.$blk!==undefined){break s;}$s=-1;return au;}return;}if($f===undefined){$f={$blk:EY.ptr.prototype.Convert};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.$s=$s;$f.$r=$r;return $f;};EY.prototype.Convert=function(an){return this.$val.Convert(an);};GJ=function(an,ao){var an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=ao.Kind();if((ap===(2))||(ap===(3))||(ap===(4))||(ap===(5))||(ap===(6))){$s=2;continue;}if((ap===(7))||(ap===(8))||(ap===(9))||(ap===(10))||(ap===(11))||(ap===(12))){$s=3;continue;}if((ap===(13))||(ap===(14))){$s=4;continue;}if((ap===(15))||(ap===(16))){$s=5;continue;}if(ap===(24)){$s=6;continue;}if(ap===(23)){$s=7;continue;}$s=8;continue;case 2:aq=an.Kind();if((aq===(2))||(aq===(3))||(aq===(4))||(aq===(5))||(aq===(6))||(aq===(7))||(aq===(8))||(aq===(9))||(aq===(10))||(aq===(11))||(aq===(12))){$s=-1;return GP;}else if((aq===(13))||(aq===(14))){$s=-1;return GT;}else if(aq===(24)){$s=-1;return GX;}$s=8;continue;case 3:ar=an.Kind();if((ar===(2))||(ar===(3))||(ar===(4))||(ar===(5))||(ar===(6))||(ar===(7))||(ar===(8))||(ar===(9))||(ar===(10))||(ar===(11))||(ar===(12))){$s=-1;return GQ;}else if((ar===(13))||(ar===(14))){$s=-1;return GU;}else if(ar===(24)){$s=-1;return GY;}$s=8;continue;case 4:as=an.Kind();if((as===(2))||(as===(3))||(as===(4))||(as===(5))||(as===(6))){$s=-1;return GR;}else if((as===(7))||(as===(8))||(as===(9))||(as===(10))||(as===(11))||(as===(12))){$s=-1;return GS;}else if((as===(13))||(as===(14))){$s=-1;return GV;}$s=8;continue;case 5:at=an.Kind();if((at===(15))||(at===(16))){$s=-1;return GW;}$s=8;continue;case 6:if(!(an.Kind()===23)){au=false;$s=11;continue s;}av=an.Elem().PkgPath();$s=12;case 12:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}au=av==="";case 11:if(au){$s=9;continue;}$s=10;continue;case 9:aw=an.Elem().Kind();$s=14;case 14:if($c){$c=false;aw=aw.$blk();}if(aw&&aw.$blk!==undefined){break s;}ax=aw;if(ax===(8)){$s=-1;return HA;}else if(ax===(5)){$s=-1;return HC;}case 13:case 10:$s=8;continue;case 7:if(!(an.Kind()===24)){ay=false;$s=17;continue s;}az=ao.Elem().PkgPath();$s=18;case 18:if($c){$c=false;az=az.$blk();}if(az&&az.$blk!==undefined){break s;}ay=az==="";case 17:if(ay){$s=15;continue;}$s=16;continue;case 15:ba=ao.Elem().Kind();$s=20;case 20:if($c){$c=false;ba=ba.$blk();}if(ba&&ba.$blk!==undefined){break s;}bb=ba;if(bb===(8)){$s=-1;return GZ;}else if(bb===(5)){$s=-1;return HB;}case 19:case 16:case 8:case 1:bc=DP(an,ao,false);$s=23;case 23:if($c){$c=false;bc=bc.$blk();}if(bc&&bc.$blk!==undefined){break s;}if(bc){$s=21;continue;}$s=22;continue;case 21:$s=-1;return BC;case 22:if(!((an.Kind()===22)&&an.Name()===""&&(ao.Kind()===22)&&ao.Name()==="")){bd=false;$s=26;continue s;}be=an.Elem().common();$s=27;case 27:if($c){$c=false;be=be.$blk();}if(be&&be.$blk!==undefined){break s;}bf=be;bg=ao.Elem().common();$s=28;case 28:if($c){$c=false;bg=bg.$blk();}if(bg&&bg.$blk!==undefined){break s;}bh=bg;bi=DP(bf,bh,false);$s=29;case 29:if($c){$c=false;bi=bi.$blk();}if(bi&&bi.$blk!==undefined){break s;}bd=bi;case 26:if(bd){$s=24;continue;}$s=25;continue;case 24:$s=-1;return BC;case 25:if(DM(an,ao)){if(ao.Kind()===20){$s=-1;return HE;}$s=-1;return HD;}$s=-1;return $throwNilPointerError;}return;}if($f===undefined){$f={$blk:GJ};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.$s=$s;$f.$r=$r;return $f;};GK=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=ap.common();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;as=AL(ar);at=ar.size;if(at===(4)){(as).$set(($fround(ao)));}else if(at===(8)){(as).$set(ao);}$s=-1;return new EY.ptr(ar,as,(((an|128)>>>0)|((ar.Kind()>>>0)))>>>0);}return;}if($f===undefined){$f={$blk:GK};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};GL=function(an,ao,ap){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=ap.common();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;as=AL(ar);at=ar.size;if(at===(8)){(as).$set((new $Complex64(ao.$real,ao.$imag)));}else if(at===(16)){(as).$set(ao);}$s=-1;return new EY.ptr(ar,as,(((an|128)>>>0)|((ar.Kind()>>>0)))>>>0);}return;}if($f===undefined){$f={$blk:GL};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};GM=function(an,ao,ap){var an,ao,ap,aq,ar,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=$clone(GH(ap),EY).Elem();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;$clone(ar,EY).SetString(ao);ar.flag=(((ar.flag&~256)>>>0)|an)>>>0;$s=-1;return ar;}return;}if($f===undefined){$f={$blk:GM};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.$s=$s;$f.$r=$r;return $f;};GN=function(an,ao,ap){var an,ao,ap,aq,ar,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=$clone(GH(ap),EY).Elem();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;$r=$clone(ar,EY).SetBytes(ao);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}ar.flag=(((ar.flag&~256)>>>0)|an)>>>0;$s=-1;return ar;}return;}if($f===undefined){$f={$blk:GN};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.$s=$s;$f.$r=$r;return $f;};GO=function(an,ao,ap){var an,ao,ap,aq,ar,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=$clone(GH(ap),EY).Elem();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;$r=$clone(ar,EY).setRunes(ao);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}ar.flag=(((ar.flag&~256)>>>0)|an)>>>0;$s=-1;return ar;}return;}if($f===undefined){$f={$blk:GO};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.$s=$s;$f.$r=$r;return $f;};GP=function(an,ao){var an,ao,ap,aq,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=AM(new EZ(an.flag).ro(),((ap=$clone(an,EY).Int(),new $Uint64(ap.$high,ap.$low))),ao);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return aq;}return;}if($f===undefined){$f={$blk:GP};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.$s=$s;$f.$r=$r;return $f;};GQ=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=AM(new EZ(an.flag).ro(),$clone(an,EY).Uint(),ao);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:GQ};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};GR=function(an,ao){var an,ao,ap,aq,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:aq=AM(new EZ(an.flag).ro(),((ap=(new $Int64(0,$clone(an,EY).Float())),new $Uint64(ap.$high,ap.$low))),ao);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}$s=-1;return aq;}return;}if($f===undefined){$f={$blk:GR};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.$s=$s;$f.$r=$r;return $f;};GS=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=AM(new EZ(an.flag).ro(),(new $Uint64(0,$clone(an,EY).Float())),ao);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:GS};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};GT=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=GK(new EZ(an.flag).ro(),($flatten64($clone(an,EY).Int())),ao);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:GT};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};GU=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=GK(new EZ(an.flag).ro(),($flatten64($clone(an,EY).Uint())),ao);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:GU};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};GV=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=GK(new EZ(an.flag).ro(),$clone(an,EY).Float(),ao);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:GV};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};GW=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=GL(new EZ(an.flag).ro(),$clone(an,EY).Complex(),ao);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:GW};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};GX=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=GM(new EZ(an.flag).ro(),($encodeRune($clone(an,EY).Int().$low)),ao);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:GX};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};GY=function(an,ao){var an,ao,ap,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=GM(new EZ(an.flag).ro(),($encodeRune($clone(an,EY).Uint().$low)),ao);$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}$s=-1;return ap;}return;}if($f===undefined){$f={$blk:GY};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.$s=$s;$f.$r=$r;return $f;};GZ=function(an,ao){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=new EZ(an.flag).ro();aq=$clone(an,EY).Bytes();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=($bytesToString(aq));as=ao;at=GM(ap,ar,as);$s=2;case 2:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return at;}return;}if($f===undefined){$f={$blk:GZ};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};HA=function(an,ao){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=new EZ(an.flag).ro();aq=$clone(an,EY).String();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=(new IU($stringToBytes(aq)));as=ao;at=GN(ap,ar,as);$s=2;case 2:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return at;}return;}if($f===undefined){$f={$blk:HA};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};HB=function(an,ao){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=new EZ(an.flag).ro();aq=$clone(an,EY).runes();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=($runesToString(aq));as=ao;at=GM(ap,ar,as);$s=2;case 2:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return at;}return;}if($f===undefined){$f={$blk:HB};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};HC=function(an,ao){var an,ao,ap,aq,ar,as,at,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=new EZ(an.flag).ro();aq=$clone(an,EY).String();$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=(new JE($stringToRunes(aq)));as=ao;at=GO(ap,ar,as);$s=2;case 2:if($c){$c=false;at=at.$blk();}if(at&&at.$blk!==undefined){break s;}$s=-1;return at;}return;}if($f===undefined){$f={$blk:HC};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.$s=$s;$f.$r=$r;return $f;};HD=function(an,ao){var an,ao,ap,aq,ar,as,at,au,av,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:ap=ao.common();$s=1;case 1:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}aq=AL(ap);$s=2;case 2:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;as=BF($clone(an,EY),false);$s=3;case 3:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}at=as;au=ao.NumMethod();$s=7;case 7:if($c){$c=false;au=au.$blk();}if(au&&au.$blk!==undefined){break s;}if(au===0){$s=4;continue;}$s=5;continue;case 4:(ar).$set(at);$s=6;continue;case 5:BG($assertType(ao,HR),at,ar);case 6:av=ao.common();$s=8;case 8:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}$s=-1;return new EY.ptr(av,ar,(((new EZ(an.flag).ro()|128)>>>0)|20)>>>0);}return;}if($f===undefined){$f={$blk:HD};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.$s=$s;$f.$r=$r;return $f;};HE=function(an,ao){var an,ao,ap,aq,ar,as,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if($clone(an,EY).IsNil()){$s=1;continue;}$s=2;continue;case 1:ap=AK(ao);$s=3;case 3:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}aq=ap;aq.flag=(aq.flag|(new EZ(an.flag).ro()))>>>0;$s=-1;return aq;case 2:ar=$clone(an,EY).Elem();$s=4;case 4:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}as=HD($clone(ar,EY),ao);$s=5;case 5:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}$s=-1;return as;}return;}if($f===undefined){$f={$blk:HE};}$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.$s=$s;$f.$r=$r;return $f;};IE.methods=[{prop:"methods",name:"methods",pkg:"reflect",typ:$funcType([],[IB],false)},{prop:"exportedMethods",name:"exportedMethods",pkg:"reflect",typ:$funcType([],[IB],false)}];IK.methods=[{prop:"in$",name:"in",pkg:"reflect",typ:$funcType([],[HS],false)},{prop:"out",name:"out",pkg:"reflect",typ:$funcType([],[HS],false)}];Q.methods=[{prop:"name",name:"name",pkg:"reflect",typ:$funcType([],[$String],false)},{prop:"tag",name:"tag",pkg:"reflect",typ:$funcType([],[$String],false)},{prop:"pkgPath",name:"pkgPath",pkg:"reflect",typ:$funcType([],[$String],false)},{prop:"isExported",name:"isExported",pkg:"reflect",typ:$funcType([],[$Bool],false)},{prop:"data",name:"data",pkg:"reflect",typ:$funcType([$Int,$String],[IA],false)},{prop:"nameLen",name:"nameLen",pkg:"reflect",typ:$funcType([],[$Int],false)},{prop:"tagLen",name:"tagLen",pkg:"reflect",typ:$funcType([],[$Int],false)}];IJ.methods=[{prop:"skipUntilValidKey",name:"skipUntilValidKey",pkg:"reflect",typ:$funcType([],[],false)}];CC.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];HR.methods=[{prop:"uncommon",name:"uncommon",pkg:"reflect",typ:$funcType([],[IE],false)},{prop:"nameOff",name:"nameOff",pkg:"reflect",typ:$funcType([DB],[Q],false)},{prop:"typeOff",name:"typeOff",pkg:"reflect",typ:$funcType([DC],[HR],false)},{prop:"ptrTo",name:"ptrTo",pkg:"reflect",typ:$funcType([],[HR],false)},{prop:"pointers",name:"pointers",pkg:"reflect",typ:$funcType([],[$Bool],false)},{prop:"Comparable",name:"Comparable",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Method",name:"Method",pkg:"",typ:$funcType([$Int],[CS],false)},{prop:"textOff",name:"textOff",pkg:"reflect",typ:$funcType([DD],[$UnsafePointer],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"Size",name:"Size",pkg:"",typ:$funcType([],[$Uintptr],false)},{prop:"Bits",name:"Bits",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Align",name:"Align",pkg:"",typ:$funcType([],[$Int],false)},{prop:"FieldAlign",name:"FieldAlign",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Kind",name:"Kind",pkg:"",typ:$funcType([],[CC],false)},{prop:"common",name:"common",pkg:"reflect",typ:$funcType([],[HR],false)},{prop:"exportedMethods",name:"exportedMethods",pkg:"reflect",typ:$funcType([],[IB],false)},{prop:"NumMethod",name:"NumMethod",pkg:"",typ:$funcType([],[$Int],false)},{prop:"MethodByName",name:"MethodByName",pkg:"",typ:$funcType([$String],[CS,$Bool],false)},{prop:"PkgPath",name:"PkgPath",pkg:"",typ:$funcType([],[$String],false)},{prop:"Name",name:"Name",pkg:"",typ:$funcType([],[$String],false)},{prop:"ChanDir",name:"ChanDir",pkg:"",typ:$funcType([],[CH],false)},{prop:"IsVariadic",name:"IsVariadic",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Elem",name:"Elem",pkg:"",typ:$funcType([],[CB],false)},{prop:"Field",name:"Field",pkg:"",typ:$funcType([$Int],[DG],false)},{prop:"FieldByIndex",name:"FieldByIndex",pkg:"",typ:$funcType([IR],[DG],false)},{prop:"FieldByName",name:"FieldByName",pkg:"",typ:$funcType([$String],[DG,$Bool],false)},{prop:"FieldByNameFunc",name:"FieldByNameFunc",pkg:"",typ:$funcType([JG],[DG,$Bool],false)},{prop:"In",name:"In",pkg:"",typ:$funcType([$Int],[CB],false)},{prop:"Key",name:"Key",pkg:"",typ:$funcType([],[CB],false)},{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"NumField",name:"NumField",pkg:"",typ:$funcType([],[$Int],false)},{prop:"NumIn",name:"NumIn",pkg:"",typ:$funcType([],[$Int],false)},{prop:"NumOut",name:"NumOut",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Out",name:"Out",pkg:"",typ:$funcType([$Int],[CB],false)},{prop:"Implements",name:"Implements",pkg:"",typ:$funcType([CB],[$Bool],false)},{prop:"AssignableTo",name:"AssignableTo",pkg:"",typ:$funcType([CB],[$Bool],false)},{prop:"ConvertibleTo",name:"ConvertibleTo",pkg:"",typ:$funcType([CB],[$Bool],false)}];CH.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];IP.methods=[{prop:"Method",name:"Method",pkg:"",typ:$funcType([$Int],[CS],false)},{prop:"NumMethod",name:"NumMethod",pkg:"",typ:$funcType([],[$Int],false)},{prop:"MethodByName",name:"MethodByName",pkg:"",typ:$funcType([$String],[CS,$Bool],false)}];JJ.methods=[{prop:"offset",name:"offset",pkg:"reflect",typ:$funcType([],[$Uintptr],false)},{prop:"embedded",name:"embedded",pkg:"reflect",typ:$funcType([],[$Bool],false)}];IT.methods=[{prop:"Field",name:"Field",pkg:"",typ:$funcType([$Int],[DG],false)},{prop:"FieldByIndex",name:"FieldByIndex",pkg:"",typ:$funcType([IR],[DG],false)},{prop:"FieldByNameFunc",name:"FieldByNameFunc",pkg:"",typ:$funcType([JG],[DG,$Bool],false)},{prop:"FieldByName",name:"FieldByName",pkg:"",typ:$funcType([$String],[DG,$Bool],false)}];DH.methods=[{prop:"Get",name:"Get",pkg:"",typ:$funcType([$String],[$String],false)},{prop:"Lookup",name:"Lookup",pkg:"",typ:$funcType([$String],[$String,$Bool],false)}];EY.methods=[{prop:"object",name:"object",pkg:"reflect",typ:$funcType([],[HW],false)},{prop:"assignTo",name:"assignTo",pkg:"reflect",typ:$funcType([$String,HR,$UnsafePointer],[EY],false)},{prop:"call",name:"call",pkg:"reflect",typ:$funcType([$String,II],[II],false)},{prop:"Cap",name:"Cap",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Elem",name:"Elem",pkg:"",typ:$funcType([],[EY],false)},{prop:"Field",name:"Field",pkg:"",typ:$funcType([$Int],[EY],false)},{prop:"Index",name:"Index",pkg:"",typ:$funcType([$Int],[EY],false)},{prop:"InterfaceData",name:"InterfaceData",pkg:"",typ:$funcType([],[JQ],false)},{prop:"IsNil",name:"IsNil",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Pointer",name:"Pointer",pkg:"",typ:$funcType([],[$Uintptr],false)},{prop:"Set",name:"Set",pkg:"",typ:$funcType([EY],[],false)},{prop:"SetBytes",name:"SetBytes",pkg:"",typ:$funcType([IU],[],false)},{prop:"SetCap",name:"SetCap",pkg:"",typ:$funcType([$Int],[],false)},{prop:"SetLen",name:"SetLen",pkg:"",typ:$funcType([$Int],[],false)},{prop:"Slice",name:"Slice",pkg:"",typ:$funcType([$Int,$Int],[EY],false)},{prop:"Slice3",name:"Slice3",pkg:"",typ:$funcType([$Int,$Int,$Int],[EY],false)},{prop:"Close",name:"Close",pkg:"",typ:$funcType([],[],false)},{prop:"pointer",name:"pointer",pkg:"reflect",typ:$funcType([],[$UnsafePointer],false)},{prop:"Addr",name:"Addr",pkg:"",typ:$funcType([],[EY],false)},{prop:"Bool",name:"Bool",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Bytes",name:"Bytes",pkg:"",typ:$funcType([],[IU],false)},{prop:"runes",name:"runes",pkg:"reflect",typ:$funcType([],[JE],false)},{prop:"CanAddr",name:"CanAddr",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"CanSet",name:"CanSet",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Call",name:"Call",pkg:"",typ:$funcType([II],[II],false)},{prop:"CallSlice",name:"CallSlice",pkg:"",typ:$funcType([II],[II],false)},{prop:"Complex",name:"Complex",pkg:"",typ:$funcType([],[$Complex128],false)},{prop:"FieldByIndex",name:"FieldByIndex",pkg:"",typ:$funcType([IR],[EY],false)},{prop:"FieldByName",name:"FieldByName",pkg:"",typ:$funcType([$String],[EY],false)},{prop:"FieldByNameFunc",name:"FieldByNameFunc",pkg:"",typ:$funcType([JG],[EY],false)},{prop:"Float",name:"Float",pkg:"",typ:$funcType([],[$Float64],false)},{prop:"Int",name:"Int",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"CanInterface",name:"CanInterface",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Interface",name:"Interface",pkg:"",typ:$funcType([],[$emptyInterface],false)},{prop:"IsValid",name:"IsValid",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Kind",name:"Kind",pkg:"",typ:$funcType([],[CC],false)},{prop:"MapIndex",name:"MapIndex",pkg:"",typ:$funcType([EY],[EY],false)},{prop:"MapKeys",name:"MapKeys",pkg:"",typ:$funcType([],[II],false)},{prop:"MapRange",name:"MapRange",pkg:"",typ:$funcType([],[JR],false)},{prop:"Method",name:"Method",pkg:"",typ:$funcType([$Int],[EY],false)},{prop:"NumMethod",name:"NumMethod",pkg:"",typ:$funcType([],[$Int],false)},{prop:"MethodByName",name:"MethodByName",pkg:"",typ:$funcType([$String],[EY],false)},{prop:"NumField",name:"NumField",pkg:"",typ:$funcType([],[$Int],false)},{prop:"OverflowComplex",name:"OverflowComplex",pkg:"",typ:$funcType([$Complex128],[$Bool],false)},{prop:"OverflowFloat",name:"OverflowFloat",pkg:"",typ:$funcType([$Float64],[$Bool],false)},{prop:"OverflowInt",name:"OverflowInt",pkg:"",typ:$funcType([$Int64],[$Bool],false)},{prop:"OverflowUint",name:"OverflowUint",pkg:"",typ:$funcType([$Uint64],[$Bool],false)},{prop:"Recv",name:"Recv",pkg:"",typ:$funcType([],[EY,$Bool],false)},{prop:"recv",name:"recv",pkg:"reflect",typ:$funcType([$Bool],[EY,$Bool],false)},{prop:"Send",name:"Send",pkg:"",typ:$funcType([EY],[],false)},{prop:"send",name:"send",pkg:"reflect",typ:$funcType([EY,$Bool],[$Bool],false)},{prop:"SetBool",name:"SetBool",pkg:"",typ:$funcType([$Bool],[],false)},{prop:"setRunes",name:"setRunes",pkg:"reflect",typ:$funcType([JE],[],false)},{prop:"SetComplex",name:"SetComplex",pkg:"",typ:$funcType([$Complex128],[],false)},{prop:"SetFloat",name:"SetFloat",pkg:"",typ:$funcType([$Float64],[],false)},{prop:"SetInt",name:"SetInt",pkg:"",typ:$funcType([$Int64],[],false)},{prop:"SetMapIndex",name:"SetMapIndex",pkg:"",typ:$funcType([EY,EY],[],false)},{prop:"SetUint",name:"SetUint",pkg:"",typ:$funcType([$Uint64],[],false)},{prop:"SetPointer",name:"SetPointer",pkg:"",typ:$funcType([$UnsafePointer],[],false)},{prop:"SetString",name:"SetString",pkg:"",typ:$funcType([$String],[],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"TryRecv",name:"TryRecv",pkg:"",typ:$funcType([],[EY,$Bool],false)},{prop:"TrySend",name:"TrySend",pkg:"",typ:$funcType([EY],[$Bool],false)},{prop:"Type",name:"Type",pkg:"",typ:$funcType([],[CB],false)},{prop:"Uint",name:"Uint",pkg:"",typ:$funcType([],[$Uint64],false)},{prop:"UnsafeAddr",name:"UnsafeAddr",pkg:"",typ:$funcType([],[$Uintptr],false)},{prop:"Convert",name:"Convert",pkg:"",typ:$funcType([CB],[EY],false)}];EZ.methods=[{prop:"kind",name:"kind",pkg:"reflect",typ:$funcType([],[CC],false)},{prop:"ro",name:"ro",pkg:"reflect",typ:$funcType([],[EZ],false)},{prop:"mustBe",name:"mustBe",pkg:"reflect",typ:$funcType([CC],[],false)},{prop:"mustBeExported",name:"mustBeExported",pkg:"reflect",typ:$funcType([],[],false)},{prop:"mustBeAssignable",name:"mustBeAssignable",pkg:"reflect",typ:$funcType([],[],false)}];JS.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];JR.methods=[{prop:"Key",name:"Key",pkg:"",typ:$funcType([],[EY],false)},{prop:"Value",name:"Value",pkg:"",typ:$funcType([],[EY],false)},{prop:"Next",name:"Next",pkg:"",typ:$funcType([],[$Bool],false)}];N.init("reflect",[{prop:"pkgPath",name:"pkgPath",embedded:false,exported:false,typ:DB,tag:""},{prop:"mcount",name:"mcount",embedded:false,exported:false,typ:$Uint16,tag:""},{prop:"xcount",name:"xcount",embedded:false,exported:false,typ:$Uint16,tag:""},{prop:"moff",name:"moff",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"_methods",name:"_methods",embedded:false,exported:false,typ:IB,tag:""}]);P.init("reflect",[{prop:"rtype",name:"rtype",embedded:true,exported:false,typ:CE,tag:"reflect:\"func\""},{prop:"inCount",name:"inCount",embedded:false,exported:false,typ:$Uint16,tag:""},{prop:"outCount",name:"outCount",embedded:false,exported:false,typ:$Uint16,tag:""},{prop:"_in",name:"_in",embedded:false,exported:false,typ:HS,tag:""},{prop:"_out",name:"_out",embedded:false,exported:false,typ:HS,tag:""}]);Q.init("reflect",[{prop:"bytes",name:"bytes",embedded:false,exported:false,typ:IA,tag:""}]);R.init("reflect",[{prop:"name",name:"name",embedded:false,exported:false,typ:$String,tag:""},{prop:"tag",name:"tag",embedded:false,exported:false,typ:$String,tag:""},{prop:"exported",name:"exported",embedded:false,exported:false,typ:$Bool,tag:""}]);AW.init("reflect",[{prop:"t",name:"t",embedded:false,exported:false,typ:CB,tag:""},{prop:"m",name:"m",embedded:false,exported:false,typ:HW,tag:""},{prop:"keys",name:"keys",embedded:false,exported:false,typ:HW,tag:""},{prop:"i",name:"i",embedded:false,exported:false,typ:$Int,tag:""},{prop:"last",name:"last",embedded:false,exported:false,typ:HW,tag:""}]);CB.init([{prop:"Align",name:"Align",pkg:"",typ:$funcType([],[$Int],false)},{prop:"AssignableTo",name:"AssignableTo",pkg:"",typ:$funcType([CB],[$Bool],false)},{prop:"Bits",name:"Bits",pkg:"",typ:$funcType([],[$Int],false)},{prop:"ChanDir",name:"ChanDir",pkg:"",typ:$funcType([],[CH],false)},{prop:"Comparable",name:"Comparable",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"ConvertibleTo",name:"ConvertibleTo",pkg:"",typ:$funcType([CB],[$Bool],false)},{prop:"Elem",name:"Elem",pkg:"",typ:$funcType([],[CB],false)},{prop:"Field",name:"Field",pkg:"",typ:$funcType([$Int],[DG],false)},{prop:"FieldAlign",name:"FieldAlign",pkg:"",typ:$funcType([],[$Int],false)},{prop:"FieldByIndex",name:"FieldByIndex",pkg:"",typ:$funcType([IR],[DG],false)},{prop:"FieldByName",name:"FieldByName",pkg:"",typ:$funcType([$String],[DG,$Bool],false)},{prop:"FieldByNameFunc",name:"FieldByNameFunc",pkg:"",typ:$funcType([JG],[DG,$Bool],false)},{prop:"Implements",name:"Implements",pkg:"",typ:$funcType([CB],[$Bool],false)},{prop:"In",name:"In",pkg:"",typ:$funcType([$Int],[CB],false)},{prop:"IsVariadic",name:"IsVariadic",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Key",name:"Key",pkg:"",typ:$funcType([],[CB],false)},{prop:"Kind",name:"Kind",pkg:"",typ:$funcType([],[CC],false)},{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Method",name:"Method",pkg:"",typ:$funcType([$Int],[CS],false)},{prop:"MethodByName",name:"MethodByName",pkg:"",typ:$funcType([$String],[CS,$Bool],false)},{prop:"Name",name:"Name",pkg:"",typ:$funcType([],[$String],false)},{prop:"NumField",name:"NumField",pkg:"",typ:$funcType([],[$Int],false)},{prop:"NumIn",name:"NumIn",pkg:"",typ:$funcType([],[$Int],false)},{prop:"NumMethod",name:"NumMethod",pkg:"",typ:$funcType([],[$Int],false)},{prop:"NumOut",name:"NumOut",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Out",name:"Out",pkg:"",typ:$funcType([$Int],[CB],false)},{prop:"PkgPath",name:"PkgPath",pkg:"",typ:$funcType([],[$String],false)},{prop:"Size",name:"Size",pkg:"",typ:$funcType([],[$Uintptr],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"common",name:"common",pkg:"reflect",typ:$funcType([],[HR],false)},{prop:"uncommon",name:"uncommon",pkg:"reflect",typ:$funcType([],[IE],false)}]);CE.init("reflect",[{prop:"size",name:"size",embedded:false,exported:false,typ:$Uintptr,tag:""},{prop:"ptrdata",name:"ptrdata",embedded:false,exported:false,typ:$Uintptr,tag:""},{prop:"hash",name:"hash",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"tflag",name:"tflag",embedded:false,exported:false,typ:CD,tag:""},{prop:"align",name:"align",embedded:false,exported:false,typ:$Uint8,tag:""},{prop:"fieldAlign",name:"fieldAlign",embedded:false,exported:false,typ:$Uint8,tag:""},{prop:"kind",name:"kind",embedded:false,exported:false,typ:$Uint8,tag:""},{prop:"alg",name:"alg",embedded:false,exported:false,typ:HZ,tag:""},{prop:"gcdata",name:"gcdata",embedded:false,exported:false,typ:IA,tag:""},{prop:"str",name:"str",embedded:false,exported:false,typ:DB,tag:""},{prop:"ptrToThis",name:"ptrToThis",embedded:false,exported:false,typ:DC,tag:""}]);CF.init("reflect",[{prop:"hash",name:"hash",embedded:false,exported:false,typ:JH,tag:""},{prop:"equal",name:"equal",embedded:false,exported:false,typ:JI,tag:""}]);CG.init("reflect",[{prop:"name",name:"name",embedded:false,exported:false,typ:DB,tag:""},{prop:"mtyp",name:"mtyp",embedded:false,exported:false,typ:DC,tag:""},{prop:"ifn",name:"ifn",embedded:false,exported:false,typ:DD,tag:""},{prop:"tfn",name:"tfn",embedded:false,exported:false,typ:DD,tag:""}]);CI.init("reflect",[{prop:"rtype",name:"rtype",embedded:true,exported:false,typ:CE,tag:""},{prop:"elem",name:"elem",embedded:false,exported:false,typ:HR,tag:""},{prop:"slice",name:"slice",embedded:false,exported:false,typ:HR,tag:""},{prop:"len",name:"len",embedded:false,exported:false,typ:$Uintptr,tag:""}]);CJ.init("reflect",[{prop:"rtype",name:"rtype",embedded:true,exported:false,typ:CE,tag:""},{prop:"elem",name:"elem",embedded:false,exported:false,typ:HR,tag:""},{prop:"dir",name:"dir",embedded:false,exported:false,typ:$Uintptr,tag:""}]);CK.init("reflect",[{prop:"name",name:"name",embedded:false,exported:false,typ:DB,tag:""},{prop:"typ",name:"typ",embedded:false,exported:false,typ:DC,tag:""}]);CL.init("reflect",[{prop:"rtype",name:"rtype",embedded:true,exported:false,typ:CE,tag:""},{prop:"pkgPath",name:"pkgPath",embedded:false,exported:false,typ:Q,tag:""},{prop:"methods",name:"methods",embedded:false,exported:false,typ:IC,tag:""}]);CM.init("reflect",[{prop:"rtype",name:"rtype",embedded:true,exported:false,typ:CE,tag:""},{prop:"key",name:"key",embedded:false,exported:false,typ:HR,tag:""},{prop:"elem",name:"elem",embedded:false,exported:false,typ:HR,tag:""},{prop:"bucket",name:"bucket",embedded:false,exported:false,typ:HR,tag:""},{prop:"keysize",name:"keysize",embedded:false,exported:false,typ:$Uint8,tag:""},{prop:"valuesize",name:"valuesize",embedded:false,exported:false,typ:$Uint8,tag:""},{prop:"bucketsize",name:"bucketsize",embedded:false,exported:false,typ:$Uint16,tag:""},{prop:"flags",name:"flags",embedded:false,exported:false,typ:$Uint32,tag:""}]);CN.init("reflect",[{prop:"rtype",name:"rtype",embedded:true,exported:false,typ:CE,tag:""},{prop:"elem",name:"elem",embedded:false,exported:false,typ:HR,tag:""}]);CO.init("reflect",[{prop:"rtype",name:"rtype",embedded:true,exported:false,typ:CE,tag:""},{prop:"elem",name:"elem",embedded:false,exported:false,typ:HR,tag:""}]);CP.init("reflect",[{prop:"name",name:"name",embedded:false,exported:false,typ:Q,tag:""},{prop:"typ",name:"typ",embedded:false,exported:false,typ:HR,tag:""},{prop:"offsetEmbed",name:"offsetEmbed",embedded:false,exported:false,typ:$Uintptr,tag:""}]);CQ.init("reflect",[{prop:"rtype",name:"rtype",embedded:true,exported:false,typ:CE,tag:""},{prop:"pkgPath",name:"pkgPath",embedded:false,exported:false,typ:Q,tag:""},{prop:"fields",name:"fields",embedded:false,exported:false,typ:ID,tag:""}]);CS.init("",[{prop:"Name",name:"Name",embedded:false,exported:true,typ:$String,tag:""},{prop:"PkgPath",name:"PkgPath",embedded:false,exported:true,typ:$String,tag:""},{prop:"Type",name:"Type",embedded:false,exported:true,typ:CB,tag:""},{prop:"Func",name:"Func",embedded:false,exported:true,typ:EY,tag:""},{prop:"Index",name:"Index",embedded:false,exported:true,typ:$Int,tag:""}]);DG.init("",[{prop:"Name",name:"Name",embedded:false,exported:true,typ:$String,tag:""},{prop:"PkgPath",name:"PkgPath",embedded:false,exported:true,typ:$String,tag:""},{prop:"Type",name:"Type",embedded:false,exported:true,typ:CB,tag:""},{prop:"Tag",name:"Tag",embedded:false,exported:true,typ:DH,tag:""},{prop:"Offset",name:"Offset",embedded:false,exported:true,typ:$Uintptr,tag:""},{prop:"Index",name:"Index",embedded:false,exported:true,typ:IR,tag:""},{prop:"Anonymous",name:"Anonymous",embedded:false,exported:true,typ:$Bool,tag:""}]);DI.init("reflect",[{prop:"typ",name:"typ",embedded:false,exported:false,typ:IT,tag:""},{prop:"index",name:"index",embedded:false,exported:false,typ:IR,tag:""}]);EY.init("reflect",[{prop:"typ",name:"typ",embedded:false,exported:false,typ:HR,tag:""},{prop:"ptr",name:"ptr",embedded:false,exported:false,typ:$UnsafePointer,tag:""},{prop:"flag",name:"flag",embedded:true,exported:false,typ:EZ,tag:""}]);FC.init("",[{prop:"Method",name:"Method",embedded:false,exported:true,typ:$String,tag:""},{prop:"Kind",name:"Kind",embedded:false,exported:true,typ:CC,tag:""}]);FM.init("reflect",[{prop:"m",name:"m",embedded:false,exported:false,typ:EY,tag:""},{prop:"it",name:"it",embedded:false,exported:false,typ:$UnsafePointer,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=H.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=G.$init();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}U=HQ.nil;W=HS.nil;I=false;O={};S={};BJ=$assertType($internalize($call,$emptyInterface),HX);BO=$assertType($internalize($select,$emptyInterface),HX);BK=L($jsObjectPtr);CT=new HY(["invalid","bool","int","int8","int16","int32","int64","uint","uint8","uint16","uint32","uint64","uintptr","float32","float64","complex64","complex128","array","chan","func","interface","map","ptr","slice","string","struct","unsafe.Pointer"]);FL=$assertType(AD(new $Uint8(0)),HR);$r=J();$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["sort"]=(function(){var $pkg={},$init,A,I,T,Z,AW,B,E,J,K,L,M,N,O,P,Q,R,U,AC,AG,AH,AI,AJ;A=$packages["reflect"];I=$pkg.Interface=$newType(8,$kindInterface,"sort.Interface",true,"sort",true,null);T=$pkg.reverse=$newType(0,$kindStruct,"sort.reverse",true,"sort",false,function(Interface_){this.$val=this;if(arguments.length===0){this.Interface=$ifaceNil;return;}this.Interface=Interface_;});Z=$pkg.StringSlice=$newType(12,$kindSlice,"sort.StringSlice",true,"sort",true,null);AW=$sliceType($String);B=function(a,b){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=0;d=a;e=c;f=d;case 1:if(!(e>0)>>>0))>>>1>>>0)>>0));h=b(g);$s=6;case 6:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}if(!h){$s=3;continue;}$s=4;continue;case 3:e=g+1>>0;$s=5;continue;case 4:f=g;case 5:$s=1;continue;case 2:$s=-1;return e;}return;}if($f===undefined){$f={$blk:B};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Search=B;E=function(a,b){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=[b];c=B(a[0].$length,(function(a,b){return function(c){var c;return((c<0||c>=a[0].$length)?($throwRuntimeError("index out of range"),undefined):a[0].$array[a[0].$offset+c])>=b[0];};})(a,b));$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return c;}return;}if($f===undefined){$f={$blk:E};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};$pkg.SearchStrings=E;Z.prototype.Search=function(a){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=E($subslice(new AW(b.$array),b.$offset,b.$offset+b.$length),a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return c;}return;}if($f===undefined){$f={$blk:Z.prototype.Search};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(Z).prototype.Search=function(a){return this.$get().Search(a);};J=function(a,b,c){var a,b,c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=b+1>>0;case 1:if(!(db)){f=false;$s=5;continue s;}g=a.Less(e,e-1>>0);$s=6;case 6:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}f=g;case 5:if(!(f)){$s=4;continue;}$r=a.Swap(e,e-1>>0);$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}e=e-(1)>>0;$s=3;continue;case 4:d=d+(1)>>0;$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:J};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};K=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=b;case 1:f=($imul(2,e))+1>>0;if(f>=c){$s=2;continue;}if(!((f+1>>0)>0,(d+f>>0)+1>>0);$s=6;case 6:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;case 5:if(g){$s=3;continue;}$s=4;continue;case 3:f=f+(1)>>0;case 4:i=a.Less(d+e>>0,d+f>>0);$s=9;case 9:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}if(!i){$s=7;continue;}$s=8;continue;case 7:$s=-1;return;case 8:$r=a.Swap(d+e>>0,d+f>>0);$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}e=f;$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:K};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};L=function(a,b,c){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=b;e=0;f=c-b>>0;h=(g=((f-1>>0))/2,(g===g&&g!==1/0&&g!==-1/0)?g>>0:$throwRuntimeError("integer divide by zero"));case 1:if(!(h>=0)){$s=2;continue;}$r=K(a,h,f,d);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}h=h-(1)>>0;$s=1;continue;case 2:i=f-1>>0;case 4:if(!(i>=0)){$s=5;continue;}$r=a.Swap(d,d+i>>0);$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=K(a,e,i,d);$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}i=i-(1)>>0;$s=4;continue;case 5:$s=-1;return;}return;}if($f===undefined){$f={$blk:L};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};M=function(a,b,c,d){var a,b,c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=a.Less(b,c);$s=3;case 3:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}if(e){$s=1;continue;}$s=2;continue;case 1:$r=a.Swap(b,c);$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 2:f=a.Less(d,b);$s=7;case 7:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}if(f){$s=5;continue;}$s=6;continue;case 5:$r=a.Swap(d,b);$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}g=a.Less(b,c);$s=11;case 11:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}if(g){$s=9;continue;}$s=10;continue;case 9:$r=a.Swap(b,c);$s=12;case 12:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 10:case 6:$s=-1;return;}return;}if($f===undefined){$f={$blk:M};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};N=function(a,b,c,d){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=0;case 1:if(!(e>0,c+e>>0);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}e=e+(1)>>0;$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:N};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};O=function(a,b,c){var a,aa,ab,ac,ad,ae,af,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=0;e=0;f=((((((b+c>>0)>>>0))>>>1>>>0)>>0));if((c-b>>0)>40){$s=1;continue;}$s=2;continue;case 1:h=(g=((c-b>>0))/8,(g===g&&g!==1/0&&g!==-1/0)?g>>0:$throwRuntimeError("integer divide by zero"));$r=M(a,b,b+h>>0,b+($imul(2,h))>>0);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=M(a,f,f-h>>0,f+h>>0);$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=M(a,c-1>>0,(c-1>>0)-h>>0,(c-1>>0)-($imul(2,h))>>0);$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 2:$r=M(a,b,f,c-1>>0);$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}i=b;j=b+1>>0;k=c-1>>0;l=j;m=k;case 7:if(!(l>0;$s=7;continue;case 8:p=l;case 11:case 13:if(!(p>0;$s=13;continue;case 14:case 17:if(!(p>0);$s=20;case 20:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}s=t;case 19:if(!(s)){$s=18;continue;}m=m-(1)>>0;$s=17;continue;case 18:if(p>=m){$s=12;continue;}$r=a.Swap(p,m-1>>0);$s=21;case 21:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}p=p+(1)>>0;m=m-(1)>>0;$s=11;continue;case 12:u=(c-m>>0)<5;if(!u&&(c-m>>0)<(v=((c-b>>0))/4,(v===v&&v!==1/0&&v!==-1/0)?v>>0:$throwRuntimeError("integer divide by zero"))){$s=22;continue;}$s=23;continue;case 22:w=0;x=a.Less(i,c-1>>0);$s=26;case 26:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}if(!x){$s=24;continue;}$s=25;continue;case 24:$r=a.Swap(m,c-1>>0);$s=27;case 27:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}m=m+(1)>>0;w=w+(1)>>0;case 25:y=a.Less(p-1>>0,i);$s=30;case 30:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}if(!y){$s=28;continue;}$s=29;continue;case 28:p=p-(1)>>0;w=w+(1)>>0;case 29:z=a.Less(f,i);$s=33;case 33:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}if(!z){$s=31;continue;}$s=32;continue;case 31:$r=a.Swap(f,p-1>>0);$s=34;case 34:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}p=p-(1)>>0;w=w+(1)>>0;case 32:u=w>1;case 23:if(u){$s=35;continue;}$s=36;continue;case 35:case 37:case 39:if(!(l>0,i);$s=42;case 42:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}aa=!ab;case 41:if(!(aa)){$s=40;continue;}p=p-(1)>>0;$s=39;continue;case 40:case 43:if(!(l>0;$s=43;continue;case 44:if(l>=p){$s=38;continue;}$r=a.Swap(l,p-1>>0);$s=47;case 47:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}l=l+(1)>>0;p=p-(1)>>0;$s=37;continue;case 38:case 36:$r=a.Swap(i,p-1>>0);$s=48;case 48:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}ae=p-1>>0;af=m;d=ae;e=af;$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:O};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};P=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:case 1:if(!((c-b>>0)>12)){$s=2;continue;}if(d===0){$s=3;continue;}$s=4;continue;case 3:$r=L(a,b,c);$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 4:d=d-(1)>>0;f=O(a,b,c);$s=6;case 6:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;g=e[0];h=e[1];if((g-b>>0)<(c-h>>0)){$s=7;continue;}$s=8;continue;case 7:$r=P(a,b,g,d);$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}b=h;$s=9;continue;case 8:$r=P(a,h,c,d);$s=11;case 11:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}c=g;case 9:$s=1;continue;case 2:if((c-b>>0)>1){$s=12;continue;}$s=13;continue;case 12:i=b+6>>0;case 14:if(!(i>0);$s=18;case 18:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}if(j){$s=16;continue;}$s=17;continue;case 16:$r=a.Swap(i,i-6>>0);$s=19;case 19:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 17:i=i+(1)>>0;$s=14;continue;case 15:$r=J(a,b,c);$s=20;case 20:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 13:$s=-1;return;}return;}if($f===undefined){$f={$blk:P};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};Q=function(a){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=a.Len();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;$r=P(a,0,c,R(c));$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:Q};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Sort=Q;R=function(a){var a,b,c;b=0;c=a;while(true){if(!(c>0)){break;}b=b+(1)>>0;c=(c>>$min((1),31))>>0;}return $imul(b,2);};T.ptr.prototype.Less=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.Interface.Less(b,a);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:T.ptr.prototype.Less};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};T.prototype.Less=function(a,b){return this.$val.Less(a,b);};U=function(a){var a;return new T.ptr(a);};$pkg.Reverse=U;Z.prototype.Len=function(){var a;a=this;return a.$length;};$ptrType(Z).prototype.Len=function(){return this.$get().Len();};Z.prototype.Less=function(a,b){var a,b,c;c=this;return((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a])<((b<0||b>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]);};$ptrType(Z).prototype.Less=function(a,b){return this.$get().Less(a,b);};Z.prototype.Swap=function(a,b){var a,b,c,d,e;c=this;d=((b<0||b>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]);e=((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]);((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]=d);((b<0||b>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]=e);};$ptrType(Z).prototype.Swap=function(a,b){return this.$get().Swap(a,b);};Z.prototype.Sort=function(){var a,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;$r=Q(a);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:Z.prototype.Sort};}$f.a=a;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(Z).prototype.Sort=function(){return this.$get().Sort();};AC=function(a){var a,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=Q(($subslice(new Z(a.$array),a.$offset,a.$offset+a.$length)));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AC};}$f.a=a;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Strings=AC;AG=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=a;c=a.Len();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;$r=AH(b,d);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AG};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Stable=AG;AH=function(a,b){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=20;d=0;e=c;f=d;g=e;case 1:if(!(g<=b)){$s=2;continue;}$r=J(a,f,g);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}f=g;g=g+(c)>>0;$s=1;continue;case 2:$r=J(a,f,b);$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 5:if(!(c>0,g);$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}f=g;g=g+(($imul(2,c)))>>0;$s=7;continue;case 8:j=f+c>>0;if(j>0)===1){$s=1;continue;}$s=2;continue;case 1:e=c;f=d;case 3:if(!(e>0)>>>0))>>>1>>>0)>>0));h=a.Less(g,b);$s=8;case 8:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}if(h){$s=5;continue;}$s=6;continue;case 5:e=g+1>>0;$s=7;continue;case 6:f=g;case 7:$s=3;continue;case 4:i=b;case 9:if(!(i<(e-1>>0))){$s=10;continue;}$r=a.Swap(i,i+1>>0);$s=11;case 11:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}i=i+(1)>>0;$s=9;continue;case 10:$s=-1;return;case 2:if((d-c>>0)===1){$s=12;continue;}$s=13;continue;case 12:j=b;k=c;case 14:if(!(j>0)>>>0))>>>1>>>0)>>0));m=a.Less(c,l);$s=19;case 19:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}if(!m){$s=16;continue;}$s=17;continue;case 16:j=l+1>>0;$s=18;continue;case 17:k=l;case 18:$s=14;continue;case 15:n=c;case 20:if(!(n>j)){$s=21;continue;}$r=a.Swap(n,n-1>>0);$s=22;case 22:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}n=n-(1)>>0;$s=20;continue;case 21:$s=-1;return;case 13:o=((((((b+d>>0)>>>0))>>>1>>>0)>>0));p=o+c>>0;q=0;r=0;s=q;t=r;if(c>o){s=p-d>>0;t=o;}else{s=b;t=c;}u=p-1>>0;case 23:if(!(s>0)>>>0))>>>1>>>0)>>0));w=a.Less(u-v>>0,v);$s=28;case 28:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}if(!w){$s=25;continue;}$s=26;continue;case 25:s=v+1>>0;$s=27;continue;case 26:t=v;case 27:$s=23;continue;case 24:x=p-s>>0;if(s>0;f=d-c>>0;case 1:if(!(!((e===f)))){$s=2;continue;}if(e>f){$s=3;continue;}$s=4;continue;case 3:$r=N(a,c-e>>0,c,f);$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}e=e-(f)>>0;$s=5;continue;case 4:$r=N(a,c-e>>0,(c+f>>0)-e>>0,e);$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}f=f-(e)>>0;case 5:$s=1;continue;case 2:$r=N(a,c-e>>0,c,e);$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AJ};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};T.methods=[{prop:"Less",name:"Less",pkg:"",typ:$funcType([$Int,$Int],[$Bool],false)}];Z.methods=[{prop:"Search",name:"Search",pkg:"",typ:$funcType([$String],[$Int],false)},{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Less",name:"Less",pkg:"",typ:$funcType([$Int,$Int],[$Bool],false)},{prop:"Swap",name:"Swap",pkg:"",typ:$funcType([$Int,$Int],[],false)},{prop:"Sort",name:"Sort",pkg:"",typ:$funcType([],[],false)}];I.init([{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Less",name:"Less",pkg:"",typ:$funcType([$Int,$Int],[$Bool],false)},{prop:"Swap",name:"Swap",pkg:"",typ:$funcType([$Int,$Int],[],false)}]);T.init("",[{prop:"Interface",name:"Interface",embedded:true,exported:true,typ:I,tag:""}]);Z.init($String);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["internal/fmtsort"]=(function(){var $pkg={},$init,A,B,C,I,J,D,E,F,G,H;A=$packages["reflect"];B=$packages["sort"];C=$pkg.SortedMap=$newType(0,$kindStruct,"fmtsort.SortedMap",true,"internal/fmtsort",true,function(Key_,Value_){this.$val=this;if(arguments.length===0){this.Key=J.nil;this.Value=J.nil;return;}this.Key=Key_;this.Value=Value_;});I=$ptrType(C);J=$sliceType(A.Value);C.ptr.prototype.Len=function(){var a;a=this;return a.Key.$length;};C.prototype.Len=function(){return this.$val.Len();};C.ptr.prototype.Less=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;f=E($clone((d=c.Key,((a<0||a>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+a])),A.Value),$clone((e=c.Key,((b<0||b>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+b])),A.Value));$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$s=-1;return f<0;}return;}if($f===undefined){$f={$blk:C.ptr.prototype.Less};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};C.prototype.Less=function(a,b){return this.$val.Less(a,b);};C.ptr.prototype.Swap=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;c=this;d=(e=c.Key,((b<0||b>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+b]));f=(g=c.Key,((a<0||a>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+a]));(h=c.Key,((a<0||a>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+a]=d));(i=c.Key,((b<0||b>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+b]=f));j=(k=c.Value,((b<0||b>=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+b]));l=(m=c.Value,((a<0||a>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+a]));(n=c.Value,((a<0||a>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+a]=j));(o=c.Value,((b<0||b>=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+b]=l));};C.prototype.Swap=function(a,b){return this.$val.Swap(a,b);};D=function(a){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=$clone(a,A.Value).Type().Kind();$s=3;case 3:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}if(!((b===21))){$s=1;continue;}$s=2;continue;case 1:$s=-1;return I.nil;case 2:c=$makeSlice(J,$clone(a,A.Value).Len());d=$makeSlice(J,c.$length);e=$clone(a,A.Value).MapRange();f=0;case 4:g=e.Next();$s=6;case 6:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}if(!(g)){$s=5;continue;}h=e.Key();$s=7;case 7:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=h);i=e.Value();$s=8;case 8:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}((f<0||f>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+f]=i);f=f+(1)>>0;$s=4;continue;case 5:j=new C.ptr(c,d);$r=B.Stable(j);$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return j;}return;}if($f===undefined){$f={$blk:D};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Sort=D;E=function(a,b){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;b=$f.b;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;bj=$f.bj;bk=$f.bk;bl=$f.bl;bm=$f.bm;bn=$f.bn;bo=$f.bo;bp=$f.bp;bq=$f.bq;br=$f.br;bs=$f.bs;bt=$f.bt;bu=$f.bu;bv=$f.bv;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=$clone(a,A.Value).Type();d=$clone(b,A.Value).Type();e=c;f=d;if(!($interfaceIsEqual(e,f))){$s=-1;return-1;}g=$clone(a,A.Value).Kind();if((g===(2))||(g===(3))||(g===(4))||(g===(5))||(g===(6))){$s=2;continue;}if((g===(7))||(g===(8))||(g===(9))||(g===(10))||(g===(11))||(g===(12))){$s=3;continue;}if(g===(24)){$s=4;continue;}if((g===(13))||(g===(14))){$s=5;continue;}if((g===(15))||(g===(16))){$s=6;continue;}if(g===(1)){$s=7;continue;}if(g===(22)){$s=8;continue;}if(g===(18)){$s=9;continue;}if(g===(25)){$s=10;continue;}if(g===(17)){$s=11;continue;}if(g===(20)){$s=12;continue;}$s=13;continue;case 2:h=$clone(a,A.Value).Int();i=$clone(b,A.Value).Int();j=h;k=i;if((j.$highk.$high||(j.$high===k.$high&&j.$low>k.$low))){$s=-1;return 1;}else{$s=-1;return 0;}$s=14;continue;case 3:l=$clone(a,A.Value).Uint();m=$clone(b,A.Value).Uint();n=l;o=m;if((n.$higho.$high||(n.$high===o.$high&&n.$low>o.$low))){$s=-1;return 1;}else{$s=-1;return 0;}$s=14;continue;case 4:q=$clone(a,A.Value).String();$s=15;case 15:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}p=q;s=$clone(b,A.Value).String();$s=16;case 16:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}r=s;t=p;u=r;if(tu){$s=-1;return 1;}else{$s=-1;return 0;}$s=14;continue;case 5:$s=-1;return G($clone(a,A.Value).Float(),$clone(b,A.Value).Float());case 6:v=$clone(a,A.Value).Complex();w=$clone(b,A.Value).Complex();x=v;y=w;z=G(x.$real,y.$real);if(!((z===0))){$s=-1;return z;}$s=-1;return G(x.$imag,y.$imag);case 7:aa=$clone(a,A.Value).Bool();ab=$clone(b,A.Value).Bool();ac=aa;ad=ab;if(ac===ad){$s=-1;return 0;}else if(ac){$s=-1;return 1;}else{$s=-1;return-1;}$s=14;continue;case 8:ae=$clone(a,A.Value).Pointer();af=$clone(b,A.Value).Pointer();ag=ae;ah=af;if(agah){$s=-1;return 1;}else{$s=-1;return 0;}$s=14;continue;case 9:ai=F($clone(a,A.Value),$clone(b,A.Value));aj=ai[0];ak=ai[1];if(ak){$s=-1;return aj;}al=$clone(a,A.Value).Pointer();am=$clone(b,A.Value).Pointer();an=al;ao=am;if(anao){$s=-1;return 1;}else{$s=-1;return 0;}$s=14;continue;case 10:ap=0;case 17:if(!(ap<$clone(a,A.Value).NumField())){$s=18;continue;}aq=$clone(a,A.Value).Field(ap);$s=19;case 19:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=$clone(aq,A.Value);as=$clone(b,A.Value).Field(ap);$s=20;case 20:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}at=$clone(as,A.Value);au=E(ar,at);$s=21;case 21:if($c){$c=false;au=au.$blk();}if(au&&au.$blk!==undefined){break s;}av=au;if(!((av===0))){$s=-1;return av;}ap=ap+(1)>>0;$s=17;continue;case 18:$s=-1;return 0;case 11:aw=0;case 22:if(!(aw<$clone(a,A.Value).Len())){$s=23;continue;}ax=$clone(a,A.Value).Index(aw);$s=24;case 24:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}ay=$clone(ax,A.Value);az=$clone(b,A.Value).Index(aw);$s=25;case 25:if($c){$c=false;az=az.$blk();}if(az&&az.$blk!==undefined){break s;}ba=$clone(az,A.Value);bb=E(ay,ba);$s=26;case 26:if($c){$c=false;bb=bb.$blk();}if(bb&&bb.$blk!==undefined){break s;}bc=bb;if(!((bc===0))){$s=-1;return bc;}aw=aw+(1)>>0;$s=22;continue;case 23:$s=-1;return 0;case 12:bd=F($clone(a,A.Value),$clone(b,A.Value));be=bd[0];bf=bd[1];if(bf){$s=-1;return be;}bg=$clone(a,A.Value).Elem();$s=27;case 27:if($c){$c=false;bg=bg.$blk();}if(bg&&bg.$blk!==undefined){break s;}bh=$clone(bg,A.Value).Type();$s=28;case 28:if($c){$c=false;bh=bh.$blk();}if(bh&&bh.$blk!==undefined){break s;}bi=A.ValueOf(bh);$s=29;case 29:if($c){$c=false;bi=bi.$blk();}if(bi&&bi.$blk!==undefined){break s;}bj=$clone(bi,A.Value);bk=$clone(b,A.Value).Elem();$s=30;case 30:if($c){$c=false;bk=bk.$blk();}if(bk&&bk.$blk!==undefined){break s;}bl=$clone(bk,A.Value).Type();$s=31;case 31:if($c){$c=false;bl=bl.$blk();}if(bl&&bl.$blk!==undefined){break s;}bm=A.ValueOf(bl);$s=32;case 32:if($c){$c=false;bm=bm.$blk();}if(bm&&bm.$blk!==undefined){break s;}bn=$clone(bm,A.Value);bo=E(bj,bn);$s=33;case 33:if($c){$c=false;bo=bo.$blk();}if(bo&&bo.$blk!==undefined){break s;}bp=bo;if(!((bp===0))){$s=-1;return bp;}bq=$clone(a,A.Value).Elem();$s=34;case 34:if($c){$c=false;bq=bq.$blk();}if(bq&&bq.$blk!==undefined){break s;}br=$clone(bq,A.Value);bs=$clone(b,A.Value).Elem();$s=35;case 35:if($c){$c=false;bs=bs.$blk();}if(bs&&bs.$blk!==undefined){break s;}bt=$clone(bs,A.Value);bu=E(br,bt);$s=36;case 36:if($c){$c=false;bu=bu.$blk();}if(bu&&bu.$blk!==undefined){break s;}$s=-1;return bu;case 13:bv=e.String();$s=37;case 37:if($c){$c=false;bv=bv.$blk();}if(bv&&bv.$blk!==undefined){break s;}$panic(new $String("bad type in compare: "+bv));case 14:case 1:$s=-1;return 0;}return;}if($f===undefined){$f={$blk:E};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.b=b;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.bj=bj;$f.bk=bk;$f.bl=bl;$f.bm=bm;$f.bn=bn;$f.bo=bo;$f.bp=bp;$f.bq=bq;$f.br=br;$f.bs=bs;$f.bt=bt;$f.bu=bu;$f.bv=bv;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};F=function(a,b){var a,b;if($clone(a,A.Value).IsNil()){if($clone(b,A.Value).IsNil()){return[0,true];}return[-1,true];}if($clone(b,A.Value).IsNil()){return[1,true];}return[0,false];};G=function(a,b){var a,b;if(H(a)){return-1;}else if(H(b)){return 1;}else if(ab){return 1;}return 0;};H=function(a){var a;return!((a===a));};I.methods=[{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Less",name:"Less",pkg:"",typ:$funcType([$Int,$Int],[$Bool],false)},{prop:"Swap",name:"Swap",pkg:"",typ:$funcType([$Int,$Int],[],false)}];C.init("",[{prop:"Key",name:"Key",embedded:false,exported:true,typ:J,tag:""},{prop:"Value",name:"Value",embedded:false,exported:true,typ:J,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["io"]=(function(){var $pkg={},$init,A,B,C,D,E,K,O,P,V,W,AG,AY,AZ,AJ,AK,Z,AA,AC,AE;A=$packages["errors"];B=$packages["sync"];C=$packages["sync/atomic"];D=$pkg.Reader=$newType(8,$kindInterface,"io.Reader",true,"io",true,null);E=$pkg.Writer=$newType(8,$kindInterface,"io.Writer",true,"io",true,null);K=$pkg.ReadWriteCloser=$newType(8,$kindInterface,"io.ReadWriteCloser",true,"io",true,null);O=$pkg.ReaderFrom=$newType(8,$kindInterface,"io.ReaderFrom",true,"io",true,null);P=$pkg.WriterTo=$newType(8,$kindInterface,"io.WriterTo",true,"io",true,null);V=$pkg.RuneReader=$newType(8,$kindInterface,"io.RuneReader",true,"io",true,null);W=$pkg.RuneScanner=$newType(8,$kindInterface,"io.RuneScanner",true,"io",true,null);AG=$pkg.LimitedReader=$newType(0,$kindStruct,"io.LimitedReader",true,"io",true,function(R_,N_){this.$val=this;if(arguments.length===0){this.R=$ifaceNil;this.N=new $Int64(0,0);return;}this.R=R_;this.N=N_;});AY=$sliceType($Uint8);AZ=$ptrType(AG);Z=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=0;e=$ifaceNil;if(b.$length>0;$s=1;continue;case 2:if(d>=c){e=$ifaceNil;}else if(d>0&&$interfaceIsEqual(e,$pkg.EOF)){e=$pkg.ErrUnexpectedEOF;}$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:Z};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};$pkg.ReadAtLeast=Z;AA=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=0;d=$ifaceNil;f=Z(a,b,b.$length);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;c=e[0];d=e[1];$s=-1;return[c,d];}return;}if($f===undefined){$f={$blk:AA};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};$pkg.ReadFull=AA;AC=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=new $Int64(0,0);d=$ifaceNil;f=AE(a,b,AY.nil);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;c=e[0];d=e[1];$s=-1;return[c,d];}return;}if($f===undefined){$f={$blk:AC};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Copy=AC;AE=function(a,b,c){var a,aa,ab,ac,ad,ae,af,ag,ah,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=new $Int64(0,0);e=$ifaceNil;f=$assertType(b,P,true);g=f[0];h=f[1];if(h){$s=1;continue;}$s=2;continue;case 1:j=g.WriteTo(a);$s=3;case 3:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}i=j;d=i[0];e=i[1];$s=-1;return[d,e];case 2:k=$assertType(a,O,true);l=k[0];m=k[1];if(m){$s=4;continue;}$s=5;continue;case 4:o=l.ReadFrom(b);$s=6;case 6:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;d=n[0];e=n[1];$s=-1;return[d,e];case 5:if(c===AY.nil){p=32768;q=$assertType(b,AZ,true);r=q[0];s=q[1];if(s&&(t=(new $Int64(0,p)),u=r.N,(t.$high>u.$high||(t.$high===u.$high&&t.$low>u.$low)))){if((v=r.N,(v.$high<0||(v.$high===0&&v.$low<1)))){p=1;}else{p=(((w=r.N,w.$low+((w.$high>>31)*4294967296))>>0));}}c=$makeSlice(AY,p);}case 7:y=b.Read(c);$s=9;case 9:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}x=y;z=x[0];aa=x[1];if(z>0){$s=10;continue;}$s=11;continue;case 10:ac=a.Write($subslice(c,0,z));$s=12;case 12:if($c){$c=false;ac=ac.$blk();}if(ac&&ac.$blk!==undefined){break s;}ab=ac;ad=ab[0];ae=ab[1];if(ad>0){d=(af=(new $Int64(0,ad)),new $Int64(d.$high+af.$high,d.$low+af.$low));}if(!($interfaceIsEqual(ae,$ifaceNil))){e=ae;$s=8;continue;}if(!((z===ad))){e=$pkg.ErrShortWrite;$s=8;continue;}case 11:if(!($interfaceIsEqual(aa,$ifaceNil))){if(!($interfaceIsEqual(aa,$pkg.EOF))){e=aa;}$s=8;continue;}$s=7;continue;case 8:ag=d;ah=e;d=ag;e=ah;$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:AE};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};AG.ptr.prototype.Read=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;d=this;if((e=d.N,(e.$high<0||(e.$high===0&&e.$low<=0)))){f=0;g=$pkg.EOF;b=f;c=g;$s=-1;return[b,c];}if((h=(new $Int64(0,a.$length)),i=d.N,(h.$high>i.$high||(h.$high===i.$high&&h.$low>i.$low)))){a=$subslice(a,0,$flatten64(d.N));}k=d.R.Read(a);$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;b=j[0];c=j[1];d.N=(l=d.N,m=(new $Int64(0,b)),new $Int64(l.$high-m.$high,l.$low-m.$low));$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AG.ptr.prototype.Read};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};AG.prototype.Read=function(a){return this.$val.Read(a);};AZ.methods=[{prop:"Read",name:"Read",pkg:"",typ:$funcType([AY],[$Int,$error],false)}];D.init([{prop:"Read",name:"Read",pkg:"",typ:$funcType([AY],[$Int,$error],false)}]);E.init([{prop:"Write",name:"Write",pkg:"",typ:$funcType([AY],[$Int,$error],false)}]);K.init([{prop:"Close",name:"Close",pkg:"",typ:$funcType([],[$error],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([AY],[$Int,$error],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([AY],[$Int,$error],false)}]);O.init([{prop:"ReadFrom",name:"ReadFrom",pkg:"",typ:$funcType([D],[$Int64,$error],false)}]);P.init([{prop:"WriteTo",name:"WriteTo",pkg:"",typ:$funcType([E],[$Int64,$error],false)}]);V.init([{prop:"ReadRune",name:"ReadRune",pkg:"",typ:$funcType([],[$Int32,$Int,$error],false)}]);W.init([{prop:"ReadRune",name:"ReadRune",pkg:"",typ:$funcType([],[$Int32,$Int,$error],false)},{prop:"UnreadRune",name:"UnreadRune",pkg:"",typ:$funcType([],[$error],false)}]);AG.init("",[{prop:"R",name:"R",embedded:false,exported:true,typ:D,tag:""},{prop:"N",name:"N",embedded:false,exported:true,typ:$Int64,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$pkg.ErrShortWrite=A.New("short write");$pkg.ErrShortBuffer=A.New("short buffer");$pkg.EOF=A.New("EOF");$pkg.ErrUnexpectedEOF=A.New("unexpected EOF");$pkg.ErrNoProgress=A.New("multiple Read calls return no data or error");AJ=A.New("Seek: invalid whence");AK=A.New("Seek: invalid offset");$pkg.ErrClosedPipe=A.New("io: read/write on closed pipe");}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["syscall"]=(function(){var $pkg={},$init,A,D,B,C,CB,CF,CI,CL,EH,EI,GM,GN,GV,GW,GX,GY,NJ,NS,NX,NY,NZ,OA,OB,OC,OD,OE,OF,OG,OH,OI,OJ,OK,OS,OU,OW,OX,OY,PL,PN,PV,PZ,QF,QG,QH,QL,QM,QN,QO,QP,QT,QU,RA,RB,RC,RD,RE,RG,RK,RL,RO,RP,RQ,RR,RS,RT,RU,RV,RW,RX,RY,RZ,SF,E,F,P,Q,R,AG,FX,GO,GP,GQ,HR,PU,HW,G,H,I,K,L,M,S,T,U,V,Y,Z,AA,AB,AC,BC,BD,BZ,CA,CD,CE,CG,CH,CJ,CK,CM,CN,CZ,DA,DF,DH,DL,DP,EJ,EK,EL,EM,EU,EV,EX,FP,FQ,FR,FS,GA,GC,GR,GT,GU,GZ,HC,HD,HE,HF,HG,HH,HI,HJ,HL,HO,HZ,IB,IR,IS,IY,IZ,JB,JE,JF,JZ,KU,KY,LG,LH,LJ,LT,LU,LV,MF,MK,ML,MM,MO,MR,MS,MT,MW,MX,MY,MZ,NA,NB;A=$packages["github.com/gopherjs/gopherjs/js"];D=$packages["internal/race"];B=$packages["runtime"];C=$packages["sync"];CB=$pkg.RawConn=$newType(8,$kindInterface,"syscall.RawConn",true,"syscall",true,null);CF=$pkg.NetlinkRouteRequest=$newType(0,$kindStruct,"syscall.NetlinkRouteRequest",true,"syscall",true,function(Header_,Data_){this.$val=this;if(arguments.length===0){this.Header=new OS.ptr(0,0,0,0,0);this.Data=new OU.ptr(0);return;}this.Header=Header_;this.Data=Data_;});CI=$pkg.NetlinkMessage=$newType(0,$kindStruct,"syscall.NetlinkMessage",true,"syscall",true,function(Header_,Data_){this.$val=this;if(arguments.length===0){this.Header=new OS.ptr(0,0,0,0,0);this.Data=PL.nil;return;}this.Header=Header_;this.Data=Data_;});CL=$pkg.NetlinkRouteAttr=$newType(0,$kindStruct,"syscall.NetlinkRouteAttr",true,"syscall",true,function(Attr_,Value_){this.$val=this;if(arguments.length===0){this.Attr=new OW.ptr(0,0);this.Value=PL.nil;return;}this.Attr=Attr_;this.Value=Value_;});EH=$pkg.SockaddrLinklayer=$newType(0,$kindStruct,"syscall.SockaddrLinklayer",true,"syscall",true,function(Protocol_,Ifindex_,Hatype_,Pkttype_,Halen_,Addr_,raw_){this.$val=this;if(arguments.length===0){this.Protocol=0;this.Ifindex=0;this.Hatype=0;this.Pkttype=0;this.Halen=0;this.Addr=QF.zero();this.raw=new OA.ptr(0,0,0,0,0,0,QF.zero());return;}this.Protocol=Protocol_;this.Ifindex=Ifindex_;this.Hatype=Hatype_;this.Pkttype=Pkttype_;this.Halen=Halen_;this.Addr=Addr_;this.raw=raw_;});EI=$pkg.SockaddrNetlink=$newType(0,$kindStruct,"syscall.SockaddrNetlink",true,"syscall",true,function(Family_,Pad_,Pid_,Groups_,raw_){this.$val=this;if(arguments.length===0){this.Family=0;this.Pad=0;this.Pid=0;this.Groups=0;this.raw=new OB.ptr(0,0,0,0);return;}this.Family=Family_;this.Pad=Pad_;this.Pid=Pid_;this.Groups=Groups_;this.raw=raw_;});GM=$pkg.mmapper=$newType(0,$kindStruct,"syscall.mmapper",true,"syscall",false,function(Mutex_,active_,mmap_,munmap_){this.$val=this;if(arguments.length===0){this.Mutex=new C.Mutex.ptr(0,0);this.active=false;this.mmap=$throwNilPointerError;this.munmap=$throwNilPointerError;return;}this.Mutex=Mutex_;this.active=active_;this.mmap=mmap_;this.munmap=munmap_;});GN=$pkg.Errno=$newType(4,$kindUintptr,"syscall.Errno",true,"syscall",true,null);GV=$pkg.Sockaddr=$newType(8,$kindInterface,"syscall.Sockaddr",true,"syscall",true,null);GW=$pkg.SockaddrInet4=$newType(0,$kindStruct,"syscall.SockaddrInet4",true,"syscall",true,function(Port_,Addr_,raw_){this.$val=this;if(arguments.length===0){this.Port=0;this.Addr=RB.zero();this.raw=new NX.ptr(0,0,RB.zero(),QF.zero());return;}this.Port=Port_;this.Addr=Addr_;this.raw=raw_;});GX=$pkg.SockaddrInet6=$newType(0,$kindStruct,"syscall.SockaddrInet6",true,"syscall",true,function(Port_,ZoneId_,Addr_,raw_){this.$val=this;if(arguments.length===0){this.Port=0;this.ZoneId=0;this.Addr=QH.zero();this.raw=new NY.ptr(0,0,0,QH.zero(),0);return;}this.Port=Port_;this.ZoneId=ZoneId_;this.Addr=Addr_;this.raw=raw_;});GY=$pkg.SockaddrUnix=$newType(0,$kindStruct,"syscall.SockaddrUnix",true,"syscall",true,function(Name_,raw_){this.$val=this;if(arguments.length===0){this.Name="";this.raw=new NZ.ptr(0,RA.zero());return;}this.Name=Name_;this.raw=raw_;});NJ=$pkg.Timespec=$newType(0,$kindStruct,"syscall.Timespec",true,"syscall",true,function(Sec_,Nsec_){this.$val=this;if(arguments.length===0){this.Sec=new $Int64(0,0);this.Nsec=new $Int64(0,0);return;}this.Sec=Sec_;this.Nsec=Nsec_;});NS=$pkg.Stat_t=$newType(0,$kindStruct,"syscall.Stat_t",true,"syscall",true,function(Dev_,Ino_,Nlink_,Mode_,Uid_,Gid_,X__pad0_,Rdev_,Size_,Blksize_,Blocks_,Atim_,Mtim_,Ctim_,X__unused_){this.$val=this;if(arguments.length===0){this.Dev=new $Uint64(0,0);this.Ino=new $Uint64(0,0);this.Nlink=new $Uint64(0,0);this.Mode=0;this.Uid=0;this.Gid=0;this.X__pad0=0;this.Rdev=new $Uint64(0,0);this.Size=new $Int64(0,0);this.Blksize=new $Int64(0,0);this.Blocks=new $Int64(0,0);this.Atim=new NJ.ptr(new $Int64(0,0),new $Int64(0,0));this.Mtim=new NJ.ptr(new $Int64(0,0),new $Int64(0,0));this.Ctim=new NJ.ptr(new $Int64(0,0),new $Int64(0,0));this.X__unused=QU.zero();return;}this.Dev=Dev_;this.Ino=Ino_;this.Nlink=Nlink_;this.Mode=Mode_;this.Uid=Uid_;this.Gid=Gid_;this.X__pad0=X__pad0_;this.Rdev=Rdev_;this.Size=Size_;this.Blksize=Blksize_;this.Blocks=Blocks_;this.Atim=Atim_;this.Mtim=Mtim_;this.Ctim=Ctim_;this.X__unused=X__unused_;});NX=$pkg.RawSockaddrInet4=$newType(0,$kindStruct,"syscall.RawSockaddrInet4",true,"syscall",true,function(Family_,Port_,Addr_,Zero_){this.$val=this;if(arguments.length===0){this.Family=0;this.Port=0;this.Addr=RB.zero();this.Zero=QF.zero();return;}this.Family=Family_;this.Port=Port_;this.Addr=Addr_;this.Zero=Zero_;});NY=$pkg.RawSockaddrInet6=$newType(0,$kindStruct,"syscall.RawSockaddrInet6",true,"syscall",true,function(Family_,Port_,Flowinfo_,Addr_,Scope_id_){this.$val=this;if(arguments.length===0){this.Family=0;this.Port=0;this.Flowinfo=0;this.Addr=QH.zero();this.Scope_id=0;return;}this.Family=Family_;this.Port=Port_;this.Flowinfo=Flowinfo_;this.Addr=Addr_;this.Scope_id=Scope_id_;});NZ=$pkg.RawSockaddrUnix=$newType(0,$kindStruct,"syscall.RawSockaddrUnix",true,"syscall",true,function(Family_,Path_){this.$val=this;if(arguments.length===0){this.Family=0;this.Path=RA.zero();return;}this.Family=Family_;this.Path=Path_;});OA=$pkg.RawSockaddrLinklayer=$newType(0,$kindStruct,"syscall.RawSockaddrLinklayer",true,"syscall",true,function(Family_,Protocol_,Ifindex_,Hatype_,Pkttype_,Halen_,Addr_){this.$val=this;if(arguments.length===0){this.Family=0;this.Protocol=0;this.Ifindex=0;this.Hatype=0;this.Pkttype=0;this.Halen=0;this.Addr=QF.zero();return;}this.Family=Family_;this.Protocol=Protocol_;this.Ifindex=Ifindex_;this.Hatype=Hatype_;this.Pkttype=Pkttype_;this.Halen=Halen_;this.Addr=Addr_;});OB=$pkg.RawSockaddrNetlink=$newType(0,$kindStruct,"syscall.RawSockaddrNetlink",true,"syscall",true,function(Family_,Pad_,Pid_,Groups_){this.$val=this;if(arguments.length===0){this.Family=0;this.Pad=0;this.Pid=0;this.Groups=0;return;}this.Family=Family_;this.Pad=Pad_;this.Pid=Pid_;this.Groups=Groups_;});OC=$pkg.RawSockaddr=$newType(0,$kindStruct,"syscall.RawSockaddr",true,"syscall",true,function(Family_,Data_){this.$val=this;if(arguments.length===0){this.Family=0;this.Data=RC.zero();return;}this.Family=Family_;this.Data=Data_;});OD=$pkg.RawSockaddrAny=$newType(0,$kindStruct,"syscall.RawSockaddrAny",true,"syscall",true,function(Addr_,Pad_){this.$val=this;if(arguments.length===0){this.Addr=new OC.ptr(0,RC.zero());this.Pad=RD.zero();return;}this.Addr=Addr_;this.Pad=Pad_;});OE=$pkg._Socklen=$newType(4,$kindUint32,"syscall._Socklen",true,"syscall",false,null);OF=$pkg.Linger=$newType(0,$kindStruct,"syscall.Linger",true,"syscall",true,function(Onoff_,Linger_){this.$val=this;if(arguments.length===0){this.Onoff=0;this.Linger=0;return;}this.Onoff=Onoff_;this.Linger=Linger_;});OG=$pkg.Iovec=$newType(0,$kindStruct,"syscall.Iovec",true,"syscall",true,function(Base_,Len_){this.$val=this;if(arguments.length===0){this.Base=PV.nil;this.Len=new $Uint64(0,0);return;}this.Base=Base_;this.Len=Len_;});OH=$pkg.IPMreq=$newType(0,$kindStruct,"syscall.IPMreq",true,"syscall",true,function(Multiaddr_,Interface_){this.$val=this;if(arguments.length===0){this.Multiaddr=RB.zero();this.Interface=RB.zero();return;}this.Multiaddr=Multiaddr_;this.Interface=Interface_;});OI=$pkg.IPMreqn=$newType(0,$kindStruct,"syscall.IPMreqn",true,"syscall",true,function(Multiaddr_,Address_,Ifindex_){this.$val=this;if(arguments.length===0){this.Multiaddr=RB.zero();this.Address=RB.zero();this.Ifindex=0;return;}this.Multiaddr=Multiaddr_;this.Address=Address_;this.Ifindex=Ifindex_;});OJ=$pkg.IPv6Mreq=$newType(0,$kindStruct,"syscall.IPv6Mreq",true,"syscall",true,function(Multiaddr_,Interface_){this.$val=this;if(arguments.length===0){this.Multiaddr=QH.zero();this.Interface=0;return;}this.Multiaddr=Multiaddr_;this.Interface=Interface_;});OK=$pkg.Msghdr=$newType(0,$kindStruct,"syscall.Msghdr",true,"syscall",true,function(Name_,Namelen_,Pad_cgo_0_,Iov_,Iovlen_,Control_,Controllen_,Flags_,Pad_cgo_1_){this.$val=this;if(arguments.length===0){this.Name=PV.nil;this.Namelen=0;this.Pad_cgo_0=RB.zero();this.Iov=RG.nil;this.Iovlen=new $Uint64(0,0);this.Control=PV.nil;this.Controllen=new $Uint64(0,0);this.Flags=0;this.Pad_cgo_1=RB.zero();return;}this.Name=Name_;this.Namelen=Namelen_;this.Pad_cgo_0=Pad_cgo_0_;this.Iov=Iov_;this.Iovlen=Iovlen_;this.Control=Control_;this.Controllen=Controllen_;this.Flags=Flags_;this.Pad_cgo_1=Pad_cgo_1_;});OS=$pkg.NlMsghdr=$newType(0,$kindStruct,"syscall.NlMsghdr",true,"syscall",true,function(Len_,Type_,Flags_,Seq_,Pid_){this.$val=this;if(arguments.length===0){this.Len=0;this.Type=0;this.Flags=0;this.Seq=0;this.Pid=0;return;}this.Len=Len_;this.Type=Type_;this.Flags=Flags_;this.Seq=Seq_;this.Pid=Pid_;});OU=$pkg.RtGenmsg=$newType(0,$kindStruct,"syscall.RtGenmsg",true,"syscall",true,function(Family_){this.$val=this;if(arguments.length===0){this.Family=0;return;}this.Family=Family_;});OW=$pkg.RtAttr=$newType(0,$kindStruct,"syscall.RtAttr",true,"syscall",true,function(Len_,Type_){this.$val=this;if(arguments.length===0){this.Len=0;this.Type=0;return;}this.Len=Len_;this.Type=Type_;});OX=$pkg.IfInfomsg=$newType(0,$kindStruct,"syscall.IfInfomsg",true,"syscall",true,function(Family_,X__ifi_pad_,Type_,Index_,Flags_,Change_){this.$val=this;if(arguments.length===0){this.Family=0;this.X__ifi_pad=0;this.Type=0;this.Index=0;this.Flags=0;this.Change=0;return;}this.Family=Family_;this.X__ifi_pad=X__ifi_pad_;this.Type=Type_;this.Index=Index_;this.Flags=Flags_;this.Change=Change_;});OY=$pkg.IfAddrmsg=$newType(0,$kindStruct,"syscall.IfAddrmsg",true,"syscall",true,function(Family_,Prefixlen_,Flags_,Scope_,Index_){this.$val=this;if(arguments.length===0){this.Family=0;this.Prefixlen=0;this.Flags=0;this.Scope=0;this.Index=0;return;}this.Family=Family_;this.Prefixlen=Prefixlen_;this.Flags=Flags_;this.Scope=Scope_;this.Index=Index_;});PL=$sliceType($Uint8);PN=$sliceType($String);PV=$ptrType($Uint8);PZ=$ptrType($Int32);QF=$arrayType($Uint8,8);QG=$ptrType($Uint16);QH=$arrayType($Uint8,16);QL=$ptrType(EI);QM=$sliceType(CI);QN=$ptrType(OS);QO=$sliceType(CL);QP=$ptrType(OW);QT=$arrayType($Uint8,32);QU=$arrayType($Int64,3);RA=$arrayType($Int8,108);RB=$arrayType($Uint8,4);RC=$arrayType($Int8,14);RD=$arrayType($Int8,96);RE=$ptrType(OE);RG=$ptrType(OG);RK=$structType("syscall",[{prop:"addr",name:"addr",embedded:false,exported:false,typ:$Uintptr,tag:""},{prop:"len",name:"len",embedded:false,exported:false,typ:$Int,tag:""},{prop:"cap",name:"cap",embedded:false,exported:false,typ:$Int,tag:""}]);RL=$ptrType($Int64);RO=$funcType([$Uintptr],[],false);RP=$funcType([$Uintptr],[$Bool],false);RQ=$ptrType(CF);RR=$ptrType(EH);RS=$ptrType(GM);RT=$mapType(PV,PL);RU=$funcType([$Uintptr,$Uintptr,$Int,$Int,$Int,$Int64],[$Uintptr,$error],false);RV=$funcType([$Uintptr,$Uintptr],[$error],false);RW=$ptrType(GW);RX=$ptrType(GX);RY=$ptrType(GY);RZ=$ptrType(NJ);SF=$ptrType(OK);G=function(){$flushConsole=(function(){if(!((F.$length===0))){$global.console.log($externalize(($bytesToString(F)),$String));F=PL.nil;}});};H=function(){if(!E){$global.console.error($externalize("warning: system calls not available, see https://github.com/gopherjs/gopherjs/blob/master/doc/syscalls.md",$String));}E=true;};I=function(k){var k,l,m;l=$global.goPrintToConsole;if(!(l===undefined)){l(k);return;}F=$appendSlice(F,k);while(true){m=L(F,10);if(m===-1){break;}$global.console.log($externalize(($bytesToString($subslice(F,0,m))),$String));F=$subslice(F,(m+1>>0));}};K=function(k){var k;T(231,((k>>>0)),0,0);};$pkg.Exit=K;L=function(k,l){var k,l,m,n,o,p;m=k;n=0;while(true){if(!(n=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+n]);if(p===l){return o;}n++;}return-1;};M=function(){var k,l,m,n,o,p;k=$global.process;if(k===undefined){return PN.nil;}l=k.env;m=$global.Object.keys(l);n=$makeSlice(PN,$parseInt(m.length));o=0;while(true){if(!(o<$parseInt(m.length))){break;}p=$internalize(m[o],$String);((o<0||o>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]=p+"="+$internalize(l[$externalize(p,$String)],$String));o=o+(1)>>0;}return n;};S=function(k){var k,l,$deferred;var $err=null;try{$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);$deferred.push([(function(){$recover();}),[]]);if(P===null){if(Q){return null;}Q=true;l=$global.require;if(l===undefined){$panic(new $String(""));}P=l($externalize("syscall",$String));}return P[$externalize(k,$String)];}catch(err){$err=err;return null;}finally{$callDeferred($deferred,$err);}};T=function(k,l,m,n){var aa,ab,ac,ad,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;o=0;p=0;q=0;r=S("Syscall");if(!(r===null)){s=r(k,l,m,n);t=((($parseInt(s[0])>>0)>>>0));u=((($parseInt(s[1])>>0)>>>0));v=((($parseInt(s[2])>>0)>>>0));o=t;p=u;q=v;return[o,p,q];}if((k===1)&&((l===1)||(l===2))){w=m;x=$makeSlice(PL,$parseInt(w.length));x.$array=w;I(x);y=(($parseInt(w.length)>>>0));z=0;aa=0;o=y;p=z;q=aa;return[o,p,q];}if(k===231){B.Goexit();}H();ab=((R>>>0));ac=0;ad=13;o=ab;p=ac;q=ad;return[o,p,q];};$pkg.Syscall=T;U=function(k,l,m,n,o,p,q){var aa,ab,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;r=0;s=0;t=0;u=S("Syscall6");if(!(u===null)){v=u(k,l,m,n,o,p,q);w=((($parseInt(v[0])>>0)>>>0));x=((($parseInt(v[1])>>0)>>>0));y=((($parseInt(v[2])>>0)>>>0));r=w;s=x;t=y;return[r,s,t];}if(!((k===202))){H();}z=((R>>>0));aa=0;ab=13;r=z;s=aa;t=ab;return[r,s,t];};$pkg.Syscall6=U;V=function(k,l,m,n){var k,l,m,n,o,p,q,r,s,t,u,v,w,x,y;o=0;p=0;q=0;r=S("Syscall");if(!(r===null)){s=r(k,l,m,n);t=((($parseInt(s[0])>>0)>>>0));u=((($parseInt(s[1])>>0)>>>0));v=((($parseInt(s[2])>>0)>>>0));o=t;p=u;q=v;return[o,p,q];}H();w=((R>>>0));x=0;y=13;o=w;p=x;q=y;return[o,p,q];};$pkg.RawSyscall=V;Y=function(k){var k,l,m,n,o,p;l=new($global.Uint8Array)(k.length+1>>0);m=(new PL($stringToBytes(k)));n=0;while(true){if(!(n=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+n]);if(p===0){return[PV.nil,new GN(22)];}l[o]=p;n++;}l[k.length]=0;return[((l)),$ifaceNil];};$pkg.BytePtrFromString=Y;Z=function(k,l,m){var k,l,m,n,o,p,q,r,s,t,u;n=new $Uint64(0,0);o=false;if(k.$length<(((l+m>>>0)>>0))){p=new $Uint64(0,0);q=false;n=p;o=q;return[n,o];}if(false){r=AA($subslice(k,l),m);s=true;n=r;o=s;return[n,o];}t=AB($subslice(k,l),m);u=true;n=t;o=u;return[n,o];};AA=function(k,l){var aa,ab,ac,ad,ae,af,ag,ah,ai,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;m=l;if(m===(1)){return(new $Uint64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0])));}else if(m===(2)){$unused((1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]));return(n=(new $Uint64(0,(1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]))),o=$shiftLeft64((new $Uint64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]))),8),new $Uint64(n.$high|o.$high,(n.$low|o.$low)>>>0));}else if(m===(4)){$unused((3>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+3]));return(p=(q=(r=(new $Uint64(0,(3>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+3]))),s=$shiftLeft64((new $Uint64(0,(2>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+2]))),8),new $Uint64(r.$high|s.$high,(r.$low|s.$low)>>>0)),t=$shiftLeft64((new $Uint64(0,(1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]))),16),new $Uint64(q.$high|t.$high,(q.$low|t.$low)>>>0)),u=$shiftLeft64((new $Uint64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]))),24),new $Uint64(p.$high|u.$high,(p.$low|u.$low)>>>0));}else if(m===(8)){$unused((7>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+7]));return(v=(w=(x=(y=(z=(aa=(ab=(new $Uint64(0,(7>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+7]))),ac=$shiftLeft64((new $Uint64(0,(6>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+6]))),8),new $Uint64(ab.$high|ac.$high,(ab.$low|ac.$low)>>>0)),ad=$shiftLeft64((new $Uint64(0,(5>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+5]))),16),new $Uint64(aa.$high|ad.$high,(aa.$low|ad.$low)>>>0)),ae=$shiftLeft64((new $Uint64(0,(4>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+4]))),24),new $Uint64(z.$high|ae.$high,(z.$low|ae.$low)>>>0)),af=$shiftLeft64((new $Uint64(0,(3>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+3]))),32),new $Uint64(y.$high|af.$high,(y.$low|af.$low)>>>0)),ag=$shiftLeft64((new $Uint64(0,(2>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+2]))),40),new $Uint64(x.$high|ag.$high,(x.$low|ag.$low)>>>0)),ah=$shiftLeft64((new $Uint64(0,(1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]))),48),new $Uint64(w.$high|ah.$high,(w.$low|ah.$low)>>>0)),ai=$shiftLeft64((new $Uint64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]))),56),new $Uint64(v.$high|ai.$high,(v.$low|ai.$low)>>>0));}else{$panic(new $String("syscall: readInt with unsupported size"));}};AB=function(k,l){var aa,ab,ac,ad,ae,af,ag,ah,ai,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;m=l;if(m===(1)){return(new $Uint64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0])));}else if(m===(2)){$unused((1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]));return(n=(new $Uint64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]))),o=$shiftLeft64((new $Uint64(0,(1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]))),8),new $Uint64(n.$high|o.$high,(n.$low|o.$low)>>>0));}else if(m===(4)){$unused((3>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+3]));return(p=(q=(r=(new $Uint64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]))),s=$shiftLeft64((new $Uint64(0,(1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]))),8),new $Uint64(r.$high|s.$high,(r.$low|s.$low)>>>0)),t=$shiftLeft64((new $Uint64(0,(2>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+2]))),16),new $Uint64(q.$high|t.$high,(q.$low|t.$low)>>>0)),u=$shiftLeft64((new $Uint64(0,(3>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+3]))),24),new $Uint64(p.$high|u.$high,(p.$low|u.$low)>>>0));}else if(m===(8)){$unused((7>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+7]));return(v=(w=(x=(y=(z=(aa=(ab=(new $Uint64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]))),ac=$shiftLeft64((new $Uint64(0,(1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]))),8),new $Uint64(ab.$high|ac.$high,(ab.$low|ac.$low)>>>0)),ad=$shiftLeft64((new $Uint64(0,(2>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+2]))),16),new $Uint64(aa.$high|ad.$high,(aa.$low|ad.$low)>>>0)),ae=$shiftLeft64((new $Uint64(0,(3>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+3]))),24),new $Uint64(z.$high|ae.$high,(z.$low|ae.$low)>>>0)),af=$shiftLeft64((new $Uint64(0,(4>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+4]))),32),new $Uint64(y.$high|af.$high,(y.$low|af.$low)>>>0)),ag=$shiftLeft64((new $Uint64(0,(5>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+5]))),40),new $Uint64(x.$high|ag.$high,(x.$low|ag.$low)>>>0)),ah=$shiftLeft64((new $Uint64(0,(6>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+6]))),48),new $Uint64(w.$high|ah.$high,(w.$low|ah.$low)>>>0)),ai=$shiftLeft64((new $Uint64(0,(7>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+7]))),56),new $Uint64(v.$high|ai.$high,(v.$low|ai.$low)>>>0));}else{$panic(new $String("syscall: readInt with unsupported size"));}};AC=function(k,l,m){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;n=0;o=0;p=PN.nil;q=k.$length;o=0;while(true){if(!(!((l===0))&&k.$length>0)){break;}r=FR(k);s=r[0];t=r[1];if(!t||(u=(new $Uint64(0,k.$length)),(s.$high>u.$high||(s.$high===u.$high&&s.$low>u.$low)))){v=q;w=o;x=m;n=v;o=w;p=x;return[n,o,p];}y=$subslice(k,0,$flatten64(s));k=$subslice(k,$flatten64(s));z=FQ(y);aa=z[0];t=z[1];if(!t){break;}if((aa.$high===0&&aa.$low===0)){continue;}ab=FS(y);ac=ab[0];t=ab[1];if(!t||(ad=new $Uint64(0+ac.$high,19+ac.$low),ae=(new $Uint64(0,y.$length)),(ad.$high>ae.$high||(ad.$high===ae.$high&&ad.$low>ae.$low)))){break;}af=$subslice(y,19,$flatten64(new $Uint64(0+ac.$high,19+ac.$low)));ag=af;ah=0;while(true){if(!(ah=ag.$length)?($throwRuntimeError("index out of range"),undefined):ag.$array[ag.$offset+ah]);if(aj===0){af=$subslice(af,0,ai);break;}ah++;}if(($bytesToString(af))==="."||($bytesToString(af))===".."){continue;}l=l-(1)>>0;o=o+(1)>>0;m=$append(m,($bytesToString(af)));}ak=q-k.$length>>0;al=o;am=m;n=ak;o=al;p=am;return[n,o,p];};$pkg.ParseDirent=AC;BC=function(k){var k;JB(k,2,1);};$pkg.CloseOnExec=BC;BD=function(k,l){var k,l,m,n,o,p;m=$ifaceNil;n=JB(k,3,0);o=n[0];m=n[1];if(!($interfaceIsEqual(m,$ifaceNil))){m=m;return m;}if(l){o=o|(2048);}else{o=(o&~(2048))>>0;}p=JB(k,4,o);m=p[1];m=m;return m;};$pkg.SetNonblock=BD;BZ=function(k,l){var k,l;};CA=function(k,l){var k,l;};CD=function(k){var k;return(((k+4>>0)-1>>0))&-4;};CE=function(k){var k;return(((k+4>>0)-1>>0))&-4;};CF.ptr.prototype.toWireFormat=function(){var k,l;k=this;l=$makeSlice(PL,k.Header.Len);(($sliceToArray($subslice(l,0,4)))).$set(k.Header.Len);(($sliceToArray($subslice(l,4,6)))).$set(k.Header.Type);(($sliceToArray($subslice(l,6,8)))).$set(k.Header.Flags);(($sliceToArray($subslice(l,8,12)))).$set(k.Header.Seq);(($sliceToArray($subslice(l,12,16)))).$set(k.Header.Pid);(16>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+16]=(k.Data.Family));return l;};CF.prototype.toWireFormat=function(){return this.$val.toWireFormat();};CG=function(k,l,m){var k,l,m,n;n=new CF.ptr(new OS.ptr(0,0,0,0,0),new OU.ptr(0));n.Header.Len=17;n.Header.Type=((k<<16>>>16));n.Header.Flags=769;n.Header.Seq=((l>>>0));n.Data.Family=((m<<24>>>24));return n.toWireFormat();};CH=function(k,l){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);m=HO(16,3,0);n=m[0];o=m[1];if(!($interfaceIsEqual(o,$ifaceNil))){$s=-1;return[PL.nil,o];}$deferred.push([IR,[n]]);p=new EI.ptr(16,0,0,0,new OB.ptr(0,0,0,0));q=GZ(n,p);$s=1;case 1:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}r=q;if(!($interfaceIsEqual(r,$ifaceNil))){$s=-1;return[PL.nil,r];}s=CG(k,1,l);t=HE(n,s,0,p);$s=2;case 2:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}u=t;if(!($interfaceIsEqual(u,$ifaceNil))){$s=-1;return[PL.nil,u];}v=PL.nil;w=$makeSlice(PL,DF());done:while(true){x=w;y=HD(n,x,0);z=y[0];aa=y[2];if(!($interfaceIsEqual(aa,$ifaceNil))){$s=-1;return[PL.nil,aa];}if(z<16){$s=-1;return[PL.nil,new GN(22)];}x=$subslice(x,0,z);v=$appendSlice(v,x);ab=CJ(x);ac=ab[0];aa=ab[1];if(!($interfaceIsEqual(aa,$ifaceNil))){$s=-1;return[PL.nil,aa];}ad=ac;ae=0;while(true){if(!(ae=ad.$length)?($throwRuntimeError("index out of range"),undefined):ad.$array[ad.$offset+ae]),CI);ag=EM(n);ah=ag[0];ai=ag[1];if(!($interfaceIsEqual(ai,$ifaceNil))){$s=-1;return[PL.nil,ai];}aj=ah;if($assertType(aj,QL,true)[1]){ak=aj.$val;if(!((af.Header.Seq===1))||!((af.Header.Pid===ak.Pid))){$s=-1;return[PL.nil,new GN(22)];}}else{al=aj;$s=-1;return[PL.nil,new GN(22)];}if(af.Header.Type===3){break done;}if(af.Header.Type===2){$s=-1;return[PL.nil,new GN(22)];}ae++;}}$s=-1;return[v,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[PL.nil,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:CH};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};$pkg.NetlinkRIB=CH;CJ=function(k){var k,l,m,n,o,p,q,r;l=QM.nil;while(true){if(!(k.$length>=16)){break;}m=CK(k);n=m[0];o=m[1];p=m[2];q=m[3];if(!($interfaceIsEqual(q,$ifaceNil))){return[QM.nil,q];}r=new CI.ptr($clone(n,OS),$subslice(o,0,(((n.Len>>0))-16>>0)));l=$append(l,r);k=$subslice(k,p);}return[l,$ifaceNil];};$pkg.ParseNetlinkMessage=CJ;CK=function(k){var k,l,m,n,o,p;o=((l=($sliceToArray(k)),m=new OS.ptr(0,0,0,0,0),n=new DataView(l.buffer,l.byteOffset),m.Len=n.getUint32(0,true),m.Type=n.getUint16(4,true),m.Flags=n.getUint16(6,true),m.Seq=n.getUint32(8,true),m.Pid=n.getUint32(12,true),m));p=CD(((o.Len>>0)));if(((o.Len>>0))<16||p>k.$length){return[QN.nil,PL.nil,0,new GN(22)];}return[o,$subslice(k,16),p,$ifaceNil];};CM=function(k){var k,l,m,n,o,p,q,r,s,t;l=PL.nil;m=k.Header.Type;if((m===(16))||(m===(17))){l=$subslice(k.Data,16);}else if((m===(20))||(m===(21))){l=$subslice(k.Data,8);}else if((m===(24))||(m===(25))){l=$subslice(k.Data,12);}else{return[QO.nil,new GN(22)];}n=QO.nil;while(true){if(!(l.$length>=4)){break;}o=CN(l);p=o[0];q=o[1];r=o[2];s=o[3];if(!($interfaceIsEqual(s,$ifaceNil))){return[QO.nil,s];}t=new CL.ptr($clone(p,OW),$subslice(q,0,(((p.Len>>0))-4>>0)));n=$append(n,t);l=$subslice(l,r);}return[n,$ifaceNil];};$pkg.ParseNetlinkRouteAttr=CM;CN=function(k){var k,l,m,n,o;o=((l=($sliceToArray(k)),m=new OW.ptr(0,0),n=new DataView(l.buffer,l.byteOffset),m.Len=n.getUint16(0,true),m.Type=n.getUint16(2,true),m));if(((o.Len>>0))<4||((o.Len>>0))>k.$length){return[QP.nil,PL.nil,0,new GN(22)];}return[o,$subslice(k,4),CE(((o.Len>>0))),$ifaceNil];};CZ=function(k){var k;if(k<0){return"-"+DA(((-k>>>0)));}return DA(((k>>>0)));};DA=function(k){var k,l,m,n,o;l=QT.zero();m=31;while(true){if(!(k>=10)){break;}((m<0||m>=l.length)?($throwRuntimeError("index out of range"),undefined):l[m]=((((n=k%10,n===n?n:$throwRuntimeError("integer divide by zero"))+48>>>0)<<24>>>24)));m=m-(1)>>0;k=(o=k/(10),(o===o&&o!==1/0&&o!==-1/0)?o>>>0:$throwRuntimeError("integer divide by zero"));}((m<0||m>=l.length)?($throwRuntimeError("index out of range"),undefined):l[m]=(((k+48>>>0)<<24>>>24)));return($bytesToString($subslice(new PL(l),m)));};NJ.ptr.prototype.Unix=function(){var k,l,m,n,o;k=new $Int64(0,0);l=new $Int64(0,0);m=this;n=(m.Sec);o=(m.Nsec);k=n;l=o;return[k,l];};NJ.prototype.Unix=function(){return this.$val.Unix();};NJ.ptr.prototype.Nano=function(){var k,l,m;k=this;return(l=$mul64((k.Sec),new $Int64(0,1000000000)),m=(k.Nsec),new $Int64(l.$high+m.$high,l.$low+m.$low));};NJ.prototype.Nano=function(){return this.$val.Nano();};DF=function(){$throwRuntimeError("native function not implemented: syscall.Getpagesize");};$pkg.Getpagesize=DF;DH=function(k,l){var k,l,m;m=$ifaceNil;m=DL(-100,k,l,0);return m;};$pkg.Chmod=DH;DL=function(k,l,m,n){var k,l,m,n,o;o=$ifaceNil;if(!((((n&~256)>>0)===0))){o=new GN(22);return o;}else if(!(((n&256)===0))){o=new GN(95);return o;}o=HZ(k,l,m);return o;};$pkg.Fchmodat=DL;DP=function(k,l,m){var k,l,m,n,o,p;n=0;o=$ifaceNil;p=IB(-100,k,l|0,m);n=p[0];o=p[1];return[n,o];};$pkg.Open=DP;GW.ptr.prototype.sockaddr=function(){var k,l,m,n,o,p,q,r,s;k=this;if(k.Port<0||k.Port>65535){return[0,0,new GN(22)];}k.raw.Family=2;m=(((l=k.raw,(l.$ptr_Port||(l.$ptr_Port=new QG(function(){return this.$target.Port;},function($v){this.$target.Port=$v;},l))))));m.nilCheck,m[0]=(((k.Port>>8>>0)<<24>>>24));m.nilCheck,m[1]=((k.Port<<24>>>24));n=0;while(true){if(!(n<4)){break;}(p=k.raw.Addr,((n<0||n>=p.length)?($throwRuntimeError("index out of range"),undefined):p[n]=(o=k.Addr,((n<0||n>=o.length)?($throwRuntimeError("index out of range"),undefined):o[n]))));n=n+(1)>>0;}q=new Uint8Array(16);return[(q),16,$ifaceNil];};GW.prototype.sockaddr=function(){return this.$val.sockaddr();};GX.ptr.prototype.sockaddr=function(){var k,l,m,n,o,p,q,r,s;k=this;if(k.Port<0||k.Port>65535){return[0,0,new GN(22)];}k.raw.Family=10;m=(((l=k.raw,(l.$ptr_Port||(l.$ptr_Port=new QG(function(){return this.$target.Port;},function($v){this.$target.Port=$v;},l))))));m.nilCheck,m[0]=(((k.Port>>8>>0)<<24>>>24));m.nilCheck,m[1]=((k.Port<<24>>>24));k.raw.Scope_id=k.ZoneId;n=0;while(true){if(!(n<16)){break;}(p=k.raw.Addr,((n<0||n>=p.length)?($throwRuntimeError("index out of range"),undefined):p[n]=(o=k.Addr,((n<0||n>=o.length)?($throwRuntimeError("index out of range"),undefined):o[n]))));n=n+(1)>>0;}q=new Uint8Array(28);return[(q),28,$ifaceNil];};GX.prototype.sockaddr=function(){return this.$val.sockaddr();};GY.ptr.prototype.sockaddr=function(){var k,l,m,n,o,p,q,r,s;k=this;l=k.Name;m=l.length;if(m>108){return[0,0,new GN(22)];}if((m===108)&&!((l.charCodeAt(0)===64))){return[0,0,new GN(22)];}k.raw.Family=1;n=0;while(true){if(!(n=o.length)?($throwRuntimeError("index out of range"),undefined):o[n]=((l.charCodeAt(n)<<24>>24))));n=n+(1)>>0;}p=2;if(m>0){p=p+((((m>>>0))+1>>>0))>>>0;}if(k.raw.Path[0]===64){k.raw.Path[0]=0;p=p-(1)>>>0;}q=new Uint8Array(110);return[(q),p,$ifaceNil];};GY.prototype.sockaddr=function(){return this.$val.sockaddr();};EH.ptr.prototype.sockaddr=function(){var k,l,m,n,o,p,q;k=this;if(k.Ifindex<0||k.Ifindex>2147483647){return[0,0,new GN(22)];}k.raw.Family=17;k.raw.Protocol=k.Protocol;k.raw.Ifindex=((k.Ifindex>>0));k.raw.Hatype=k.Hatype;k.raw.Pkttype=k.Pkttype;k.raw.Halen=k.Halen;l=0;while(true){if(!(l<8)){break;}(n=k.raw.Addr,((l<0||l>=n.length)?($throwRuntimeError("index out of range"),undefined):n[l]=(m=k.Addr,((l<0||l>=m.length)?($throwRuntimeError("index out of range"),undefined):m[l]))));l=l+(1)>>0;}o=new Uint8Array(20);return[(o),20,$ifaceNil];};EH.prototype.sockaddr=function(){return this.$val.sockaddr();};EI.ptr.prototype.sockaddr=function(){var k,l,m,n;k=this;k.raw.Family=16;k.raw.Pad=k.Pad;k.raw.Pid=k.Pid;k.raw.Groups=k.Groups;l=new Uint8Array(12);return[(l),12,$ifaceNil];};EI.prototype.sockaddr=function(){return this.$val.sockaddr();};EJ=function(k){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;l=k.Addr.Family;if(l===(16)){p=new Uint8Array(112);s=((m=(p),n=new OB.ptr(0,0,0,0),o=new DataView(m.buffer,m.byteOffset),n.Family=o.getUint16(0,true),n.Pad=o.getUint16(2,true),n.Pid=o.getUint32(4,true),n.Groups=o.getUint32(8,true),n));q=k,r=new DataView(p.buffer,p.byteOffset),q.Addr.Family=r.getUint16(0,true),q.Addr.Data=new($nativeArray($kindInt8))(p.buffer,$min(p.byteOffset+2,p.buffer.byteLength)),q.Pad=new($nativeArray($kindInt8))(p.buffer,$min(p.byteOffset+16,p.buffer.byteLength));t=new EI.ptr(0,0,0,0,new OB.ptr(0,0,0,0));t.Family=s.Family;t.Pad=s.Pad;t.Pid=s.Pid;t.Groups=s.Groups;return[t,$ifaceNil];}else if(l===(17)){x=new Uint8Array(112);aa=((u=(x),v=new OA.ptr(0,0,0,0,0,0,QF.zero()),w=new DataView(u.buffer,u.byteOffset),v.Family=w.getUint16(0,true),v.Protocol=w.getUint16(2,true),v.Ifindex=w.getInt32(4,true),v.Hatype=w.getUint16(8,true),v.Pkttype=w.getUint8(10,true),v.Halen=w.getUint8(11,true),v.Addr=new($nativeArray($kindUint8))(u.buffer,$min(u.byteOffset+12,u.buffer.byteLength)),v));y=k,z=new DataView(x.buffer,x.byteOffset),y.Addr.Family=z.getUint16(0,true),y.Addr.Data=new($nativeArray($kindInt8))(x.buffer,$min(x.byteOffset+2,x.buffer.byteLength)),y.Pad=new($nativeArray($kindInt8))(x.buffer,$min(x.byteOffset+16,x.buffer.byteLength));ab=new EH.ptr(0,0,0,0,0,QF.zero(),new OA.ptr(0,0,0,0,0,0,QF.zero()));ab.Protocol=aa.Protocol;ab.Ifindex=((aa.Ifindex>>0));ab.Hatype=aa.Hatype;ab.Pkttype=aa.Pkttype;ab.Halen=aa.Halen;ac=0;while(true){if(!(ac<8)){break;}(ae=ab.Addr,((ac<0||ac>=ae.length)?($throwRuntimeError("index out of range"),undefined):ae[ac]=(ad=aa.Addr,((ac<0||ac>=ad.length)?($throwRuntimeError("index out of range"),undefined):ad[ac]))));ac=ac+(1)>>0;}return[ab,$ifaceNil];}else if(l===(1)){ai=new Uint8Array(112);al=((af=(ai),ag=new NZ.ptr(0,RA.zero()),ah=new DataView(af.buffer,af.byteOffset),ag.Family=ah.getUint16(0,true),ag.Path=new($nativeArray($kindInt8))(af.buffer,$min(af.byteOffset+2,af.buffer.byteLength)),ag));aj=k,ak=new DataView(ai.buffer,ai.byteOffset),aj.Addr.Family=ak.getUint16(0,true),aj.Addr.Data=new($nativeArray($kindInt8))(ai.buffer,$min(ai.byteOffset+2,ai.buffer.byteLength)),aj.Pad=new($nativeArray($kindInt8))(ai.buffer,$min(ai.byteOffset+16,ai.buffer.byteLength));am=new GY.ptr("",new NZ.ptr(0,RA.zero()));if(al.Path[0]===0){al.Path[0]=64;}an=0;while(true){if(!(an<108&&!(((ao=al.Path,((an<0||an>=ao.length)?($throwRuntimeError("index out of range"),undefined):ao[an]))===0)))){break;}an=an+(1)>>0;}ap=$subslice(new PL((($sliceToArray(new PL(al.Path))))),0,an);am.Name=($bytesToString(ap));return[am,$ifaceNil];}else if(l===(2)){at=new Uint8Array(112);aw=((aq=(at),ar=new NX.ptr(0,0,RB.zero(),QF.zero()),as=new DataView(aq.buffer,aq.byteOffset),ar.Family=as.getUint16(0,true),ar.Port=as.getUint16(2,true),ar.Addr=new($nativeArray($kindUint8))(aq.buffer,$min(aq.byteOffset+4,aq.buffer.byteLength)),ar.Zero=new($nativeArray($kindUint8))(aq.buffer,$min(aq.byteOffset+8,aq.buffer.byteLength)),ar));au=k,av=new DataView(at.buffer,at.byteOffset),au.Addr.Family=av.getUint16(0,true),au.Addr.Data=new($nativeArray($kindInt8))(at.buffer,$min(at.byteOffset+2,at.buffer.byteLength)),au.Pad=new($nativeArray($kindInt8))(at.buffer,$min(at.byteOffset+16,at.buffer.byteLength));ax=new GW.ptr(0,RB.zero(),new NX.ptr(0,0,RB.zero(),QF.zero()));ay=(((aw.$ptr_Port||(aw.$ptr_Port=new QG(function(){return this.$target.Port;},function($v){this.$target.Port=$v;},aw)))));ax.Port=((((ay.nilCheck,ay[0])>>0))<<8>>0)+(((ay.nilCheck,ay[1])>>0))>>0;az=0;while(true){if(!(az<4)){break;}(bb=ax.Addr,((az<0||az>=bb.length)?($throwRuntimeError("index out of range"),undefined):bb[az]=(ba=aw.Addr,((az<0||az>=ba.length)?($throwRuntimeError("index out of range"),undefined):ba[az]))));az=az+(1)>>0;}return[ax,$ifaceNil];}else if(l===(10)){bf=new Uint8Array(112);bi=((bc=(bf),bd=new NY.ptr(0,0,0,QH.zero(),0),be=new DataView(bc.buffer,bc.byteOffset),bd.Family=be.getUint16(0,true),bd.Port=be.getUint16(2,true),bd.Flowinfo=be.getUint32(4,true),bd.Addr=new($nativeArray($kindUint8))(bc.buffer,$min(bc.byteOffset+8,bc.buffer.byteLength)),bd.Scope_id=be.getUint32(24,true),bd));bg=k,bh=new DataView(bf.buffer,bf.byteOffset),bg.Addr.Family=bh.getUint16(0,true),bg.Addr.Data=new($nativeArray($kindInt8))(bf.buffer,$min(bf.byteOffset+2,bf.buffer.byteLength)),bg.Pad=new($nativeArray($kindInt8))(bf.buffer,$min(bf.byteOffset+16,bf.buffer.byteLength));bj=new GX.ptr(0,0,QH.zero(),new NY.ptr(0,0,0,QH.zero(),0));bk=(((bi.$ptr_Port||(bi.$ptr_Port=new QG(function(){return this.$target.Port;},function($v){this.$target.Port=$v;},bi)))));bj.Port=((((bk.nilCheck,bk[0])>>0))<<8>>0)+(((bk.nilCheck,bk[1])>>0))>>0;bj.ZoneId=bi.Scope_id;bl=0;while(true){if(!(bl<16)){break;}(bn=bj.Addr,((bl<0||bl>=bn.length)?($throwRuntimeError("index out of range"),undefined):bn[bl]=(bm=bi.Addr,((bl<0||bl>=bm.length)?($throwRuntimeError("index out of range"),undefined):bm[bl]))));bl=bl+(1)>>0;}return[bj,$ifaceNil];}return[$ifaceNil,new GN(97)];};EK=function(k){var k,l,m,n,o,p,q,r,s;l=0;m=$ifaceNil;n=$ifaceNil;o=new OD.ptr(new OC.ptr(0,RC.zero()),RD.zero());p=112;q=MK(k,o,(r||(r=new RE(function(){return p;},function($v){p=$v;}))));l=q[0];n=q[1];if(!($interfaceIsEqual(n,$ifaceNil))){return[l,m,n];}s=EJ(o);m=s[0];n=s[1];if(!($interfaceIsEqual(n,$ifaceNil))){IR(l);l=0;}return[l,m,n];};$pkg.Accept=EK;EL=function(k,l){var k,l,m,n,o,p,q,r,s,t;m=0;n=$ifaceNil;o=$ifaceNil;p=new OD.ptr(new OC.ptr(0,RC.zero()),RD.zero());q=112;r=ML(k,p,(s||(s=new RE(function(){return q;},function($v){q=$v;}))),l);m=r[0];o=r[1];if(!($interfaceIsEqual(o,$ifaceNil))){return[m,n,o];}if(q>112){$panic(new $String("RawSockaddrAny too small"));}t=EJ(p);n=t[0];o=t[1];if(!($interfaceIsEqual(o,$ifaceNil))){IR(m);m=0;}return[m,n,o];};$pkg.Accept4=EL;EM=function(k){var k,l,m,n,o,p,q;l=$ifaceNil;m=$ifaceNil;n=new OD.ptr(new OC.ptr(0,RC.zero()),RD.zero());o=112;m=MW(k,n,(p||(p=new RE(function(){return o;},function($v){o=$v;}))));if(!($interfaceIsEqual(m,$ifaceNil))){return[l,m];}q=EJ(n);l=q[0];m=q[1];return[l,m];};$pkg.Getsockname=EM;EU=function(k,l,m,n){var k,l,m,n,o,p,q,r;o=$ifaceNil;p=new Uint8Array(12);o=MS(k,l,m,(p),12);q=n,r=new DataView(p.buffer,p.byteOffset),q.Multiaddr=new($nativeArray($kindUint8))(p.buffer,$min(p.byteOffset+0,p.buffer.byteLength)),q.Address=new($nativeArray($kindUint8))(p.buffer,$min(p.byteOffset+4,p.buffer.byteLength)),q.Ifindex=r.getInt32(8,true);return o;};$pkg.SetsockoptIPMreqn=EU;EV=function(k,l,m,n){var aa,ab,ac,ad,ae,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;o=0;p=0;q=0;r=$ifaceNil;s=$ifaceNil;t=new OK.ptr(PV.nil,0,RB.zero(),RG.nil,new $Uint64(0,0),PV.nil,new $Uint64(0,0),0,RB.zero());u=new OD.ptr(new OC.ptr(0,RC.zero()),RD.zero());v=new Uint8Array(112);t.Name=((v));w=u,x=new DataView(v.buffer,v.byteOffset),w.Addr.Family=x.getUint16(0,true),w.Addr.Data=new($nativeArray($kindInt8))(v.buffer,$min(v.byteOffset+2,v.buffer.byteLength)),w.Pad=new($nativeArray($kindInt8))(v.buffer,$min(v.byteOffset+16,v.buffer.byteLength));t.Namelen=112;y=new OG.ptr(PV.nil,new $Uint64(0,0));if(l.$length>0){y.Base=$indexPtr(l.$array,l.$offset+0,PV);y.SetLen(l.$length);}z=0;if(m.$length>0){if(l.$length===0){aa=0;ab=HC(k,1,3);aa=ab[0];s=ab[1];if(!($interfaceIsEqual(s,$ifaceNil))){return[o,p,q,r,s];}if(!((aa===2))){y.Base=(ac||(ac=new PV(function(){return z;},function($v){z=$v;})));y.SetLen(1);}}t.Control=$indexPtr(m.$array,m.$offset+0,PV);t.SetControllen(m.$length);}t.Iov=y;t.Iovlen=new $Uint64(0,1);ad=MZ(k,t,n);o=ad[0];s=ad[1];if(!($interfaceIsEqual(s,$ifaceNil))){return[o,p,q,r,s];}p=((t.Controllen.$low>>0));q=((t.Flags>>0));if(!((u.Addr.Family===0))){ae=EJ(u);r=ae[0];s=ae[1];}return[o,p,q,r,s];};$pkg.Recvmsg=EV;EX=function(k,l,m,n,o){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:p=[p];q=[q];r=[r];s=0;t=$ifaceNil;u=0;v=0;if(!($interfaceIsEqual(n,$ifaceNil))){$s=1;continue;}$s=2;continue;case 1:w=$ifaceNil;y=n.sockaddr();$s=3;case 3:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}x=y;u=x[0];v=x[1];w=x[2];if(!($interfaceIsEqual(w,$ifaceNil))){z=0;aa=w;s=z;t=aa;$s=-1;return[s,t];}case 2:r[0]=new OK.ptr(PV.nil,0,RB.zero(),RG.nil,new $Uint64(0,0),PV.nil,new $Uint64(0,0),0,RB.zero());r[0].Name=(u);r[0].Namelen=((v>>>0));q[0]=new OG.ptr(PV.nil,new $Uint64(0,0));if(l.$length>0){q[0].Base=$indexPtr(l.$array,l.$offset+0,PV);q[0].SetLen(l.$length);}p[0]=0;if(m.$length>0){if(l.$length===0){ab=0;ac=HC(k,1,3);ab=ac[0];t=ac[1];if(!($interfaceIsEqual(t,$ifaceNil))){ad=0;ae=t;s=ad;t=ae;$s=-1;return[s,t];}if(!((ab===2))){q[0].Base=(p.$ptr||(p.$ptr=new PV(function(){return this.$target[0];},function($v){this.$target[0]=$v;},p)));q[0].SetLen(1);}}r[0].Control=$indexPtr(m.$array,m.$offset+0,PV);r[0].SetControllen(m.$length);}r[0].Iov=q[0];r[0].Iovlen=new $Uint64(0,1);af=NA(k,r[0],o);s=af[0];t=af[1];if(!($interfaceIsEqual(t,$ifaceNil))){ag=0;ah=t;s=ag;t=ah;$s=-1;return[s,t];}if(m.$length>0&&(l.$length===0)){s=0;}ai=s;aj=$ifaceNil;s=ai;t=aj;$s=-1;return[s,t];}return;}if($f===undefined){$f={$blk:EX};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};$pkg.SendmsgN=EX;FP=function(k,l){var k,l,m,n,o;m=0;n=$ifaceNil;o=JF(k,l);m=o[0];n=o[1];return[m,n];};$pkg.ReadDirent=FP;FQ=function(k){var k;return Z(k,0,8);};FR=function(k){var k;return Z(k,16,2);};FS=function(k){var k,l,m,n;l=FR(k);m=l[0];n=l[1];if(!n){return[new $Uint64(0,0),false];}return[new $Uint64(m.$high-0,m.$low-19),true];};GA=function(k,l){var k,l,m;m=$ifaceNil;m=MO(-100,k,l,0);return m;};$pkg.Stat=GA;GC=function(k,l){var k,l,m;m=$ifaceNil;m=MO(-100,k,l,256);return m;};$pkg.Lstat=GC;OG.ptr.prototype.SetLen=function(k){var k,l;l=this;l.Len=(new $Uint64(0,k));};OG.prototype.SetLen=function(k){return this.$val.SetLen(k);};OK.ptr.prototype.SetControllen=function(k){var k,l;l=this;l.Controllen=(new $Uint64(0,k));};OK.prototype.SetControllen=function(k){return this.$val.SetControllen(k);};GM.ptr.prototype.Mmap=function(k,l,m,n,o){var aa,ab,ac,ad,ae,af,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);p=[p];q=PL.nil;r=$ifaceNil;s=this;if(m<=0){t=PL.nil;u=new GN(22);q=t;r=u;$s=-1;return[q,r];}w=s.mmap(0,((m>>>0)),n,o,k,l);$s=1;case 1:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}v=w;x=v[0];y=v[1];if(!($interfaceIsEqual(y,$ifaceNil))){z=PL.nil;aa=y;q=z;r=aa;$s=-1;return[q,r];}p[0]=new RK.ptr(x,m,m);ab=p[0];ac=$indexPtr(ab.$array,ab.$offset+(ab.$capacity-1>>0),PV);$r=s.Mutex.Lock();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$deferred.push([$methodVal(s.Mutex,"Unlock"),[]]);ad=ac;(s.active||$throwRuntimeError("assignment to entry in nil map"))[PV.keyFor(ad)]={k:ad,v:ab};ae=ab;af=$ifaceNil;q=ae;r=af;$s=-1;return[q,r];}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if(!$curGoroutine.asleep){return[q,r];}if($curGoroutine.asleep){if($f===undefined){$f={$blk:GM.ptr.prototype.Mmap};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};GM.prototype.Mmap=function(k,l,m,n,o){return this.$val.Mmap(k,l,m,n,o);};GM.ptr.prototype.Munmap=function(k){var k,l,m,n,o,p,q,r,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);l=$ifaceNil;m=this;if((k.$length===0)||!((k.$length===k.$capacity))){l=new GN(22);$s=-1;return l;}n=$indexPtr(k.$array,k.$offset+(k.$capacity-1>>0),PV);$r=m.Mutex.Lock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$deferred.push([$methodVal(m.Mutex,"Unlock"),[]]);p=(o=m.active[PV.keyFor(n)],o!==undefined?o.v:PL.nil);if(p===PL.nil||!($indexPtr(p.$array,p.$offset+0,PV)===$indexPtr(k.$array,k.$offset+0,PV))){l=new GN(22);$s=-1;return l;}q=m.munmap((($sliceToArray(p))),((p.$length>>>0)));$s=2;case 2:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}r=q;if(!($interfaceIsEqual(r,$ifaceNil))){l=r;$s=-1;return l;}delete m.active[PV.keyFor(n)];l=$ifaceNil;$s=-1;return l;}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if(!$curGoroutine.asleep){return l;}if($curGoroutine.asleep){if($f===undefined){$f={$blk:GM.ptr.prototype.Munmap};}$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};GM.prototype.Munmap=function(k){return this.$val.Munmap(k);};GN.prototype.Error=function(){var k,l;k=this.$val;if(0<=((k>>0))&&((k>>0))<133){l=((k<0||k>=HW.length)?($throwRuntimeError("index out of range"),undefined):HW[k]);if(!(l==="")){return l;}}return"errno "+CZ(((k>>0)));};$ptrType(GN).prototype.Error=function(){return new GN(this.$get()).Error();};GN.prototype.Temporary=function(){var k;k=this.$val;return(k===4)||(k===24)||new GN(k).Timeout();};$ptrType(GN).prototype.Temporary=function(){return new GN(this.$get()).Temporary();};GN.prototype.Timeout=function(){var k;k=this.$val;return(k===11)||(k===11)||(k===110);};$ptrType(GN).prototype.Timeout=function(){return new GN(this.$get()).Timeout();};GR=function(k){var k,l;l=k;if(l===(0)){return $ifaceNil;}else if(l===(11)){return GO;}else if(l===(22)){return GP;}else if(l===(2)){return GQ;}return new GN(k);};GT=function(k,l){var k,l,m,n,o;m=0;n=$ifaceNil;o=JZ(k,l);m=o[0];n=o[1];if(false){if(m>0){D.WriteRange(($sliceToArray(l)),m);}if($interfaceIsEqual(n,$ifaceNil)){D.Acquire(((PU||(PU=new RL(function(){return HR;},function($v){HR=$v;})))));}}if(false&&m>0){CA(($sliceToArray(l)),m);}return[m,n];};$pkg.Read=GT;GU=function(k,l){var k,l,m,n,o;m=0;n=$ifaceNil;if(false){D.ReleaseMerge(((PU||(PU=new RL(function(){return HR;},function($v){HR=$v;})))));}o=KU(k,l);m=o[0];n=o[1];if(false&&m>0){D.ReadRange(($sliceToArray(l)),m);}if(false&&m>0){BZ(($sliceToArray(l)),m);}return[m,n];};$pkg.Write=GU;GZ=function(k,l){var k,l,m,n,o,p,q,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:m=$ifaceNil;o=l.sockaddr();$s=1;case 1:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;p=n[0];q=n[1];m=n[2];if(!($interfaceIsEqual(m,$ifaceNil))){m=m;$s=-1;return m;}m=MM(k,p,q);$s=-1;return m;}return;}if($f===undefined){$f={$blk:GZ};}$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Bind=GZ;HC=function(k,l,m){var k,l,m,n,o,p,q,r,s,t,u;n=0;o=$ifaceNil;p=0;q=4;o=MR(k,l,m,((r||(r=new PZ(function(){return p;},function($v){p=$v;})))),(s||(s=new RE(function(){return q;},function($v){q=$v;}))));t=((p>>0));u=o;n=t;o=u;return[n,o];};$pkg.GetsockoptInt=HC;HD=function(k,l,m){var k,l,m,n,o,p,q,r,s,t,u;n=0;o=$ifaceNil;p=$ifaceNil;q=new OD.ptr(new OC.ptr(0,RC.zero()),RD.zero());r=112;s=MX(k,l,m,q,(t||(t=new RE(function(){return r;},function($v){r=$v;}))));n=s[0];p=s[1];if(!($interfaceIsEqual(p,$ifaceNil))){return[n,o,p];}if(!((q.Addr.Family===0))){u=EJ(q);o=u[0];p=u[1];}return[n,o,p];};$pkg.Recvfrom=HD;HE=function(k,l,m,n){var k,l,m,n,o,p,q,r,s,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:o=$ifaceNil;q=n.sockaddr();$s=1;case 1:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}p=q;r=p[0];s=p[1];o=p[2];if(!($interfaceIsEqual(o,$ifaceNil))){o=o;$s=-1;return o;}o=MY(k,l,m,r,s);$s=-1;return o;}return;}if($f===undefined){$f={$blk:HE};}$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Sendto=HE;HF=function(k,l,m,n){var k,l,m,n,o,p;o=$ifaceNil;o=MS(k,l,m,((p||(p=new PV(function(){return n;},function($v){n=$v;})))),1);return o;};$pkg.SetsockoptByte=HF;HG=function(k,l,m,n){var k,l,m,n,o,p,q;o=$ifaceNil;p=((n>>0));o=MS(k,l,m,((q||(q=new PZ(function(){return p;},function($v){p=$v;})))),4);return o;};$pkg.SetsockoptInt=HG;HH=function(k,l,m,n){var k,l,m,n,o;o=$ifaceNil;o=MS(k,l,m,($sliceToArray(new PL(n))),4);return o;};$pkg.SetsockoptInet4Addr=HH;HI=function(k,l,m,n){var k,l,m,n,o,p,q,r;o=$ifaceNil;p=new Uint8Array(8);o=MS(k,l,m,(p),8);q=n,r=new DataView(p.buffer,p.byteOffset),q.Multiaddr=new($nativeArray($kindUint8))(p.buffer,$min(p.byteOffset+0,p.buffer.byteLength)),q.Interface=new($nativeArray($kindUint8))(p.buffer,$min(p.byteOffset+4,p.buffer.byteLength));return o;};$pkg.SetsockoptIPMreq=HI;HJ=function(k,l,m,n){var k,l,m,n,o,p,q,r;o=$ifaceNil;p=new Uint8Array(20);o=MS(k,l,m,(p),20);q=n,r=new DataView(p.buffer,p.byteOffset),q.Multiaddr=new($nativeArray($kindUint8))(p.buffer,$min(p.byteOffset+0,p.buffer.byteLength)),q.Interface=r.getUint32(16,true);return o;};$pkg.SetsockoptIPv6Mreq=HJ;HL=function(k,l,m,n){var k,l,m,n,o,p,q,r;o=$ifaceNil;p=new Uint8Array(8);o=MS(k,l,m,(p),8);q=n,r=new DataView(p.buffer,p.byteOffset),q.Onoff=r.getInt32(0,true),q.Linger=r.getInt32(4,true);return o;};$pkg.SetsockoptLinger=HL;HO=function(k,l,m){var k,l,m,n,o,p,q,r;n=0;o=$ifaceNil;if((k===10)&&$pkg.SocketDisableIPv6){p=-1;q=new GN(97);n=p;o=q;return[n,o];}r=MT(k,l,m);n=r[0];o=r[1];return[n,o];};$pkg.Socket=HO;HZ=function(k,l,m){var k,l,m,n,o,p,q,r;n=$ifaceNil;o=PV.nil;p=Y(l);o=p[0];n=p[1];if(!($interfaceIsEqual(n,$ifaceNil))){return n;}q=T(268,((k>>>0)),((o)),((m>>>0)));r=q[2];if(!((r===0))){n=GR(r);}return n;};IB=function(k,l,m,n){var k,l,m,n,o,p,q,r,s,t,u;o=0;p=$ifaceNil;q=PV.nil;r=Y(l);q=r[0];p=r[1];if(!($interfaceIsEqual(p,$ifaceNil))){return[o,p];}s=U(257,((k>>>0)),((q)),((m>>>0)),((n>>>0)),0,0);t=s[0];u=s[2];o=((t>>0));if(!((u===0))){p=GR(u);}return[o,p];};IR=function(k){var k,l,m,n;l=$ifaceNil;m=T(3,((k>>>0)),0,0);n=m[2];if(!((n===0))){l=GR(n);}return l;};$pkg.Close=IR;IS=function(k){var k,l,m,n,o,p;l=0;m=$ifaceNil;n=T(32,((k>>>0)),0,0);o=n[0];p=n[2];l=((o>>0));if(!((p===0))){m=GR(p);}return[l,m];};$pkg.Dup=IS;IY=function(k){var k,l,m,n;l=$ifaceNil;m=T(81,((k>>>0)),0,0);n=m[2];if(!((n===0))){l=GR(n);}return l;};$pkg.Fchdir=IY;IZ=function(k,l){var k,l,m,n,o;m=$ifaceNil;n=T(91,((k>>>0)),((l>>>0)),0);o=n[2];if(!((o===0))){m=GR(o);}return m;};$pkg.Fchmod=IZ;JB=function(k,l,m){var k,l,m,n,o,p,q,r;n=0;o=$ifaceNil;p=T(72,((k>>>0)),((l>>>0)),((m>>>0)));q=p[0];r=p[2];n=((q>>0));if(!((r===0))){o=GR(r);}return[n,o];};JE=function(k){var k,l,m,n;l=$ifaceNil;m=T(74,((k>>>0)),0,0);n=m[2];if(!((n===0))){l=GR(n);}return l;};$pkg.Fsync=JE;JF=function(k,l){var k,l,m,n,o,p,q,r;m=0;n=$ifaceNil;o=0;if(l.$length>0){o=($sliceToArray(l));}else{o=(new Uint8Array(0));}p=T(217,((k>>>0)),(o),((l.$length>>>0)));q=p[0];r=p[2];m=((q>>0));if(!((r===0))){n=GR(r);}return[m,n];};$pkg.Getdents=JF;JZ=function(k,l){var k,l,m,n,o,p,q,r;m=0;n=$ifaceNil;o=0;if(l.$length>0){o=($sliceToArray(l));}else{o=(new Uint8Array(0));}p=T(0,((k>>>0)),(o),((l.$length>>>0)));q=p[0];r=p[2];m=((q>>0));if(!((r===0))){n=GR(r);}return[m,n];};KU=function(k,l){var k,l,m,n,o,p,q,r;m=0;n=$ifaceNil;o=0;if(l.$length>0){o=($sliceToArray(l));}else{o=(new Uint8Array(0));}p=T(1,((k>>>0)),(o),((l.$length>>>0)));q=p[0];r=p[2];m=((q>>0));if(!((r===0))){n=GR(r);}return[m,n];};KY=function(k,l){var k,l,m,n,o;m=$ifaceNil;n=T(11,(k),(l),0);o=n[2];if(!((o===0))){m=GR(o);}return m;};LG=function(k,l,m){var k,l,m,n,o,p;n=$ifaceNil;o=T(93,((k>>>0)),((l>>>0)),((m>>>0)));p=o[2];if(!((p===0))){n=GR(p);}return n;};$pkg.Fchown=LG;LH=function(k,l){var k,l,m,n,o,p,q,r;m=$ifaceNil;o=new Uint8Array(144);n=T(5,((k>>>0)),((o)),0);p=l,q=new DataView(o.buffer,o.byteOffset),p.Dev=new $Uint64(q.getUint32(4,true),q.getUint32(0,true)),p.Ino=new $Uint64(q.getUint32(12,true),q.getUint32(8,true)),p.Nlink=new $Uint64(q.getUint32(20,true),q.getUint32(16,true)),p.Mode=q.getUint32(24,true),p.Uid=q.getUint32(28,true),p.Gid=q.getUint32(32,true),p.X__pad0=q.getInt32(36,true),p.Rdev=new $Uint64(q.getUint32(44,true),q.getUint32(40,true)),p.Size=new $Int64(q.getUint32(52,true),q.getUint32(48,true)),p.Blksize=new $Int64(q.getUint32(60,true),q.getUint32(56,true)),p.Blocks=new $Int64(q.getUint32(68,true),q.getUint32(64,true)),p.Atim.Sec=new $Int64(q.getUint32(76,true),q.getUint32(72,true)),p.Atim.Nsec=new $Int64(q.getUint32(84,true),q.getUint32(80,true)),p.Mtim.Sec=new $Int64(q.getUint32(92,true),q.getUint32(88,true)),p.Mtim.Nsec=new $Int64(q.getUint32(100,true),q.getUint32(96,true)),p.Ctim.Sec=new $Int64(q.getUint32(108,true),q.getUint32(104,true)),p.Ctim.Nsec=new $Int64(q.getUint32(116,true),q.getUint32(112,true)),p.X__unused=new($nativeArray($kindInt64))(o.buffer,$min(o.byteOffset+120,o.buffer.byteLength));r=n[2];if(!((r===0))){m=GR(r);}return m;};$pkg.Fstat=LH;LJ=function(k,l){var k,l,m,n,o;m=$ifaceNil;n=T(77,((k>>>0)),((l.$low>>>0)),0);o=n[2];if(!((o===0))){m=GR(o);}return m;};$pkg.Ftruncate=LJ;LT=function(k,l,m){var k,l,m,n,o,p,q,r,s;n=0;o=$ifaceNil;p=0;if(l.$length>0){p=($sliceToArray(l));}else{p=(new Uint8Array(0));}q=U(17,((k>>>0)),(p),((l.$length>>>0)),((m.$low>>>0)),0,0);r=q[0];s=q[2];n=((r>>0));if(!((s===0))){o=GR(s);}return[n,o];};$pkg.Pread=LT;LU=function(k,l,m){var k,l,m,n,o,p,q,r,s;n=0;o=$ifaceNil;p=0;if(l.$length>0){p=($sliceToArray(l));}else{p=(new Uint8Array(0));}q=U(18,((k>>>0)),(p),((l.$length>>>0)),((m.$low>>>0)),0,0);r=q[0];s=q[2];n=((r>>0));if(!((s===0))){o=GR(s);}return[n,o];};$pkg.Pwrite=LU;LV=function(k,l,m){var k,l,m,n,o,p,q,r;n=new $Int64(0,0);o=$ifaceNil;p=T(8,((k>>>0)),((l.$low>>>0)),((m>>>0)));q=p[0];r=p[2];n=(new $Int64(0,q.constructor===Number?q:1));if(!((r===0))){o=GR(r);}return[n,o];};$pkg.Seek=LV;MF=function(k,l){var k,l,m,n,o;m=$ifaceNil;n=T(48,((k>>>0)),((l>>>0)),0);o=n[2];if(!((o===0))){m=GR(o);}return m;};$pkg.Shutdown=MF;MK=function(k,l,m){var k,l,m,n,o,p,q,r,s,t,u;n=0;o=$ifaceNil;q=new Uint8Array(112);p=T(43,((k>>>0)),((q)),((m)));r=l,s=new DataView(q.buffer,q.byteOffset),r.Addr.Family=s.getUint16(0,true),r.Addr.Data=new($nativeArray($kindInt8))(q.buffer,$min(q.byteOffset+2,q.buffer.byteLength)),r.Pad=new($nativeArray($kindInt8))(q.buffer,$min(q.byteOffset+16,q.buffer.byteLength));t=p[0];u=p[2];n=((t>>0));if(!((u===0))){o=GR(u);}return[n,o];};ML=function(k,l,m,n){var k,l,m,n,o,p,q,r,s,t,u,v;o=0;p=$ifaceNil;r=new Uint8Array(112);q=U(288,((k>>>0)),((r)),((m)),((n>>>0)),0,0);s=l,t=new DataView(r.buffer,r.byteOffset),s.Addr.Family=t.getUint16(0,true),s.Addr.Data=new($nativeArray($kindInt8))(r.buffer,$min(r.byteOffset+2,r.buffer.byteLength)),s.Pad=new($nativeArray($kindInt8))(r.buffer,$min(r.byteOffset+16,r.buffer.byteLength));u=q[0];v=q[2];o=((u>>0));if(!((v===0))){p=GR(v);}return[o,p];};MM=function(k,l,m){var k,l,m,n,o,p;n=$ifaceNil;o=T(49,((k>>>0)),(l),((m>>>0)));p=o[2];if(!((p===0))){n=GR(p);}return n;};MO=function(k,l,m,n){var k,l,m,n,o,p,q,r,s,t,u,v;o=$ifaceNil;p=PV.nil;q=Y(l);p=q[0];o=q[1];if(!($interfaceIsEqual(o,$ifaceNil))){return o;}s=new Uint8Array(144);r=U(262,((k>>>0)),((p)),((s)),((n>>>0)),0,0);t=m,u=new DataView(s.buffer,s.byteOffset),t.Dev=new $Uint64(u.getUint32(4,true),u.getUint32(0,true)),t.Ino=new $Uint64(u.getUint32(12,true),u.getUint32(8,true)),t.Nlink=new $Uint64(u.getUint32(20,true),u.getUint32(16,true)),t.Mode=u.getUint32(24,true),t.Uid=u.getUint32(28,true),t.Gid=u.getUint32(32,true),t.X__pad0=u.getInt32(36,true),t.Rdev=new $Uint64(u.getUint32(44,true),u.getUint32(40,true)),t.Size=new $Int64(u.getUint32(52,true),u.getUint32(48,true)),t.Blksize=new $Int64(u.getUint32(60,true),u.getUint32(56,true)),t.Blocks=new $Int64(u.getUint32(68,true),u.getUint32(64,true)),t.Atim.Sec=new $Int64(u.getUint32(76,true),u.getUint32(72,true)),t.Atim.Nsec=new $Int64(u.getUint32(84,true),u.getUint32(80,true)),t.Mtim.Sec=new $Int64(u.getUint32(92,true),u.getUint32(88,true)),t.Mtim.Nsec=new $Int64(u.getUint32(100,true),u.getUint32(96,true)),t.Ctim.Sec=new $Int64(u.getUint32(108,true),u.getUint32(104,true)),t.Ctim.Nsec=new $Int64(u.getUint32(116,true),u.getUint32(112,true)),t.X__unused=new($nativeArray($kindInt64))(s.buffer,$min(s.byteOffset+120,s.buffer.byteLength));v=r[2];if(!((v===0))){o=GR(v);}return o;};MR=function(k,l,m,n,o){var k,l,m,n,o,p,q,r;p=$ifaceNil;q=U(55,((k>>>0)),((l>>>0)),((m>>>0)),(n),((o)),0);r=q[2];if(!((r===0))){p=GR(r);}return p;};MS=function(k,l,m,n,o){var k,l,m,n,o,p,q,r;p=$ifaceNil;q=U(54,((k>>>0)),((l>>>0)),((m>>>0)),(n),(o),0);r=q[2];if(!((r===0))){p=GR(r);}return p;};MT=function(k,l,m){var k,l,m,n,o,p,q,r;n=0;o=$ifaceNil;p=V(41,((k>>>0)),((l>>>0)),((m>>>0)));q=p[0];r=p[2];n=((q>>0));if(!((r===0))){o=GR(r);}return[n,o];};MW=function(k,l,m){var k,l,m,n,o,p,q,r,s;n=$ifaceNil;p=new Uint8Array(112);o=V(51,((k>>>0)),((p)),((m)));q=l,r=new DataView(p.buffer,p.byteOffset),q.Addr.Family=r.getUint16(0,true),q.Addr.Data=new($nativeArray($kindInt8))(p.buffer,$min(p.byteOffset+2,p.buffer.byteLength)),q.Pad=new($nativeArray($kindInt8))(p.buffer,$min(p.byteOffset+16,p.buffer.byteLength));s=o[2];if(!((s===0))){n=GR(s);}return n;};MX=function(k,l,m,n,o){var k,l,m,n,o,p,q,r,s,t,u,v,w,x;p=0;q=$ifaceNil;r=0;if(l.$length>0){r=($sliceToArray(l));}else{r=(new Uint8Array(0));}t=new Uint8Array(112);s=U(45,((k>>>0)),(r),((l.$length>>>0)),((m>>>0)),((t)),((o)));u=n,v=new DataView(t.buffer,t.byteOffset),u.Addr.Family=v.getUint16(0,true),u.Addr.Data=new($nativeArray($kindInt8))(t.buffer,$min(t.byteOffset+2,t.buffer.byteLength)),u.Pad=new($nativeArray($kindInt8))(t.buffer,$min(t.byteOffset+16,t.buffer.byteLength));w=s[0];x=s[2];p=((w>>0));if(!((x===0))){q=GR(x);}return[p,q];};MY=function(k,l,m,n,o){var k,l,m,n,o,p,q,r,s;p=$ifaceNil;q=0;if(l.$length>0){q=($sliceToArray(l));}else{q=(new Uint8Array(0));}r=U(44,((k>>>0)),(q),((l.$length>>>0)),((m>>>0)),(n),((o>>>0)));s=r[2];if(!((s===0))){p=GR(s);}return p;};MZ=function(k,l,m){var k,l,m,n,o,p,q,r,s,t,u;n=0;o=$ifaceNil;q=new Uint8Array(48);p=T(47,((k>>>0)),((q)),((m>>>0)));r=l,s=new DataView(q.buffer,q.byteOffset),r.Namelen=s.getUint32(4,true),r.Pad_cgo_0=new($nativeArray($kindUint8))(q.buffer,$min(q.byteOffset+8,q.buffer.byteLength)),r.Iovlen=new $Uint64(s.getUint32(20,true),s.getUint32(16,true)),r.Controllen=new $Uint64(s.getUint32(36,true),s.getUint32(32,true)),r.Flags=s.getInt32(40,true),r.Pad_cgo_1=new($nativeArray($kindUint8))(q.buffer,$min(q.byteOffset+44,q.buffer.byteLength));t=p[0];u=p[2];n=((t>>0));if(!((u===0))){o=GR(u);}return[n,o];};NA=function(k,l,m){var k,l,m,n,o,p,q,r,s,t,u;n=0;o=$ifaceNil;q=new Uint8Array(48);p=T(46,((k>>>0)),((q)),((m>>>0)));r=l,s=new DataView(q.buffer,q.byteOffset),r.Namelen=s.getUint32(4,true),r.Pad_cgo_0=new($nativeArray($kindUint8))(q.buffer,$min(q.byteOffset+8,q.buffer.byteLength)),r.Iovlen=new $Uint64(s.getUint32(20,true),s.getUint32(16,true)),r.Controllen=new $Uint64(s.getUint32(36,true),s.getUint32(32,true)),r.Flags=s.getInt32(40,true),r.Pad_cgo_1=new($nativeArray($kindUint8))(q.buffer,$min(q.byteOffset+44,q.buffer.byteLength));t=p[0];u=p[2];n=((t>>0));if(!((u===0))){o=GR(u);}return[n,o];};NB=function(k,l,m,n,o,p){var k,l,m,n,o,p,q,r,s,t,u;q=0;r=$ifaceNil;s=U(9,(k),(l),((m>>>0)),((n>>>0)),((o>>>0)),((p.$low>>>0)));t=s[0];u=s[2];q=(t);if(!((u===0))){r=GR(u);}return[q,r];};RQ.methods=[{prop:"toWireFormat",name:"toWireFormat",pkg:"syscall",typ:$funcType([],[PL],false)}];RR.methods=[{prop:"sockaddr",name:"sockaddr",pkg:"syscall",typ:$funcType([],[$UnsafePointer,OE,$error],false)}];QL.methods=[{prop:"sockaddr",name:"sockaddr",pkg:"syscall",typ:$funcType([],[$UnsafePointer,OE,$error],false)}];RS.methods=[{prop:"Mmap",name:"Mmap",pkg:"",typ:$funcType([$Int,$Int64,$Int,$Int,$Int],[PL,$error],false)},{prop:"Munmap",name:"Munmap",pkg:"",typ:$funcType([PL],[$error],false)}];GN.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Temporary",name:"Temporary",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)}];RW.methods=[{prop:"sockaddr",name:"sockaddr",pkg:"syscall",typ:$funcType([],[$UnsafePointer,OE,$error],false)}];RX.methods=[{prop:"sockaddr",name:"sockaddr",pkg:"syscall",typ:$funcType([],[$UnsafePointer,OE,$error],false)}];RY.methods=[{prop:"sockaddr",name:"sockaddr",pkg:"syscall",typ:$funcType([],[$UnsafePointer,OE,$error],false)}];RZ.methods=[{prop:"Unix",name:"Unix",pkg:"",typ:$funcType([],[$Int64,$Int64],false)},{prop:"Nano",name:"Nano",pkg:"",typ:$funcType([],[$Int64],false)}];RG.methods=[{prop:"SetLen",name:"SetLen",pkg:"",typ:$funcType([$Int],[],false)}];SF.methods=[{prop:"SetControllen",name:"SetControllen",pkg:"",typ:$funcType([$Int],[],false)}];CB.init([{prop:"Control",name:"Control",pkg:"",typ:$funcType([RO],[$error],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([RP],[$error],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([RP],[$error],false)}]);CF.init("",[{prop:"Header",name:"Header",embedded:false,exported:true,typ:OS,tag:""},{prop:"Data",name:"Data",embedded:false,exported:true,typ:OU,tag:""}]);CI.init("",[{prop:"Header",name:"Header",embedded:false,exported:true,typ:OS,tag:""},{prop:"Data",name:"Data",embedded:false,exported:true,typ:PL,tag:""}]);CL.init("",[{prop:"Attr",name:"Attr",embedded:false,exported:true,typ:OW,tag:""},{prop:"Value",name:"Value",embedded:false,exported:true,typ:PL,tag:""}]);EH.init("syscall",[{prop:"Protocol",name:"Protocol",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Ifindex",name:"Ifindex",embedded:false,exported:true,typ:$Int,tag:""},{prop:"Hatype",name:"Hatype",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Pkttype",name:"Pkttype",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Halen",name:"Halen",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:QF,tag:""},{prop:"raw",name:"raw",embedded:false,exported:false,typ:OA,tag:""}]);EI.init("syscall",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Pad",name:"Pad",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Pid",name:"Pid",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Groups",name:"Groups",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"raw",name:"raw",embedded:false,exported:false,typ:OB,tag:""}]);GM.init("syscall",[{prop:"Mutex",name:"Mutex",embedded:true,exported:true,typ:C.Mutex,tag:""},{prop:"active",name:"active",embedded:false,exported:false,typ:RT,tag:""},{prop:"mmap",name:"mmap",embedded:false,exported:false,typ:RU,tag:""},{prop:"munmap",name:"munmap",embedded:false,exported:false,typ:RV,tag:""}]);GV.init([{prop:"sockaddr",name:"sockaddr",pkg:"syscall",typ:$funcType([],[$UnsafePointer,OE,$error],false)}]);GW.init("syscall",[{prop:"Port",name:"Port",embedded:false,exported:true,typ:$Int,tag:""},{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:RB,tag:""},{prop:"raw",name:"raw",embedded:false,exported:false,typ:NX,tag:""}]);GX.init("syscall",[{prop:"Port",name:"Port",embedded:false,exported:true,typ:$Int,tag:""},{prop:"ZoneId",name:"ZoneId",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:QH,tag:""},{prop:"raw",name:"raw",embedded:false,exported:false,typ:NY,tag:""}]);GY.init("syscall",[{prop:"Name",name:"Name",embedded:false,exported:true,typ:$String,tag:""},{prop:"raw",name:"raw",embedded:false,exported:false,typ:NZ,tag:""}]);NJ.init("",[{prop:"Sec",name:"Sec",embedded:false,exported:true,typ:$Int64,tag:""},{prop:"Nsec",name:"Nsec",embedded:false,exported:true,typ:$Int64,tag:""}]);NS.init("",[{prop:"Dev",name:"Dev",embedded:false,exported:true,typ:$Uint64,tag:""},{prop:"Ino",name:"Ino",embedded:false,exported:true,typ:$Uint64,tag:""},{prop:"Nlink",name:"Nlink",embedded:false,exported:true,typ:$Uint64,tag:""},{prop:"Mode",name:"Mode",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Uid",name:"Uid",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Gid",name:"Gid",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"X__pad0",name:"X__pad0",embedded:false,exported:true,typ:$Int32,tag:""},{prop:"Rdev",name:"Rdev",embedded:false,exported:true,typ:$Uint64,tag:""},{prop:"Size",name:"Size",embedded:false,exported:true,typ:$Int64,tag:""},{prop:"Blksize",name:"Blksize",embedded:false,exported:true,typ:$Int64,tag:""},{prop:"Blocks",name:"Blocks",embedded:false,exported:true,typ:$Int64,tag:""},{prop:"Atim",name:"Atim",embedded:false,exported:true,typ:NJ,tag:""},{prop:"Mtim",name:"Mtim",embedded:false,exported:true,typ:NJ,tag:""},{prop:"Ctim",name:"Ctim",embedded:false,exported:true,typ:NJ,tag:""},{prop:"X__unused",name:"X__unused",embedded:false,exported:true,typ:QU,tag:""}]);NX.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Port",name:"Port",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:RB,tag:""},{prop:"Zero",name:"Zero",embedded:false,exported:true,typ:QF,tag:""}]);NY.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Port",name:"Port",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Flowinfo",name:"Flowinfo",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:QH,tag:""},{prop:"Scope_id",name:"Scope_id",embedded:false,exported:true,typ:$Uint32,tag:""}]);NZ.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Path",name:"Path",embedded:false,exported:true,typ:RA,tag:""}]);OA.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Protocol",name:"Protocol",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Ifindex",name:"Ifindex",embedded:false,exported:true,typ:$Int32,tag:""},{prop:"Hatype",name:"Hatype",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Pkttype",name:"Pkttype",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Halen",name:"Halen",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:QF,tag:""}]);OB.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Pad",name:"Pad",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Pid",name:"Pid",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Groups",name:"Groups",embedded:false,exported:true,typ:$Uint32,tag:""}]);OC.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Data",name:"Data",embedded:false,exported:true,typ:RC,tag:""}]);OD.init("",[{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:OC,tag:""},{prop:"Pad",name:"Pad",embedded:false,exported:true,typ:RD,tag:""}]);OF.init("",[{prop:"Onoff",name:"Onoff",embedded:false,exported:true,typ:$Int32,tag:""},{prop:"Linger",name:"Linger",embedded:false,exported:true,typ:$Int32,tag:""}]);OG.init("",[{prop:"Base",name:"Base",embedded:false,exported:true,typ:PV,tag:""},{prop:"Len",name:"Len",embedded:false,exported:true,typ:$Uint64,tag:""}]);OH.init("",[{prop:"Multiaddr",name:"Multiaddr",embedded:false,exported:true,typ:RB,tag:""},{prop:"Interface",name:"Interface",embedded:false,exported:true,typ:RB,tag:""}]);OI.init("",[{prop:"Multiaddr",name:"Multiaddr",embedded:false,exported:true,typ:RB,tag:""},{prop:"Address",name:"Address",embedded:false,exported:true,typ:RB,tag:""},{prop:"Ifindex",name:"Ifindex",embedded:false,exported:true,typ:$Int32,tag:""}]);OJ.init("",[{prop:"Multiaddr",name:"Multiaddr",embedded:false,exported:true,typ:QH,tag:""},{prop:"Interface",name:"Interface",embedded:false,exported:true,typ:$Uint32,tag:""}]);OK.init("",[{prop:"Name",name:"Name",embedded:false,exported:true,typ:PV,tag:""},{prop:"Namelen",name:"Namelen",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Pad_cgo_0",name:"Pad_cgo_0",embedded:false,exported:true,typ:RB,tag:""},{prop:"Iov",name:"Iov",embedded:false,exported:true,typ:RG,tag:""},{prop:"Iovlen",name:"Iovlen",embedded:false,exported:true,typ:$Uint64,tag:""},{prop:"Control",name:"Control",embedded:false,exported:true,typ:PV,tag:""},{prop:"Controllen",name:"Controllen",embedded:false,exported:true,typ:$Uint64,tag:""},{prop:"Flags",name:"Flags",embedded:false,exported:true,typ:$Int32,tag:""},{prop:"Pad_cgo_1",name:"Pad_cgo_1",embedded:false,exported:true,typ:RB,tag:""}]);OS.init("",[{prop:"Len",name:"Len",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Type",name:"Type",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Flags",name:"Flags",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Seq",name:"Seq",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Pid",name:"Pid",embedded:false,exported:true,typ:$Uint32,tag:""}]);OU.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint8,tag:""}]);OW.init("",[{prop:"Len",name:"Len",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Type",name:"Type",embedded:false,exported:true,typ:$Uint16,tag:""}]);OX.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"X__ifi_pad",name:"X__ifi_pad",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Type",name:"Type",embedded:false,exported:true,typ:$Uint16,tag:""},{prop:"Index",name:"Index",embedded:false,exported:true,typ:$Int32,tag:""},{prop:"Flags",name:"Flags",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Change",name:"Change",embedded:false,exported:true,typ:$Uint32,tag:""}]);OY.init("",[{prop:"Family",name:"Family",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Prefixlen",name:"Prefixlen",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Flags",name:"Flags",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Scope",name:"Scope",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Index",name:"Index",embedded:false,exported:true,typ:$Uint32,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}F=PL.nil;P=null;$pkg.ForkLock=new C.RWMutex.ptr(new C.Mutex.ptr(0,0),0,0,0,0);$pkg.SocketDisableIPv6=false;HR=new $Int64(0,0);E=false;Q=false;R=-1;AG=M();$pkg.Stdin=0;$pkg.Stdout=1;$pkg.Stderr=2;GO=new GN(11);GP=new GN(22);GQ=new GN(2);HW=$toNativeArray($kindString,["","operation not permitted","no such file or directory","no such process","interrupted system call","input/output error","no such device or address","argument list too long","exec format error","bad file descriptor","no child processes","resource temporarily unavailable","cannot allocate memory","permission denied","bad address","block device required","device or resource busy","file exists","invalid cross-device link","no such device","not a directory","is a directory","invalid argument","too many open files in system","too many open files","inappropriate ioctl for device","text file busy","file too large","no space left on device","illegal seek","read-only file system","too many links","broken pipe","numerical argument out of domain","numerical result out of range","resource deadlock avoided","file name too long","no locks available","function not implemented","directory not empty","too many levels of symbolic links","","no message of desired type","identifier removed","channel number out of range","level 2 not synchronized","level 3 halted","level 3 reset","link number out of range","protocol driver not attached","no CSI structure available","level 2 halted","invalid exchange","invalid request descriptor","exchange full","no anode","invalid request code","invalid slot","","bad font file format","device not a stream","no data available","timer expired","out of streams resources","machine is not on the network","package not installed","object is remote","link has been severed","advertise error","srmount error","communication error on send","protocol error","multihop attempted","RFS specific error","bad message","value too large for defined data type","name not unique on network","file descriptor in bad state","remote address changed","can not access a needed shared library","accessing a corrupted shared library",".lib section in a.out corrupted","attempting to link in too many shared libraries","cannot exec a shared library directly","invalid or incomplete multibyte or wide character","interrupted system call should be restarted","streams pipe error","too many users","socket operation on non-socket","destination address required","message too long","protocol wrong type for socket","protocol not available","protocol not supported","socket type not supported","operation not supported","protocol family not supported","address family not supported by protocol","address already in use","cannot assign requested address","network is down","network is unreachable","network dropped connection on reset","software caused connection abort","connection reset by peer","no buffer space available","transport endpoint is already connected","transport endpoint is not connected","cannot send after transport endpoint shutdown","too many references: cannot splice","connection timed out","connection refused","host is down","no route to host","operation already in progress","operation now in progress","stale NFS file handle","structure needs cleaning","not a XENIX named type file","no XENIX semaphores available","is a named type file","remote I/O error","disk quota exceeded","no medium found","wrong medium type","operation canceled","required key not available","key has expired","key has been revoked","key was rejected by service","owner died","state not recoverable","operation not possible due to RF-kill"]);FX=new GM.ptr(new C.Mutex.ptr(0,0),{},NB,KY);G();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["github.com/gopherjs/gopherjs/nosync"]=(function(){var $pkg={},$init,B,E,F,J,M,N,O,P,Q;B=$pkg.Mutex=$newType(0,$kindStruct,"nosync.Mutex",true,"github.com/gopherjs/gopherjs/nosync",true,function(locked_){this.$val=this;if(arguments.length===0){this.locked=false;return;}this.locked=locked_;});E=$pkg.Once=$newType(0,$kindStruct,"nosync.Once",true,"github.com/gopherjs/gopherjs/nosync",true,function(doing_,done_){this.$val=this;if(arguments.length===0){this.doing=false;this.done=false;return;}this.doing=doing_;this.done=done_;});F=$pkg.Pool=$newType(0,$kindStruct,"nosync.Pool",true,"github.com/gopherjs/gopherjs/nosync",true,function(store_,New_){this.$val=this;if(arguments.length===0){this.store=O.nil;this.New=$throwNilPointerError;return;}this.store=store_;this.New=New_;});J=$ptrType(B);M=$funcType([],[],false);N=$ptrType(E);O=$sliceType($emptyInterface);P=$ptrType(F);Q=$funcType([],[$emptyInterface],false);B.ptr.prototype.Lock=function(){var a;a=this;if(a.locked){$panic(new $String("nosync: mutex is already locked"));}a.locked=true;};B.prototype.Lock=function(){return this.$val.Lock();};B.ptr.prototype.Unlock=function(){var a;a=this;if(!a.locked){$panic(new $String("nosync: unlock of unlocked mutex"));}a.locked=false;};B.prototype.Unlock=function(){return this.$val.Unlock();};E.ptr.prototype.Do=function(a){var a,b,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=[b];b[0]=this;if(b[0].done){$s=-1;return;}if(b[0].doing){$panic(new $String("nosync: Do called within f"));}b[0].doing=true;$deferred.push([(function(b){return function(){b[0].doing=false;b[0].done=true;};})(b),[]]);$r=a();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:E.ptr.prototype.Do};}$f.a=a;$f.b=b;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};E.prototype.Do=function(a){return this.$val.Do(a);};F.ptr.prototype.Get=function(){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;if(a.store.$length===0){$s=1;continue;}$s=2;continue;case 1:if(!(a.New===$throwNilPointerError)){$s=3;continue;}$s=4;continue;case 3:b=a.New();$s=5;case 5:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return b;case 4:$s=-1;return $ifaceNil;case 2:e=(c=a.store,d=a.store.$length-1>>0,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]));a.store=$subslice(a.store,0,(a.store.$length-1>>0));$s=-1;return e;}return;}if($f===undefined){$f={$blk:F.ptr.prototype.Get};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};F.prototype.Get=function(){return this.$val.Get();};F.ptr.prototype.Put=function(a){var a,b;b=this;if($interfaceIsEqual(a,$ifaceNil)){return;}b.store=$append(b.store,a);};F.prototype.Put=function(a){return this.$val.Put(a);};J.methods=[{prop:"Lock",name:"Lock",pkg:"",typ:$funcType([],[],false)},{prop:"Unlock",name:"Unlock",pkg:"",typ:$funcType([],[],false)}];N.methods=[{prop:"Do",name:"Do",pkg:"",typ:$funcType([M],[],false)}];P.methods=[{prop:"Get",name:"Get",pkg:"",typ:$funcType([],[$emptyInterface],false)},{prop:"Put",name:"Put",pkg:"",typ:$funcType([$emptyInterface],[],false)}];B.init("github.com/gopherjs/gopherjs/nosync",[{prop:"locked",name:"locked",embedded:false,exported:false,typ:$Bool,tag:""}]);E.init("github.com/gopherjs/gopherjs/nosync",[{prop:"doing",name:"doing",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"done",name:"done",embedded:false,exported:false,typ:$Bool,tag:""}]);F.init("github.com/gopherjs/gopherjs/nosync",[{prop:"store",name:"store",embedded:false,exported:false,typ:O,tag:""},{prop:"New",name:"New",embedded:false,exported:true,typ:Q,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["time"]=(function(){var $pkg={},$init,C,B,E,A,D,G,AE,AX,BI,BL,BM,BO,BS,CJ,CK,CL,DK,DL,DM,DO,DQ,DR,DS,DT,DU,DV,DX,EA,EB,EC,ED,EE,EF,EG,O,Q,T,U,V,W,AA,AD,AR,BN,BP,BZ,CB,CM,DI,CN,DJ,CO,CQ,CY,h,F,H,I,J,L,M,P,R,S,X,Y,Z,AB,AC,AF,AG,AH,AI,AJ,AK,AM,AN,AO,AP,AQ,AS,AW,AY,AZ,BJ,BQ,BR,BT,BU,BV,BX,BY,CA,CC,CD,CE,CF,CG,CH,CI,CP;C=$packages["errors"];B=$packages["github.com/gopherjs/gopherjs/js"];E=$packages["github.com/gopherjs/gopherjs/nosync"];A=$packages["runtime"];D=$packages["syscall"];G=$pkg.runtimeTimer=$newType(0,$kindStruct,"time.runtimeTimer",true,"time",false,function(i_,when_,period_,f_,arg_,timeout_,active_){this.$val=this;if(arguments.length===0){this.i=0;this.when=new $Int64(0,0);this.period=new $Int64(0,0);this.f=$throwNilPointerError;this.arg=$ifaceNil;this.timeout=null;this.active=false;return;}this.i=i_;this.when=when_;this.period=period_;this.f=f_;this.arg=arg_;this.timeout=timeout_;this.active=active_;});AE=$pkg.ParseError=$newType(0,$kindStruct,"time.ParseError",true,"time",true,function(Layout_,Value_,LayoutElem_,ValueElem_,Message_){this.$val=this;if(arguments.length===0){this.Layout="";this.Value="";this.LayoutElem="";this.ValueElem="";this.Message="";return;}this.Layout=Layout_;this.Value=Value_;this.LayoutElem=LayoutElem_;this.ValueElem=ValueElem_;this.Message=Message_;});AX=$pkg.Timer=$newType(0,$kindStruct,"time.Timer",true,"time",true,function(C_,r_){this.$val=this;if(arguments.length===0){this.C=$chanNil;this.r=new G.ptr(0,new $Int64(0,0),new $Int64(0,0),$throwNilPointerError,$ifaceNil,null,false);return;}this.C=C_;this.r=r_;});BI=$pkg.Ticker=$newType(0,$kindStruct,"time.Ticker",true,"time",true,function(C_,r_){this.$val=this;if(arguments.length===0){this.C=$chanNil;this.r=new G.ptr(0,new $Int64(0,0),new $Int64(0,0),$throwNilPointerError,$ifaceNil,null,false);return;}this.C=C_;this.r=r_;});BL=$pkg.Time=$newType(0,$kindStruct,"time.Time",true,"time",true,function(wall_,ext_,loc_){this.$val=this;if(arguments.length===0){this.wall=new $Uint64(0,0);this.ext=new $Int64(0,0);this.loc=DU.nil;return;}this.wall=wall_;this.ext=ext_;this.loc=loc_;});BM=$pkg.Month=$newType(4,$kindInt,"time.Month",true,"time",true,null);BO=$pkg.Weekday=$newType(4,$kindInt,"time.Weekday",true,"time",true,null);BS=$pkg.Duration=$newType(8,$kindInt64,"time.Duration",true,"time",true,null);CJ=$pkg.Location=$newType(0,$kindStruct,"time.Location",true,"time",true,function(name_,zone_,tx_,cacheStart_,cacheEnd_,cacheZone_){this.$val=this;if(arguments.length===0){this.name="";this.zone=DK.nil;this.tx=DL.nil;this.cacheStart=new $Int64(0,0);this.cacheEnd=new $Int64(0,0);this.cacheZone=DM.nil;return;}this.name=name_;this.zone=zone_;this.tx=tx_;this.cacheStart=cacheStart_;this.cacheEnd=cacheEnd_;this.cacheZone=cacheZone_;});CK=$pkg.zone=$newType(0,$kindStruct,"time.zone",true,"time",false,function(name_,offset_,isDST_){this.$val=this;if(arguments.length===0){this.name="";this.offset=0;this.isDST=false;return;}this.name=name_;this.offset=offset_;this.isDST=isDST_;});CL=$pkg.zoneTrans=$newType(0,$kindStruct,"time.zoneTrans",true,"time",false,function(when_,index_,isstd_,isutc_){this.$val=this;if(arguments.length===0){this.when=new $Int64(0,0);this.index=0;this.isstd=false;this.isutc=false;return;}this.when=when_;this.index=index_;this.isstd=isstd_;this.isutc=isutc_;});DK=$sliceType(CK);DL=$sliceType(CL);DM=$ptrType(CK);DO=$sliceType($String);DQ=$arrayType($Uint8,20);DR=$sliceType($Uint8);DS=$arrayType($Uint8,9);DT=$arrayType($Uint8,64);DU=$ptrType(CJ);DV=$chanType(BL,false,false);DX=$arrayType($Uint8,32);EA=$funcType([$emptyInterface,$Uintptr],[],false);EB=$ptrType(B.Object);EC=$ptrType(AE);ED=$ptrType(AX);EE=$chanType(BL,false,true);EF=$ptrType(BI);EG=$ptrType(BL);F=function(){$unused(CE(new $Int64(0,0),new $Int64(0,0)));};H=function(){var i,j,k,l;i=new($global.Date)();j=$internalize(i,$String);k=P(j,40);l=P(j,41);if((k===-1)||(l===-1)){CN.name="UTC";return;}CN.name=$substring(j,(k+1>>0),l);CN.zone=new DK([new CK.ptr(CN.name,$imul(($parseInt(i.getTimezoneOffset())>>0),-60),false)]);};I=function(){return $mul64($internalize(new($global.Date)().getTime(),$Int64),new $Int64(0,1000000));};J=function(){var i,j,k,l,m,n,o,p;i=new $Int64(0,0);j=0;k=new $Int64(0,0);l=I();m=$div64(l,new $Int64(0,1000000000),false);n=(((o=$div64(l,new $Int64(0,1000000000),true),o.$low+((o.$high>>31)*4294967296))>>0));p=l;i=m;j=n;k=p;return[i,j,k];};L=function(i){var i,j,k,l;i.active=true;l=$div64(((j=i.when,k=I(),new $Int64(j.$high-k.$high,j.$low-k.$low))),new $Int64(0,1000000),false);if((l.$high>0||(l.$high===0&&l.$low>2147483647))){return;}if((l.$high<0||(l.$high===0&&l.$low<0))){l=new $Int64(0,0);}i.timeout=$setTimeout((function(){var m,n,o;i.active=false;if(!((m=i.period,(m.$high===0&&m.$low===0)))){i.when=(n=i.when,o=i.period,new $Int64(n.$high+o.$high,n.$low+o.$low));L(i);}$go(i.f,[i.arg,0]);}),$externalize(new $Int64(l.$high+0,l.$low+1),$Int64));};M=function(i){var i,j;$global.clearTimeout(i.timeout);j=i.active;i.active=false;return j;};P=function(i,j){var i,j;return $parseInt(i.indexOf($global.String.fromCharCode(j)))>>0;};R=function(i){var i,j;if(i.length===0){return false;}j=i.charCodeAt(0);return 97<=j&&j<=122;};S=function(i){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,bx,by,bz,ca,cb,cc,cd,ce,cf,cg,ch,ci,cj,ck,cl,cm,cn,co,cp,cq,cr,cs,ct,cu,cv,cw,cx,cy,cz,da,db,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;j="";k=0;l="";m=0;while(true){if(!(m>0));o=n;if(o===(74)){if(i.length>=(m+3>>0)&&$substring(i,m,(m+3>>0))==="Jan"){if(i.length>=(m+7>>0)&&$substring(i,m,(m+7>>0))==="January"){p=$substring(i,0,m);q=257;r=$substring(i,(m+7>>0));j=p;k=q;l=r;return[j,k,l];}if(!R($substring(i,(m+3>>0)))){s=$substring(i,0,m);t=258;u=$substring(i,(m+3>>0));j=s;k=t;l=u;return[j,k,l];}}}else if(o===(77)){if(i.length>=(m+3>>0)){if($substring(i,m,(m+3>>0))==="Mon"){if(i.length>=(m+6>>0)&&$substring(i,m,(m+6>>0))==="Monday"){v=$substring(i,0,m);w=261;x=$substring(i,(m+6>>0));j=v;k=w;l=x;return[j,k,l];}if(!R($substring(i,(m+3>>0)))){y=$substring(i,0,m);z=262;aa=$substring(i,(m+3>>0));j=y;k=z;l=aa;return[j,k,l];}}if($substring(i,m,(m+3>>0))==="MST"){ab=$substring(i,0,m);ac=21;ad=$substring(i,(m+3>>0));j=ab;k=ac;l=ad;return[j,k,l];}}}else if(o===(48)){if(i.length>=(m+2>>0)&&49<=i.charCodeAt((m+1>>0))&&i.charCodeAt((m+1>>0))<=54){ae=$substring(i,0,m);af=(ag=i.charCodeAt((m+1>>0))-49<<24>>>24,((ag<0||ag>=Q.length)?($throwRuntimeError("index out of range"),undefined):Q[ag]));ah=$substring(i,(m+2>>0));j=ae;k=af;l=ah;return[j,k,l];}}else if(o===(49)){if(i.length>=(m+2>>0)&&(i.charCodeAt((m+1>>0))===53)){ai=$substring(i,0,m);aj=522;ak=$substring(i,(m+2>>0));j=ai;k=aj;l=ak;return[j,k,l];}al=$substring(i,0,m);am=259;an=$substring(i,(m+1>>0));j=al;k=am;l=an;return[j,k,l];}else if(o===(50)){if(i.length>=(m+4>>0)&&$substring(i,m,(m+4>>0))==="2006"){ao=$substring(i,0,m);ap=273;aq=$substring(i,(m+4>>0));j=ao;k=ap;l=aq;return[j,k,l];}ar=$substring(i,0,m);as=263;at=$substring(i,(m+1>>0));j=ar;k=as;l=at;return[j,k,l];}else if(o===(95)){if(i.length>=(m+2>>0)&&(i.charCodeAt((m+1>>0))===50)){if(i.length>=(m+5>>0)&&$substring(i,(m+1>>0),(m+5>>0))==="2006"){au=$substring(i,0,(m+1>>0));av=273;aw=$substring(i,(m+5>>0));j=au;k=av;l=aw;return[j,k,l];}ax=$substring(i,0,m);ay=264;az=$substring(i,(m+2>>0));j=ax;k=ay;l=az;return[j,k,l];}}else if(o===(51)){ba=$substring(i,0,m);bb=523;bc=$substring(i,(m+1>>0));j=ba;k=bb;l=bc;return[j,k,l];}else if(o===(52)){bd=$substring(i,0,m);be=525;bf=$substring(i,(m+1>>0));j=bd;k=be;l=bf;return[j,k,l];}else if(o===(53)){bg=$substring(i,0,m);bh=527;bi=$substring(i,(m+1>>0));j=bg;k=bh;l=bi;return[j,k,l];}else if(o===(80)){if(i.length>=(m+2>>0)&&(i.charCodeAt((m+1>>0))===77)){bj=$substring(i,0,m);bk=531;bl=$substring(i,(m+2>>0));j=bj;k=bk;l=bl;return[j,k,l];}}else if(o===(112)){if(i.length>=(m+2>>0)&&(i.charCodeAt((m+1>>0))===109)){bm=$substring(i,0,m);bn=532;bo=$substring(i,(m+2>>0));j=bm;k=bn;l=bo;return[j,k,l];}}else if(o===(45)){if(i.length>=(m+7>>0)&&$substring(i,m,(m+7>>0))==="-070000"){bp=$substring(i,0,m);bq=28;br=$substring(i,(m+7>>0));j=bp;k=bq;l=br;return[j,k,l];}if(i.length>=(m+9>>0)&&$substring(i,m,(m+9>>0))==="-07:00:00"){bs=$substring(i,0,m);bt=31;bu=$substring(i,(m+9>>0));j=bs;k=bt;l=bu;return[j,k,l];}if(i.length>=(m+5>>0)&&$substring(i,m,(m+5>>0))==="-0700"){bv=$substring(i,0,m);bw=27;bx=$substring(i,(m+5>>0));j=bv;k=bw;l=bx;return[j,k,l];}if(i.length>=(m+6>>0)&&$substring(i,m,(m+6>>0))==="-07:00"){by=$substring(i,0,m);bz=30;ca=$substring(i,(m+6>>0));j=by;k=bz;l=ca;return[j,k,l];}if(i.length>=(m+3>>0)&&$substring(i,m,(m+3>>0))==="-07"){cb=$substring(i,0,m);cc=29;cd=$substring(i,(m+3>>0));j=cb;k=cc;l=cd;return[j,k,l];}}else if(o===(90)){if(i.length>=(m+7>>0)&&$substring(i,m,(m+7>>0))==="Z070000"){ce=$substring(i,0,m);cf=23;cg=$substring(i,(m+7>>0));j=ce;k=cf;l=cg;return[j,k,l];}if(i.length>=(m+9>>0)&&$substring(i,m,(m+9>>0))==="Z07:00:00"){ch=$substring(i,0,m);ci=26;cj=$substring(i,(m+9>>0));j=ch;k=ci;l=cj;return[j,k,l];}if(i.length>=(m+5>>0)&&$substring(i,m,(m+5>>0))==="Z0700"){ck=$substring(i,0,m);cl=22;cm=$substring(i,(m+5>>0));j=ck;k=cl;l=cm;return[j,k,l];}if(i.length>=(m+6>>0)&&$substring(i,m,(m+6>>0))==="Z07:00"){cn=$substring(i,0,m);co=25;cp=$substring(i,(m+6>>0));j=cn;k=co;l=cp;return[j,k,l];}if(i.length>=(m+3>>0)&&$substring(i,m,(m+3>>0))==="Z07"){cq=$substring(i,0,m);cr=24;cs=$substring(i,(m+3>>0));j=cq;k=cr;l=cs;return[j,k,l];}}else if(o===(46)){if((m+1>>0)>0))===48)||(i.charCodeAt((m+1>>0))===57))){ct=i.charCodeAt((m+1>>0));cu=m+1>>0;while(true){if(!(cu>0;}if(!AG(i,cu)){cv=32;if(i.charCodeAt((m+1>>0))===57){cv=33;}cv=cv|((((cu-((m+1>>0))>>0))<<16>>0));cw=$substring(i,0,m);cx=cv;cy=$substring(i,cu);j=cw;k=cx;l=cy;return[j,k,l];}}}m=m+(1)>>0;}cz=i;da=0;db="";j=cz;k=da;l=db;return[j,k,l];};X=function(i,j){var i,j,k,l,m;k=0;while(true){if(!(k>>0;m=(m|(32))>>>0;if(!((l===m))||l<97||l>122){return false;}}k=k+(1)>>0;}return true;};Y=function(i,j){var i,j,k,l,m,n;k=i;l=0;while(true){if(!(l=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+l]);if(j.length>=n.length&&X($substring(j,0,n.length),n)){return[m,$substring(j,n.length),$ifaceNil];}l++;}return[-1,j,AD];};Z=function(i,j,k){var i,j,k,l,m,n,o,p,q;l=((j>>>0));if(j<0){i=$append(i,45);l=((-j>>>0));}m=DQ.zero();n=20;while(true){if(!(l>=10)){break;}n=n-(1)>>0;p=(o=l/10,(o===o&&o!==1/0&&o!==-1/0)?o>>>0:$throwRuntimeError("integer divide by zero"));((n<0||n>=m.length)?($throwRuntimeError("index out of range"),undefined):m[n]=((((48+l>>>0)-(p*10>>>0)>>>0)<<24>>>24)));l=p;}n=n-(1)>>0;((n<0||n>=m.length)?($throwRuntimeError("index out of range"),undefined):m[n]=(((48+l>>>0)<<24>>>24)));q=20-n>>0;while(true){if(!(q>0;}return $appendSlice(i,$subslice(new DR(m),n));};AB=function(i){var i,j,k,l,m,n,o,p,q,r,s;j=0;k=$ifaceNil;l=false;if(!(i==="")&&((i.charCodeAt(0)===45)||(i.charCodeAt(0)===43))){l=i.charCodeAt(0)===45;i=$substring(i,1);}m=AS(i);n=m[0];o=m[1];k=m[2];j=(((n.$low+((n.$high>>31)*4294967296))>>0));if(!($interfaceIsEqual(k,$ifaceNil))||!(o==="")){p=0;q=AA;j=p;k=q;return[j,k];}if(l){j=-j;}r=j;s=$ifaceNil;j=r;k=s;return[j,k];};AC=function(i,j,k,l){var i,j,k,l,m,n,o,p,q,r;m=j;n=DS.zero();o=9;while(true){if(!(o>0)){break;}o=o-(1)>>0;((o<0||o>=n.length)?($throwRuntimeError("index out of range"),undefined):n[o]=((((p=m%10,p===p?p:$throwRuntimeError("integer divide by zero"))+48>>>0)<<24>>>24)));m=(q=m/(10),(q===q&&q!==1/0&&q!==-1/0)?q>>>0:$throwRuntimeError("integer divide by zero"));}if(k>9){k=9;}if(l){while(true){if(!(k>0&&((r=k-1>>0,((r<0||r>=n.length)?($throwRuntimeError("index out of range"),undefined):n[r]))===48))){break;}k=k-(1)>>0;}if(k===0){return i;}}i=$append(i,46);return $appendSlice(i,$subslice(new DR(n),0,k));};BL.ptr.prototype.String=function(){var i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;j=$clone(i,BL).Format("2006-01-02 15:04:05.999999999 -0700 MST");$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}k=j;if(!((l=(m=i.wall,new $Uint64(m.$high&2147483648,(m.$low&0)>>>0)),(l.$high===0&&l.$low===0)))){o=((n=i.ext,new $Uint64(n.$high,n.$low)));p=43;if((q=i.ext,(q.$high<0||(q.$high===0&&q.$low<0)))){p=45;o=new $Uint64(-o.$high,-o.$low);}r=$div64(o,new $Uint64(0,1000000000),false);s=$div64(o,new $Uint64(0,1000000000),true);t=r;o=s;u=$div64(t,new $Uint64(0,1000000000),false);v=$div64(t,new $Uint64(0,1000000000),true);w=u;t=v;x=DR.nil;x=$appendSlice(x," m=");x=$append(x,p);y=0;if(!((w.$high===0&&w.$low===0))){x=Z(x,((w.$low>>0)),0);y=9;}x=Z(x,((t.$low>>0)),y);x=$append(x,46);x=Z(x,((o.$low>>0)),9);k=k+(($bytesToString(x)));}$s=-1;return k;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.String};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.String=function(){return this.$val.String();};BL.ptr.prototype.Format=function(i){var i,j,k,l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:j=this;k=DR.nil;l=i.length+10>>0;if(l<64){m=DT.zero();k=$subslice(new DR(m),0,0);}else{k=$makeSlice(DR,0,l);}n=$clone(j,BL).AppendFormat(k,i);$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}k=n;$s=-1;return($bytesToString(k));}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.Format};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.Format=function(i){return this.$val.Format(i);};BL.ptr.prototype.AppendFormat=function(i,j){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:k=this;m=$clone(k,BL).locabs();$s=1;case 1:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;n=l[0];o=l[1];p=l[2];q=-1;r=0;s=0;t=-1;u=0;v=0;while(true){if(!(!(j===""))){break;}w=S(j);x=w[0];y=w[1];z=w[2];if(!(x==="")){i=$appendSlice(i,x);}if(y===0){break;}j=z;if(q<0&&!(((y&256)===0))){aa=BY(p,true);q=aa[0];r=aa[1];s=aa[2];}if(t<0&&!(((y&512)===0))){ab=BR(p);t=ab[0];u=ab[1];v=ab[2];}switch(0){default:ac=y&65535;if(ac===(274)){ad=q;if(ad<0){ad=-ad;}i=Z(i,(ae=ad%100,ae===ae?ae:$throwRuntimeError("integer divide by zero")),2);}else if(ac===(273)){i=Z(i,q,4);}else if(ac===(258)){i=$appendSlice(i,$substring(new BM(r).String(),0,3));}else if(ac===(257)){af=new BM(r).String();i=$appendSlice(i,af);}else if(ac===(259)){i=Z(i,((r>>0)),0);}else if(ac===(260)){i=Z(i,((r>>0)),2);}else if(ac===(262)){i=$appendSlice(i,$substring(new BO(BQ(p)).String(),0,3));}else if(ac===(261)){ag=new BO(BQ(p)).String();i=$appendSlice(i,ag);}else if(ac===(263)){i=Z(i,s,0);}else if(ac===(264)){if(s<10){i=$append(i,32);}i=Z(i,s,0);}else if(ac===(265)){i=Z(i,s,2);}else if(ac===(522)){i=Z(i,t,2);}else if(ac===(523)){ai=(ah=t%12,ah===ah?ah:$throwRuntimeError("integer divide by zero"));if(ai===0){ai=12;}i=Z(i,ai,0);}else if(ac===(524)){ak=(aj=t%12,aj===aj?aj:$throwRuntimeError("integer divide by zero"));if(ak===0){ak=12;}i=Z(i,ak,2);}else if(ac===(525)){i=Z(i,u,0);}else if(ac===(526)){i=Z(i,u,2);}else if(ac===(527)){i=Z(i,v,0);}else if(ac===(528)){i=Z(i,v,2);}else if(ac===(531)){if(t>=12){i=$appendSlice(i,"PM");}else{i=$appendSlice(i,"AM");}}else if(ac===(532)){if(t>=12){i=$appendSlice(i,"pm");}else{i=$appendSlice(i,"am");}}else if((ac===(22))||(ac===(25))||(ac===(23))||(ac===(24))||(ac===(26))||(ac===(27))||(ac===(30))||(ac===(28))||(ac===(29))||(ac===(31))){if((o===0)&&((y===22)||(y===25)||(y===23)||(y===24)||(y===26))){i=$append(i,90);break;}am=(al=o/60,(al===al&&al!==1/0&&al!==-1/0)?al>>0:$throwRuntimeError("integer divide by zero"));an=o;if(am<0){i=$append(i,45);am=-am;an=-an;}else{i=$append(i,43);}i=Z(i,(ao=am/60,(ao===ao&&ao!==1/0&&ao!==-1/0)?ao>>0:$throwRuntimeError("integer divide by zero")),2);if((y===25)||(y===30)||(y===26)||(y===31)){i=$append(i,58);}if(!((y===29))&&!((y===24))){i=Z(i,(ap=am%60,ap===ap?ap:$throwRuntimeError("integer divide by zero")),2);}if((y===23)||(y===28)||(y===31)||(y===26)){if((y===31)||(y===26)){i=$append(i,58);}i=Z(i,(aq=an%60,aq===aq?aq:$throwRuntimeError("integer divide by zero")),2);}}else if(ac===(21)){if(!(n==="")){i=$appendSlice(i,n);break;}as=(ar=o/60,(ar===ar&&ar!==1/0&&ar!==-1/0)?ar>>0:$throwRuntimeError("integer divide by zero"));if(as<0){i=$append(i,45);as=-as;}else{i=$append(i,43);}i=Z(i,(at=as/60,(at===at&&at!==1/0&&at!==-1/0)?at>>0:$throwRuntimeError("integer divide by zero")),2);i=Z(i,(au=as%60,au===au?au:$throwRuntimeError("integer divide by zero")),2);}else if((ac===(32))||(ac===(33))){i=AC(i,(($clone(k,BL).Nanosecond()>>>0)),y>>16>>0,(y&65535)===33);}}}$s=-1;return i;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.AppendFormat};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.AppendFormat=function(i,j){return this.$val.AppendFormat(i,j);};AF=function(i){var i;return"\""+i+"\"";};AE.ptr.prototype.Error=function(){var i;i=this;if(i.Message===""){return"parsing time "+AF(i.Value)+" as "+AF(i.Layout)+": cannot parse "+AF(i.ValueElem)+" as "+AF(i.LayoutElem);}return"parsing time "+AF(i.Value)+i.Message;};AE.prototype.Error=function(){return this.$val.Error();};AG=function(i,j){var i,j,k;if(i.length<=j){return false;}k=i.charCodeAt(j);return 48<=k&&k<=57;};AH=function(i,j){var i,j;if(!AG(i,0)){return[0,i,AD];}if(!AG(i,1)){if(j){return[0,i,AD];}return[(((i.charCodeAt(0)-48<<24>>>24)>>0)),$substring(i,1),$ifaceNil];}return[($imul((((i.charCodeAt(0)-48<<24>>>24)>>0)),10))+(((i.charCodeAt(1)-48<<24>>>24)>>0))>>0,$substring(i,2),$ifaceNil];};AI=function(i){var i;while(true){if(!(i.length>0&&(i.charCodeAt(0)===32))){break;}i=$substring(i,1);}return i;};AJ=function(i,j){var i,j;while(true){if(!(j.length>0)){break;}if(j.charCodeAt(0)===32){if(i.length>0&&!((i.charCodeAt(0)===32))){return[i,AD];}j=AI(j);i=AI(i);continue;}if((i.length===0)||!((i.charCodeAt(0)===j.charCodeAt(0)))){return[i,AD];}j=$substring(j,1);i=$substring(i,1);}return[i,$ifaceNil];};AK=function(i,j){var i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:k=AM(i,j,$pkg.UTC,$pkg.Local);$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}$s=-1;return k;}return;}if($f===undefined){$f={$blk:AK};}$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Parse=AK;AM=function(i,j,k,l){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,bx,by,bz,ca,cb,cc,cd,ce,cf,cg,ch,ci,cj,ck,cl,cm,cn,co,cp,cq,cr,cs,ct,cu,cv,cw,cx,cy,cz,da,db,dc,dd,de,df,dg,dh,di,dj,dk,dl,dm,dn,dp,dq,dr,ds,dt,du,dv,dw,dx,dy,dz,ea,eb,ec,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;bj=$f.bj;bk=$f.bk;bl=$f.bl;bm=$f.bm;bn=$f.bn;bo=$f.bo;bp=$f.bp;bq=$f.bq;br=$f.br;bs=$f.bs;bt=$f.bt;bu=$f.bu;bv=$f.bv;bw=$f.bw;bx=$f.bx;by=$f.by;bz=$f.bz;ca=$f.ca;cb=$f.cb;cc=$f.cc;cd=$f.cd;ce=$f.ce;cf=$f.cf;cg=$f.cg;ch=$f.ch;ci=$f.ci;cj=$f.cj;ck=$f.ck;cl=$f.cl;cm=$f.cm;cn=$f.cn;co=$f.co;cp=$f.cp;cq=$f.cq;cr=$f.cr;cs=$f.cs;ct=$f.ct;cu=$f.cu;cv=$f.cv;cw=$f.cw;cx=$f.cx;cy=$f.cy;cz=$f.cz;da=$f.da;db=$f.db;dc=$f.dc;dd=$f.dd;de=$f.de;df=$f.df;dg=$f.dg;dh=$f.dh;di=$f.di;dj=$f.dj;dk=$f.dk;dl=$f.dl;dm=$f.dm;dn=$f.dn;dp=$f.dp;dq=$f.dq;dr=$f.dr;ds=$f.ds;dt=$f.dt;du=$f.du;dv=$f.dv;dw=$f.dw;dx=$f.dx;dy=$f.dy;dz=$f.dz;ea=$f.ea;eb=$f.eb;ec=$f.ec;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:m=i;n=j;o=m;p=n;q="";r=false;s=false;t=0;u=1;v=1;w=0;x=0;y=0;z=0;aa=DU.nil;ab=-1;ac="";while(true){ad=$ifaceNil;ae=S(i);af=ae[0];ag=ae[1];ah=ae[2];ai=$substring(i,af.length,(i.length-ah.length>>0));aj=AJ(j,af);j=aj[0];ad=aj[1];if(!($interfaceIsEqual(ad,$ifaceNil))){$s=-1;return[new BL.ptr(new $Uint64(0,0),new $Int64(0,0),DU.nil),new AE.ptr(o,p,af,j,"")];}if(ag===0){if(!((j.length===0))){$s=-1;return[new BL.ptr(new $Uint64(0,0),new $Int64(0,0),DU.nil),new AE.ptr(o,p,"",j,": extra text: "+j)];}break;}i=ah;ak="";switch(0){default:al=ag&65535;if(al===(274)){if(j.length<2){ad=AD;break;}am=$substring(j,0,2);an=$substring(j,2);ak=am;j=an;ao=AB(ak);t=ao[0];ad=ao[1];if(t>=69){t=t+(1900)>>0;}else{t=t+(2000)>>0;}}else if(al===(273)){if(j.length<4||!AG(j,0)){ad=AD;break;}ap=$substring(j,0,4);aq=$substring(j,4);ak=ap;j=aq;ar=AB(ak);t=ar[0];ad=ar[1];}else if(al===(258)){as=Y(V,j);u=as[0];j=as[1];ad=as[2];u=u+(1)>>0;}else if(al===(257)){at=Y(W,j);u=at[0];j=at[1];ad=at[2];u=u+(1)>>0;}else if((al===(259))||(al===(260))){au=AH(j,ag===260);u=au[0];j=au[1];ad=au[2];if(u<=0||120&&(j.charCodeAt(0)===32)){j=$substring(j,1);}ax=AH(j,ag===265);v=ax[0];j=ax[1];ad=ax[2];if(v<0){q="day";}}else if(al===(522)){ay=AH(j,false);w=ay[0];j=ay[1];ad=ay[2];if(w<0||24<=w){q="hour";}}else if((al===(523))||(al===(524))){az=AH(j,ag===524);w=az[0];j=az[1];ad=az[2];if(w<0||12=2&&(j.charCodeAt(0)===46)&&AG(j,1)){bc=S(i);ag=bc[1];ag=ag&(65535);if((ag===32)||(ag===33)){break;}bd=2;while(true){if(!(bd>0;}be=AQ(j,bd);z=be[0];q=be[1];ad=be[2];j=$substring(j,bd);}}else if(al===(531)){if(j.length<2){ad=AD;break;}bf=$substring(j,0,2);bg=$substring(j,2);ak=bf;j=bg;bh=ak;if(bh===("PM")){s=true;}else if(bh===("AM")){r=true;}else{ad=AD;}}else if(al===(532)){if(j.length<2){ad=AD;break;}bi=$substring(j,0,2);bj=$substring(j,2);ak=bi;j=bj;bk=ak;if(bk===("pm")){s=true;}else if(bk===("am")){r=true;}else{ad=AD;}}else if((al===(22))||(al===(25))||(al===(23))||(al===(24))||(al===(26))||(al===(27))||(al===(29))||(al===(30))||(al===(28))||(al===(31))){if(((ag===22)||(ag===24)||(ag===25))&&j.length>=1&&(j.charCodeAt(0)===90)){j=$substring(j,1);aa=$pkg.UTC;break;}bl="";bm="";bn="";bo="";bp=bl;bq=bm;br=bn;bs=bo;if((ag===25)||(ag===30)){if(j.length<6){ad=AD;break;}if(!((j.charCodeAt(3)===58))){ad=AD;break;}bt=$substring(j,0,1);bu=$substring(j,1,3);bv=$substring(j,4,6);bw="00";bx=$substring(j,6);bp=bt;bq=bu;br=bv;bs=bw;j=bx;}else if((ag===29)||(ag===24)){if(j.length<3){ad=AD;break;}by=$substring(j,0,1);bz=$substring(j,1,3);ca="00";cb="00";cc=$substring(j,3);bp=by;bq=bz;br=ca;bs=cb;j=cc;}else if((ag===26)||(ag===31)){if(j.length<9){ad=AD;break;}if(!((j.charCodeAt(3)===58))||!((j.charCodeAt(6)===58))){ad=AD;break;}cd=$substring(j,0,1);ce=$substring(j,1,3);cf=$substring(j,4,6);cg=$substring(j,7,9);ch=$substring(j,9);bp=cd;bq=ce;br=cf;bs=cg;j=ch;}else if((ag===23)||(ag===28)){if(j.length<7){ad=AD;break;}ci=$substring(j,0,1);cj=$substring(j,1,3);ck=$substring(j,3,5);cl=$substring(j,5,7);cm=$substring(j,7);bp=ci;bq=cj;br=ck;bs=cl;j=cm;}else{if(j.length<5){ad=AD;break;}cn=$substring(j,0,1);co=$substring(j,1,3);cp=$substring(j,3,5);cq="00";cr=$substring(j,5);bp=cn;bq=co;br=cp;bs=cq;j=cr;}cs=0;ct=0;cu=0;cv=cs;cw=ct;cx=cu;cy=AB(bq);cv=cy[0];ad=cy[1];if($interfaceIsEqual(ad,$ifaceNil)){cz=AB(br);cw=cz[0];ad=cz[1];}if($interfaceIsEqual(ad,$ifaceNil)){da=AB(bs);cx=da[0];ad=da[1];}ab=($imul(((($imul(cv,60))+cw>>0)),60))+cx>>0;db=bp.charCodeAt(0);if(db===(43)){}else if(db===(45)){ab=-ab;}else{ad=AD;}}else if(al===(21)){if(j.length>=3&&$substring(j,0,3)==="UTC"){aa=$pkg.UTC;j=$substring(j,3);break;}dc=AN(j);dd=dc[0];de=dc[1];if(!de){ad=AD;break;}df=$substring(j,0,dd);dg=$substring(j,dd);ac=df;j=dg;}else if(al===(32)){dh=1+((ag>>16>>0))>>0;if(j.length>0)>0))&&j.charCodeAt((dj+1>>0))<=57)){break;}dj=dj+(1)>>0;}dk=AQ(j,1+dj>>0);z=dk[0];q=dk[1];ad=dk[2];j=$substring(j,(1+dj>>0));}}if(!(q==="")){$s=-1;return[new BL.ptr(new $Uint64(0,0),new $Int64(0,0),DU.nil),new AE.ptr(o,p,ai,j,": "+q+" out of range")];}if(!($interfaceIsEqual(ad,$ifaceNil))){$s=-1;return[new BL.ptr(new $Uint64(0,0),new $Int64(0,0),DU.nil),new AE.ptr(o,p,ai,j,"")];}}if(s&&w<12){w=w+(12)>>0;}else if(r&&(w===12)){w=0;}if(v<1||v>CA(((u>>0)),t)){$s=-1;return[new BL.ptr(new $Uint64(0,0),new $Int64(0,0),DU.nil),new AE.ptr(o,p,"",j,": day out of range")];}if(!(aa===DU.nil)){$s=1;continue;}$s=2;continue;case 1:dl=CH(t,((u>>0)),v,w,x,y,z,aa);$s=3;case 3:if($c){$c=false;dl=dl.$blk();}if(dl&&dl.$blk!==undefined){break s;}$s=-1;return[dl,$ifaceNil];case 2:if(!((ab===-1))){$s=4;continue;}$s=5;continue;case 4:dm=CH(t,((u>>0)),v,w,x,y,z,$pkg.UTC);$s=6;case 6:if($c){$c=false;dm=dm.$blk();}if(dm&&dm.$blk!==undefined){break s;}dn=$clone(dm,BL);dn.addSec((dp=(new $Int64(0,ab)),new $Int64(-dp.$high,-dp.$low)));dr=l.lookup(dn.unixSec());$s=7;case 7:if($c){$c=false;dr=dr.$blk();}if(dr&&dr.$blk!==undefined){break s;}dq=dr;ds=dq[0];dt=dq[1];if((dt===ab)&&(ac===""||ds===ac)){dn.setLoc(l);$s=-1;return[dn,$ifaceNil];}dn.setLoc(CP(ac,ab));$s=-1;return[dn,$ifaceNil];case 5:if(!(ac==="")){$s=8;continue;}$s=9;continue;case 8:du=CH(t,((u>>0)),v,w,x,y,z,$pkg.UTC);$s=10;case 10:if($c){$c=false;du=du.$blk();}if(du&&du.$blk!==undefined){break s;}dv=$clone(du,BL);dx=l.lookupName(ac,dv.unixSec());$s=11;case 11:if($c){$c=false;dx=dx.$blk();}if(dx&&dx.$blk!==undefined){break s;}dw=dx;dy=dw[0];dz=dw[1];if(dz){dv.addSec((ea=(new $Int64(0,dy)),new $Int64(-ea.$high,-ea.$low)));dv.setLoc(l);$s=-1;return[dv,$ifaceNil];}if(ac.length>3&&$substring(ac,0,3)==="GMT"){eb=AB($substring(ac,3));dy=eb[0];dy=$imul(dy,(3600));}dv.setLoc(CP(ac,dy));$s=-1;return[dv,$ifaceNil];case 9:ec=CH(t,((u>>0)),v,w,x,y,z,k);$s=12;case 12:if($c){$c=false;ec=ec.$blk();}if(ec&&ec.$blk!==undefined){break s;}$s=-1;return[ec,$ifaceNil];}return;}if($f===undefined){$f={$blk:AM};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.bj=bj;$f.bk=bk;$f.bl=bl;$f.bm=bm;$f.bn=bn;$f.bo=bo;$f.bp=bp;$f.bq=bq;$f.br=br;$f.bs=bs;$f.bt=bt;$f.bu=bu;$f.bv=bv;$f.bw=bw;$f.bx=bx;$f.by=by;$f.bz=bz;$f.ca=ca;$f.cb=cb;$f.cc=cc;$f.cd=cd;$f.ce=ce;$f.cf=cf;$f.cg=cg;$f.ch=ch;$f.ci=ci;$f.cj=cj;$f.ck=ck;$f.cl=cl;$f.cm=cm;$f.cn=cn;$f.co=co;$f.cp=cp;$f.cq=cq;$f.cr=cr;$f.cs=cs;$f.ct=ct;$f.cu=cu;$f.cv=cv;$f.cw=cw;$f.cx=cx;$f.cy=cy;$f.cz=cz;$f.da=da;$f.db=db;$f.dc=dc;$f.dd=dd;$f.de=de;$f.df=df;$f.dg=dg;$f.dh=dh;$f.di=di;$f.dj=dj;$f.dk=dk;$f.dl=dl;$f.dm=dm;$f.dn=dn;$f.dp=dp;$f.dq=dq;$f.dr=dr;$f.ds=ds;$f.dt=dt;$f.du=du;$f.dv=dv;$f.dw=dw;$f.dx=dx;$f.dy=dy;$f.dz=dz;$f.ea=ea;$f.eb=eb;$f.ec=ec;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};AN=function(i){var aa,ab,ac,ad,ae,af,ag,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;j=0;k=false;if(i.length<3){l=0;m=false;j=l;k=m;return[j,k];}if(i.length>=4&&($substring(i,0,4)==="ChST"||$substring(i,0,4)==="MeST")){n=4;o=true;j=n;k=o;return[j,k];}if($substring(i,0,3)==="GMT"){j=AO(i);p=j;q=true;j=p;k=q;return[j,k];}if((i.charCodeAt(0)===43)||(i.charCodeAt(0)===45)){j=AP(i);r=j>0;s=j;t=r;j=s;k=t;return[j,k];}u=0;u=0;while(true){if(!(u<6)){break;}if(u>=i.length){break;}v=i.charCodeAt(u);if(v<65||90>0;}w=u;if((w===(0))||(w===(1))||(w===(2))||(w===(6))){x=0;y=false;j=x;k=y;return[j,k];}else if(w===(5)){if(i.charCodeAt(4)===84){z=5;aa=true;j=z;k=aa;return[j,k];}}else if(w===(4)){if((i.charCodeAt(3)===84)||$substring(i,0,4)==="WITA"){ab=4;ac=true;j=ab;k=ac;return[j,k];}}else if(w===(3)){ad=3;ae=true;j=ad;k=ae;return[j,k];}af=0;ag=false;j=af;k=ag;return[j,k];};AO=function(i){var i;i=$substring(i,3);if(i.length===0){return 3;}return 3+AP(i)>>0;};AP=function(i){var i,j,k,l,m,n;j=i.charCodeAt(0);if(!((j===45))&&!((j===43))){return 0;}k=AS($substring(i,1));l=k[0];m=k[1];n=k[2];if(!($interfaceIsEqual(n,$ifaceNil))||$substring(i,1)===m){return 0;}if(j===45){l=new $Int64(-l.$high,-l.$low);}if((l.$high<-1||(l.$high===-1&&l.$low<4294967273))||(0>0;};AQ=function(i,j){var i,j,k,l,m,n,o,p;k=0;l="";m=$ifaceNil;if(!((i.charCodeAt(0)===46))){m=AD;return[k,l,m];}n=AB($substring(i,1,j));k=n[0];m=n[1];if(!($interfaceIsEqual(m,$ifaceNil))){return[k,l,m];}if(k<0||1000000000<=k){l="fractional second";return[k,l,m];}o=10-j>>0;p=0;while(true){if(!(p>0;}return[k,l,m];};AS=function(i){var i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;j=new $Int64(0,0);k="";l=$ifaceNil;m=0;while(true){if(!(m57){break;}if((j.$high>214748364||(j.$high===214748364&&j.$low>3435973836))){o=new $Int64(0,0);p="";q=AR;j=o;k=p;l=q;return[j,k,l];}j=(r=(s=$mul64(j,new $Int64(0,10)),t=(new $Int64(0,n)),new $Int64(s.$high+t.$high,s.$low+t.$low)),new $Int64(r.$high-0,r.$low-48));if((j.$high<0||(j.$high===0&&j.$low<0))){u=new $Int64(0,0);v="";w=AR;j=u;k=v;l=w;return[j,k,l];}m=m+(1)>>0;}x=j;y=$substring(i,m);z=$ifaceNil;j=x;k=y;l=z;return[j,k,l];};AW=function(i){var i,j,k,l;if((i.$high<0||(i.$high===0&&i.$low<=0))){return I();}l=(j=I(),k=(new $Int64(i.$high,i.$low)),new $Int64(j.$high+k.$high,j.$low+k.$low));if((l.$high<0||(l.$high===0&&l.$low<0))){l=new $Int64(2147483647,4294967295);}return l;};AX.ptr.prototype.Stop=function(){var i;i=this;if(i.r.f===$throwNilPointerError){$panic(new $String("time: Stop called on uninitialized Timer"));}return M(i.r);};AX.prototype.Stop=function(){return this.$val.Stop();};AY=function(i){var i,j,k;j=new $Chan(BL,1);k=new AX.ptr(j,new G.ptr(0,AW(i),new $Int64(0,0),AZ,new DV(j),null,false));L(k.r);return k;};$pkg.NewTimer=AY;AX.ptr.prototype.Reset=function(i){var i,j,k,l;j=this;if(j.r.f===$throwNilPointerError){$panic(new $String("time: Reset called on uninitialized Timer"));}k=AW(i);l=M(j.r);j.r.when=k;L(j.r);return l;};AX.prototype.Reset=function(i){return this.$val.Reset(i);};AZ=function(i,j){var i,j,k,$r;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;$r=$f.$r;}k=$select([[$assertType(i,DV),$clone(CC(),BL)],[]]);if(k[0]===0){}else if(k[0]===1){}if($f===undefined){$f={$blk:AZ};}$f.i=i;$f.j=j;$f.k=k;$f.$r=$r;return $f;};BJ=function(i){var i,j,k;if((i.$high<0||(i.$high===0&&i.$low<=0))){$panic(C.New("non-positive interval for NewTicker"));}j=new $Chan(BL,1);k=new BI.ptr(j,new G.ptr(0,AW(i),(new $Int64(i.$high,i.$low)),AZ,new DV(j),null,false));L(k.r);return k;};$pkg.NewTicker=BJ;BI.ptr.prototype.Stop=function(){var i;i=this;M(i.r);};BI.prototype.Stop=function(){return this.$val.Stop();};BL.ptr.prototype.nsec=function(){var i,j;i=this;return(((j=i.wall,new $Uint64(j.$high&0,(j.$low&1073741823)>>>0)).$low>>0));};BL.prototype.nsec=function(){return this.$val.nsec();};BL.ptr.prototype.sec=function(){var i,j,k,l,m;i=this;if(!((j=(k=i.wall,new $Uint64(k.$high&2147483648,(k.$low&0)>>>0)),(j.$high===0&&j.$low===0)))){return(l=((m=$shiftRightUint64($shiftLeft64(i.wall,1),31),new $Int64(m.$high,m.$low))),new $Int64(13+l.$high,3618733952+l.$low));}return i.ext;};BL.prototype.sec=function(){return this.$val.sec();};BL.ptr.prototype.unixSec=function(){var i,j;i=this;return(j=i.sec(),new $Int64(j.$high+-15,j.$low+2288912640));};BL.prototype.unixSec=function(){return this.$val.unixSec();};BL.ptr.prototype.addSec=function(i){var i,j,k,l,m,n,o,p,q,r,s,t,u;j=this;if(!((k=(l=j.wall,new $Uint64(l.$high&2147483648,(l.$low&0)>>>0)),(k.$high===0&&k.$low===0)))){n=((m=$shiftRightUint64($shiftLeft64(j.wall,1),31),new $Int64(m.$high,m.$low)));o=new $Int64(n.$high+i.$high,n.$low+i.$low);if((0>>0)),s=$shiftLeft64((new $Uint64(o.$high,o.$low)),30),new $Uint64(q.$high|s.$high,(q.$low|s.$low)>>>0)),new $Uint64(p.$high|2147483648,(p.$low|0)>>>0));return;}j.stripMono();}j.ext=(t=j.ext,u=i,new $Int64(t.$high+u.$high,t.$low+u.$low));};BL.prototype.addSec=function(i){return this.$val.addSec(i);};BL.ptr.prototype.setLoc=function(i){var i,j;j=this;if(i===CM){i=DU.nil;}j.stripMono();j.loc=i;};BL.prototype.setLoc=function(i){return this.$val.setLoc(i);};BL.ptr.prototype.stripMono=function(){var i,j,k,l,m;i=this;if(!((j=(k=i.wall,new $Uint64(k.$high&2147483648,(k.$low&0)>>>0)),(j.$high===0&&j.$low===0)))){i.ext=i.sec();i.wall=(l=i.wall,m=new $Uint64(0,1073741823),new $Uint64(l.$high&m.$high,(l.$low&m.$low)>>>0));}};BL.prototype.stripMono=function(){return this.$val.stripMono();};BL.ptr.prototype.After=function(i){var i,j,k,l,m,n,o,p,q,r;j=this;if(!((k=(l=(m=j.wall,n=i.wall,new $Uint64(m.$high&n.$high,(m.$low&n.$low)>>>0)),new $Uint64(l.$high&2147483648,(l.$low&0)>>>0)),(k.$high===0&&k.$low===0)))){return(o=j.ext,p=i.ext,(o.$high>p.$high||(o.$high===p.$high&&o.$low>p.$low)));}q=j.sec();r=i.sec();return(q.$high>r.$high||(q.$high===r.$high&&q.$low>r.$low))||(q.$high===r.$high&&q.$low===r.$low)&&j.nsec()>i.nsec();};BL.prototype.After=function(i){return this.$val.After(i);};BL.ptr.prototype.Before=function(i){var i,j,k,l,m,n,o,p,q,r,s,t;j=this;if(!((k=(l=(m=j.wall,n=i.wall,new $Uint64(m.$high&n.$high,(m.$low&n.$low)>>>0)),new $Uint64(l.$high&2147483648,(l.$low&0)>>>0)),(k.$high===0&&k.$low===0)))){return(o=j.ext,p=i.ext,(o.$high>>0)),new $Uint64(l.$high&2147483648,(l.$low&0)>>>0)),(k.$high===0&&k.$low===0)))){return(o=j.ext,p=i.ext,(o.$high===p.$high&&o.$low===p.$low));}return(q=j.sec(),r=i.sec(),(q.$high===r.$high&&q.$low===r.$low))&&(j.nsec()===i.nsec());};BL.prototype.Equal=function(i){return this.$val.Equal(i);};BM.prototype.String=function(){var i,j,k,l;i=this.$val;if(1<=i&&i<=12){return(j=i-1>>0,((j<0||j>=BN.length)?($throwRuntimeError("index out of range"),undefined):BN[j]));}k=$makeSlice(DR,20);l=BU(k,(new $Uint64(0,i)));return"%!Month("+($bytesToString($subslice(k,l)))+")";};$ptrType(BM).prototype.String=function(){return new BM(this.$get()).String();};BO.prototype.String=function(){var i,j,k;i=this.$val;if(0<=i&&i<=6){return((i<0||i>=BP.length)?($throwRuntimeError("index out of range"),undefined):BP[i]);}j=$makeSlice(DR,20);k=BU(j,(new $Uint64(0,i)));return"%!Weekday("+($bytesToString($subslice(j,k)))+")";};$ptrType(BO).prototype.String=function(){return new BO(this.$get()).String();};BL.ptr.prototype.IsZero=function(){var i,j;i=this;return(j=i.sec(),(j.$high===0&&j.$low===0))&&(i.nsec()===0);};BL.prototype.IsZero=function(){return this.$val.IsZero();};BL.ptr.prototype.abs=function(){var i,j,k,l,m,n,o,p,q,r,s,t,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;j=i.loc;if(j===DU.nil||j===CN){$s=1;continue;}$s=2;continue;case 1:k=j.get();$s=3;case 3:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;case 2:l=i.unixSec();if(!(j===CM)){$s=4;continue;}$s=5;continue;case 4:if(!(j.cacheZone===DM.nil)&&(m=j.cacheStart,(m.$high>0))/86400,(k===k&&k!==1/0&&k!==-1/0)?k>>0:$throwRuntimeError("integer divide by zero"))>>0));};BL.ptr.prototype.ISOWeek=function(){var i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=0;j=0;k=this;m=$clone(k,BL).date(true);$s=1;case 1:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;i=l[0];n=l[1];o=l[2];p=l[3];r=$clone(k,BL).Weekday();$s=2;case 2:if($c){$c=false;r=r.$blk();}if(r&&r.$blk!==undefined){break s;}s=(q=(((r+6>>0)>>0))%7,q===q?q:$throwRuntimeError("integer divide by zero"));j=(t=(((p-s>>0)+7>>0))/7,(t===t&&t!==1/0&&t!==-1/0)?t>>0:$throwRuntimeError("integer divide by zero"));v=(u=(((s-p>>0)+371>>0))%7,u===u?u:$throwRuntimeError("integer divide by zero"));if(1<=v&&v<=3){j=j+(1)>>0;}if(j===0){i=i-(1)>>0;j=52;if((v===4)||((v===5)&&CF(i))){j=j+(1)>>0;}}if((n===12)&&o>=29&&s<3){x=(w=(((s+31>>0)-o>>0))%7,w===w?w:$throwRuntimeError("integer divide by zero"));if(0<=x&&x<=2){i=i+(1)>>0;j=1;}}$s=-1;return[i,j];}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.ISOWeek};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.ISOWeek=function(){return this.$val.ISOWeek();};BL.ptr.prototype.Clock=function(){var i,j,k,l,m,n,o,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=0;j=0;k=0;l=this;n=$clone(l,BL).abs();$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}o=BR(n);$s=2;case 2:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}m=o;i=m[0];j=m[1];k=m[2];$s=-1;return[i,j,k];}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.Clock};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.Clock=function(){return this.$val.Clock();};BR=function(i){var i,j,k,l,m,n;j=0;k=0;l=0;l=(($div64(i,new $Uint64(0,86400),true).$low>>0));j=(m=l/3600,(m===m&&m!==1/0&&m!==-1/0)?m>>0:$throwRuntimeError("integer divide by zero"));l=l-(($imul(j,3600)))>>0;k=(n=l/60,(n===n&&n!==1/0&&n!==-1/0)?n>>0:$throwRuntimeError("integer divide by zero"));l=l-(($imul(k,60)))>>0;return[j,k,l];};BL.ptr.prototype.Hour=function(){var i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;k=$clone(i,BL).abs();$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}$s=-1;return(j=(($div64(k,new $Uint64(0,86400),true).$low>>0))/3600,(j===j&&j!==1/0&&j!==-1/0)?j>>0:$throwRuntimeError("integer divide by zero"));}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.Hour};}$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.Hour=function(){return this.$val.Hour();};BL.ptr.prototype.Minute=function(){var i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;k=$clone(i,BL).abs();$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}$s=-1;return(j=(($div64(k,new $Uint64(0,3600),true).$low>>0))/60,(j===j&&j!==1/0&&j!==-1/0)?j>>0:$throwRuntimeError("integer divide by zero"));}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.Minute};}$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.Minute=function(){return this.$val.Minute();};BL.ptr.prototype.Second=function(){var i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;j=$clone(i,BL).abs();$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}$s=-1;return(($div64(j,new $Uint64(0,60),true).$low>>0));}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.Second};}$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.Second=function(){return this.$val.Second();};BL.ptr.prototype.Nanosecond=function(){var i;i=this;return((i.nsec()>>0));};BL.prototype.Nanosecond=function(){return this.$val.Nanosecond();};BL.ptr.prototype.YearDay=function(){var i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;k=$clone(i,BL).date(false);$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;l=j[3];$s=-1;return l+1>>0;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.YearDay};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.YearDay=function(){return this.$val.YearDay();};BS.prototype.String=function(){var i,j,k,l,m,n,o,p;i=this;j=DX.zero();k=32;l=(new $Uint64(i.$high,i.$low));m=(i.$high<0||(i.$high===0&&i.$low<0));if(m){l=new $Uint64(-l.$high,-l.$low);}if((l.$high<0||(l.$high===0&&l.$low<1000000000))){n=0;k=k-(1)>>0;((k<0||k>=j.length)?($throwRuntimeError("index out of range"),undefined):j[k]=115);k=k-(1)>>0;if((l.$high===0&&l.$low===0)){return"0s";}else if((l.$high<0||(l.$high===0&&l.$low<1000))){n=0;((k<0||k>=j.length)?($throwRuntimeError("index out of range"),undefined):j[k]=110);}else if((l.$high<0||(l.$high===0&&l.$low<1000000))){n=3;k=k-(1)>>0;$copyString($subslice(new DR(j),k),"\xC2\xB5");}else{n=6;((k<0||k>=j.length)?($throwRuntimeError("index out of range"),undefined):j[k]=109);}o=BT($subslice(new DR(j),0,k),l,n);k=o[0];l=o[1];k=BU($subslice(new DR(j),0,k),l);}else{k=k-(1)>>0;((k<0||k>=j.length)?($throwRuntimeError("index out of range"),undefined):j[k]=115);p=BT($subslice(new DR(j),0,k),l,9);k=p[0];l=p[1];k=BU($subslice(new DR(j),0,k),$div64(l,new $Uint64(0,60),true));l=$div64(l,(new $Uint64(0,60)),false);if((l.$high>0||(l.$high===0&&l.$low>0))){k=k-(1)>>0;((k<0||k>=j.length)?($throwRuntimeError("index out of range"),undefined):j[k]=109);k=BU($subslice(new DR(j),0,k),$div64(l,new $Uint64(0,60),true));l=$div64(l,(new $Uint64(0,60)),false);if((l.$high>0||(l.$high===0&&l.$low>0))){k=k-(1)>>0;((k<0||k>=j.length)?($throwRuntimeError("index out of range"),undefined):j[k]=104);k=BU($subslice(new DR(j),0,k),l);}}}if(m){k=k-(1)>>0;((k<0||k>=j.length)?($throwRuntimeError("index out of range"),undefined):j[k]=45);}return($bytesToString($subslice(new DR(j),k)));};$ptrType(BS).prototype.String=function(){return this.$get().String();};BT=function(i,j,k){var i,j,k,l,m,n,o,p,q,r,s;l=0;m=new $Uint64(0,0);n=i.$length;o=false;p=0;while(true){if(!(p>0;((n<0||n>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+n]=(((q.$low<<24>>>24))+48<<24>>>24));}j=$div64(j,(new $Uint64(0,10)),false);p=p+(1)>>0;}if(o){n=n-(1)>>0;((n<0||n>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+n]=46);}r=n;s=j;l=r;m=s;return[l,m];};BU=function(i,j){var i,j,k;k=i.$length;if((j.$high===0&&j.$low===0)){k=k-(1)>>0;((k<0||k>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+k]=48);}else{while(true){if(!((j.$high>0||(j.$high===0&&j.$low>0)))){break;}k=k-(1)>>0;((k<0||k>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+k]=((($div64(j,new $Uint64(0,10),true).$low<<24>>>24))+48<<24>>>24));j=$div64(j,(new $Uint64(0,10)),false);}}return k;};BS.prototype.Nanoseconds=function(){var i;i=this;return(new $Int64(i.$high,i.$low));};$ptrType(BS).prototype.Nanoseconds=function(){return this.$get().Nanoseconds();};BS.prototype.Seconds=function(){var i,j,k;i=this;j=$div64(i,new BS(0,1000000000),false);k=$div64(i,new BS(0,1000000000),true);return($flatten64(j))+($flatten64(k))/1e+09;};$ptrType(BS).prototype.Seconds=function(){return this.$get().Seconds();};BS.prototype.Minutes=function(){var i,j,k;i=this;j=$div64(i,new BS(13,4165425152),false);k=$div64(i,new BS(13,4165425152),true);return($flatten64(j))+($flatten64(k))/6e+10;};$ptrType(BS).prototype.Minutes=function(){return this.$get().Minutes();};BS.prototype.Hours=function(){var i,j,k;i=this;j=$div64(i,new BS(838,817405952),false);k=$div64(i,new BS(838,817405952),true);return($flatten64(j))+($flatten64(k))/3.6e+12;};$ptrType(BS).prototype.Hours=function(){return this.$get().Hours();};BS.prototype.Truncate=function(i){var i,j,k;j=this;if((i.$high<0||(i.$high===0&&i.$low<=0))){return j;}return(k=$div64(j,i,true),new BS(j.$high-k.$high,j.$low-k.$low));};$ptrType(BS).prototype.Truncate=function(i){return this.$get().Truncate(i);};BV=function(i,j){var i,j,k,l,m,n;return(k=(l=(new $Uint64(i.$high,i.$low)),m=(new $Uint64(i.$high,i.$low)),new $Uint64(l.$high+m.$high,l.$low+m.$low)),n=(new $Uint64(j.$high,j.$low)),(k.$highj.$high||(o.$high===j.$high&&o.$low>j.$low))){return o;}return new BS(2147483647,4294967295);};$ptrType(BS).prototype.Round=function(i){return this.$get().Round(i);};BL.ptr.prototype.Add=function(i){var i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;j=this;l=((k=$div64(i,new BS(0,1000000000),false),new $Int64(k.$high,k.$low)));n=j.nsec()+(((m=$div64(i,new BS(0,1000000000),true),m.$low+((m.$high>>31)*4294967296))>>0))>>0;if(n>=1000000000){l=(o=new $Int64(0,1),new $Int64(l.$high+o.$high,l.$low+o.$low));n=n-(1000000000)>>0;}else if(n<0){l=(p=new $Int64(0,1),new $Int64(l.$high-p.$high,l.$low-p.$low));n=n+(1000000000)>>0;}j.wall=(q=(r=j.wall,new $Uint64(r.$high&~0,(r.$low&~1073741823)>>>0)),s=(new $Uint64(0,n)),new $Uint64(q.$high|s.$high,(q.$low|s.$low)>>>0));j.addSec(l);if(!((t=(u=j.wall,new $Uint64(u.$high&2147483648,(u.$low&0)>>>0)),(t.$high===0&&t.$low===0)))){x=(v=j.ext,w=(new $Int64(i.$high,i.$low)),new $Int64(v.$high+w.$high,v.$low+w.$low));if((i.$high<0||(i.$high===0&&i.$low<0))&&(y=j.ext,(x.$high>y.$high||(x.$high===y.$high&&x.$low>y.$low)))||(i.$high>0||(i.$high===0&&i.$low>0))&&(z=j.ext,(x.$high>>0)),new $Uint64(l.$high&2147483648,(l.$low&0)>>>0)),(k.$high===0&&k.$low===0)))){o=j.ext;p=i.ext;r=((q=new $Int64(o.$high-p.$high,o.$low-p.$low),new BS(q.$high,q.$low)));if((r.$high<0||(r.$high===0&&r.$low<0))&&(o.$high>p.$high||(o.$high===p.$high&&o.$low>p.$low))){return new BS(2147483647,4294967295);}if((r.$high>0||(r.$high===0&&r.$low>0))&&(o.$high>0))),new BS(s.$high+w.$high,s.$low+w.$low));if($clone($clone(i,BL).Add(x),BL).Equal($clone(j,BL))){return x;}else if($clone(j,BL).Before($clone(i,BL))){return new BS(-2147483648,0);}else{return new BS(2147483647,4294967295);}};BL.prototype.Sub=function(i){return this.$val.Sub(i);};BX=function(i){var i,j,k,l,m;j=new BL.ptr(new $Uint64(0,0),new $Int64(0,0),DU.nil);if(!((k=(l=i.wall,new $Uint64(l.$high&2147483648,(l.$low&0)>>>0)),(k.$high===0&&k.$low===0)))){BL.copy(j,new BL.ptr(new $Uint64(2147483648,0),(m=I(),new $Int64(m.$high-CB.$high,m.$low-CB.$low)),DU.nil));}else{BL.copy(j,CC());}return $clone(i,BL).Sub($clone(j,BL));};$pkg.Until=BX;BL.ptr.prototype.AddDate=function(i,j,k){var i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;n=$clone(l,BL).Date();$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m[0];p=m[1];q=m[2];s=$clone(l,BL).Clock();$s=2;case 2:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}r=s;t=r[0];u=r[1];v=r[2];w=CH(o+i>>0,p+((j>>0))>>0,q+k>>0,t,u,v,((l.nsec()>>0)),$clone(l,BL).Location());$s=3;case 3:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}$s=-1;return w;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.AddDate};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.AddDate=function(i,j,k){return this.$val.AddDate(i,j,k);};BL.ptr.prototype.date=function(i){var i,j,k,l,m,n,o,p,q,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:j=0;k=0;l=0;m=0;n=this;p=$clone(n,BL).abs();$s=1;case 1:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=BY(p,i);$s=2;case 2:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}o=q;j=o[0];k=o[1];l=o[2];m=o[3];$s=-1;return[j,k,l,m];}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.date};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.date=function(i){return this.$val.date(i);};BY=function(i,j){var aa,ab,ac,ad,ae,af,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;k=0;l=0;m=0;n=0;o=$div64(i,new $Uint64(0,86400),false);p=$div64(o,new $Uint64(0,146097),false);q=$mul64(new $Uint64(0,400),p);o=(r=$mul64(new $Uint64(0,146097),p),new $Uint64(o.$high-r.$high,o.$low-r.$low));p=$div64(o,new $Uint64(0,36524),false);p=(s=$shiftRightUint64(p,2),new $Uint64(p.$high-s.$high,p.$low-s.$low));q=(t=$mul64(new $Uint64(0,100),p),new $Uint64(q.$high+t.$high,q.$low+t.$low));o=(u=$mul64(new $Uint64(0,36524),p),new $Uint64(o.$high-u.$high,o.$low-u.$low));p=$div64(o,new $Uint64(0,1461),false);q=(v=$mul64(new $Uint64(0,4),p),new $Uint64(q.$high+v.$high,q.$low+v.$low));o=(w=$mul64(new $Uint64(0,1461),p),new $Uint64(o.$high-w.$high,o.$low-w.$low));p=$div64(o,new $Uint64(0,365),false);p=(x=$shiftRightUint64(p,2),new $Uint64(p.$high-x.$high,p.$low-x.$low));q=(y=p,new $Uint64(q.$high+y.$high,q.$low+y.$low));o=(z=$mul64(new $Uint64(0,365),p),new $Uint64(o.$high-z.$high,o.$low-z.$low));k=(((aa=(ab=(new $Int64(q.$high,q.$low)),new $Int64(ab.$high+-69,ab.$low+4075721025)),aa.$low+((aa.$high>>31)*4294967296))>>0));n=((o.$low>>0));if(!j){return[k,l,m,n];}m=n;if(CF(k)){if(m>59){m=m-(1)>>0;}else if((m===59)){l=2;m=29;return[k,l,m,n];}}l=(((ac=m/31,(ac===ac&&ac!==1/0&&ac!==-1/0)?ac>>0:$throwRuntimeError("integer divide by zero"))>>0));ae=(((ad=l+1>>0,((ad<0||ad>=BZ.length)?($throwRuntimeError("index out of range"),undefined):BZ[ad]))>>0));af=0;if(m>=ae){l=l+(1)>>0;af=ae;}else{af=((((l<0||l>=BZ.length)?($throwRuntimeError("index out of range"),undefined):BZ[l])>>0));}l=l+(1)>>0;m=(m-af>>0)+1>>0;return[k,l,m,n];};CA=function(i,j){var i,j,k;if((i===2)&&CF(j)){return 29;}return(((((i<0||i>=BZ.length)?($throwRuntimeError("index out of range"),undefined):BZ[i])-(k=i-1>>0,((k<0||k>=BZ.length)?($throwRuntimeError("index out of range"),undefined):BZ[k]))>>0)>>0));};CC=function(){var i,j,k,l,m,n,o,p,q,r;i=J();j=i[0];k=i[1];l=i[2];l=(m=CB,new $Int64(l.$high-m.$high,l.$low-m.$low));j=(n=new $Int64(0,2682288000),new $Int64(j.$high+n.$high,j.$low+n.$low));if(!((o=$shiftRightUint64((new $Uint64(j.$high,j.$low)),33),(o.$high===0&&o.$low===0)))){return new BL.ptr((new $Uint64(0,k)),new $Int64(j.$high+13,j.$low+3618733952),$pkg.Local);}return new BL.ptr((p=(q=$shiftLeft64((new $Uint64(j.$high,j.$low)),30),new $Uint64(2147483648|q.$high,(0|q.$low)>>>0)),r=(new $Uint64(0,k)),new $Uint64(p.$high|r.$high,(p.$low|r.$low)>>>0)),l,$pkg.Local);};$pkg.Now=CC;CD=function(i,j){var i,j;return new BL.ptr((new $Uint64(0,j)),new $Int64(i.$high+14,i.$low+2006054656),$pkg.Local);};BL.ptr.prototype.UTC=function(){var i;i=this;i.setLoc(CM);return i;};BL.prototype.UTC=function(){return this.$val.UTC();};BL.ptr.prototype.Local=function(){var i;i=this;i.setLoc($pkg.Local);return i;};BL.prototype.Local=function(){return this.$val.Local();};BL.ptr.prototype.In=function(i){var i,j;j=this;if(i===DU.nil){$panic(new $String("time: missing Location in call to Time.In"));}j.setLoc(i);return j;};BL.prototype.In=function(i){return this.$val.In(i);};BL.ptr.prototype.Location=function(){var i,j;i=this;j=i.loc;if(j===DU.nil){j=$pkg.UTC;}return j;};BL.prototype.Location=function(){return this.$val.Location();};BL.ptr.prototype.Zone=function(){var i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i="";j=0;k=this;m=k.loc.lookup(k.unixSec());$s=1;case 1:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;i=l[0];j=l[1];$s=-1;return[i,j];}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.Zone};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.Zone=function(){return this.$val.Zone();};BL.ptr.prototype.Unix=function(){var i;i=this;return i.unixSec();};BL.prototype.Unix=function(){return this.$val.Unix();};BL.ptr.prototype.UnixNano=function(){var i,j,k;i=this;return(j=$mul64((i.unixSec()),new $Int64(0,1000000000)),k=(new $Int64(0,i.nsec())),new $Int64(j.$high+k.$high,j.$low+k.$low));};BL.prototype.UnixNano=function(){return this.$val.UnixNano();};BL.ptr.prototype.MarshalBinary=function(){var i,j,k,l,m,n,o,p,q,r,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;j=0;if($clone(i,BL).Location()===$pkg.UTC){$s=1;continue;}$s=2;continue;case 1:j=-1;$s=3;continue;case 2:l=$clone(i,BL).Zone();$s=4;case 4:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}k=l;m=k[1];if(!(((n=m%60,n===n?n:$throwRuntimeError("integer divide by zero"))===0))){$s=-1;return[DR.nil,C.New("Time.MarshalBinary: zone offset has fractional minute")];}m=(o=m/(60),(o===o&&o!==1/0&&o!==-1/0)?o>>0:$throwRuntimeError("integer divide by zero"));if(m<-32768||(m===-1)||m>32767){$s=-1;return[DR.nil,C.New("Time.MarshalBinary: unexpected zone offset")];}j=((m<<16>>16));case 3:p=i.sec();q=i.nsec();r=new DR([1,(($shiftRightInt64(p,56).$low<<24>>>24)),(($shiftRightInt64(p,48).$low<<24>>>24)),(($shiftRightInt64(p,40).$low<<24>>>24)),(($shiftRightInt64(p,32).$low<<24>>>24)),(($shiftRightInt64(p,24).$low<<24>>>24)),(($shiftRightInt64(p,16).$low<<24>>>24)),(($shiftRightInt64(p,8).$low<<24>>>24)),((p.$low<<24>>>24)),(((q>>24>>0)<<24>>>24)),(((q>>16>>0)<<24>>>24)),(((q>>8>>0)<<24>>>24)),((q<<24>>>24)),(((j>>8<<16>>16)<<24>>>24)),((j<<24>>>24))]);$s=-1;return[r,$ifaceNil];}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.MarshalBinary};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.MarshalBinary=function(){return this.$val.MarshalBinary();};BL.ptr.prototype.UnmarshalBinary=function(i){var aa,ab,ac,ad,ae,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:j=this;k=i;if(k.$length===0){$s=-1;return C.New("Time.UnmarshalBinary: no data");}if(!(((0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0])===1))){$s=-1;return C.New("Time.UnmarshalBinary: unsupported version");}if(!((k.$length===15))){$s=-1;return C.New("Time.UnmarshalBinary: invalid length");}k=$subslice(k,1);z=(l=(m=(n=(o=(p=(q=(r=(new $Int64(0,(7>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+7]))),s=$shiftLeft64((new $Int64(0,(6>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+6]))),8),new $Int64(r.$high|s.$high,(r.$low|s.$low)>>>0)),t=$shiftLeft64((new $Int64(0,(5>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+5]))),16),new $Int64(q.$high|t.$high,(q.$low|t.$low)>>>0)),u=$shiftLeft64((new $Int64(0,(4>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+4]))),24),new $Int64(p.$high|u.$high,(p.$low|u.$low)>>>0)),v=$shiftLeft64((new $Int64(0,(3>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+3]))),32),new $Int64(o.$high|v.$high,(o.$low|v.$low)>>>0)),w=$shiftLeft64((new $Int64(0,(2>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+2]))),40),new $Int64(n.$high|w.$high,(n.$low|w.$low)>>>0)),x=$shiftLeft64((new $Int64(0,(1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]))),48),new $Int64(m.$high|x.$high,(m.$low|x.$low)>>>0)),y=$shiftLeft64((new $Int64(0,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]))),56),new $Int64(l.$high|y.$high,(l.$low|y.$low)>>>0));k=$subslice(k,8);aa=(((((3>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+3])>>0))|((((2>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+2])>>0))<<8>>0))|((((1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1])>>0))<<16>>0))|((((0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0])>>0))<<24>>0);k=$subslice(k,4);ab=$imul(((((((1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1])<<16>>16))|((((0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0])<<16>>16))<<8<<16>>16))>>0)),60);BL.copy(j,new BL.ptr(new $Uint64(0,0),new $Int64(0,0),DU.nil));j.wall=(new $Uint64(0,aa));j.ext=z;if(ab===-60){$s=1;continue;}$s=2;continue;case 1:j.setLoc(CM);$s=3;continue;case 2:ad=$pkg.Local.lookup(j.unixSec());$s=4;case 4:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}ac=ad;ae=ac[1];if(ab===ae){j.setLoc($pkg.Local);}else{j.setLoc(CP("",ab));}case 3:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.UnmarshalBinary};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.UnmarshalBinary=function(i){return this.$val.UnmarshalBinary(i);};BL.ptr.prototype.GobEncode=function(){var i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;j=$clone(i,BL).MarshalBinary();$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}$s=-1;return j;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.GobEncode};}$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.GobEncode=function(){return this.$val.GobEncode();};BL.ptr.prototype.GobDecode=function(i){var i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:j=this;k=j.UnmarshalBinary(i);$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}$s=-1;return k;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.GobDecode};}$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.GobDecode=function(i){return this.$val.GobDecode(i);};BL.ptr.prototype.MarshalJSON=function(){var i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;j=$clone(i,BL).Year();$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}k=j;if(k<0||k>=10000){$s=-1;return[DR.nil,C.New("Time.MarshalJSON: year outside of range [0,9999]")];}l=$makeSlice(DR,0,37);l=$append(l,34);m=$clone(i,BL).AppendFormat(l,"2006-01-02T15:04:05.999999999Z07:00");$s=2;case 2:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;l=$append(l,34);$s=-1;return[l,$ifaceNil];}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.MarshalJSON};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.MarshalJSON=function(){return this.$val.MarshalJSON();};BL.ptr.prototype.UnmarshalJSON=function(i){var i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:j=this;if(($bytesToString(i))==="null"){$s=-1;return $ifaceNil;}k=$ifaceNil;m=AK("\"2006-01-02T15:04:05Z07:00\"",($bytesToString(i)));$s=1;case 1:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;BL.copy(j,l[0]);k=l[1];$s=-1;return k;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.UnmarshalJSON};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.UnmarshalJSON=function(i){return this.$val.UnmarshalJSON(i);};BL.ptr.prototype.MarshalText=function(){var i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;j=$clone(i,BL).Year();$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}k=j;if(k<0||k>=10000){$s=-1;return[DR.nil,C.New("Time.MarshalText: year outside of range [0,9999]")];}l=$makeSlice(DR,0,35);m=$clone(i,BL).AppendFormat(l,"2006-01-02T15:04:05.999999999Z07:00");$s=2;case 2:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}$s=-1;return[m,$ifaceNil];}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.MarshalText};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.MarshalText=function(){return this.$val.MarshalText();};BL.ptr.prototype.UnmarshalText=function(i){var i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:j=this;k=$ifaceNil;m=AK("2006-01-02T15:04:05Z07:00",($bytesToString(i)));$s=1;case 1:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;BL.copy(j,l[0]);k=l[1];$s=-1;return k;}return;}if($f===undefined){$f={$blk:BL.ptr.prototype.UnmarshalText};}$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};BL.prototype.UnmarshalText=function(i){return this.$val.UnmarshalText(i);};CE=function(i,j){var i,j,k,l,m,n,o;if((j.$high<0||(j.$high===0&&j.$low<0))||(j.$high>0||(j.$high===0&&j.$low>=1000000000))){k=$div64(j,new $Int64(0,1000000000),false);i=(l=k,new $Int64(i.$high+l.$high,i.$low+l.$low));j=(m=$mul64(k,new $Int64(0,1000000000)),new $Int64(j.$high-m.$high,j.$low-m.$low));if((j.$high<0||(j.$high===0&&j.$low<0))){j=(n=new $Int64(0,1000000000),new $Int64(j.$high+n.$high,j.$low+n.$low));i=(o=new $Int64(0,1),new $Int64(i.$high-o.$high,i.$low-o.$low));}}return CD(i,(((j.$low+((j.$high>>31)*4294967296))>>0)));};$pkg.Unix=CE;CF=function(i){var i,j,k,l;return((j=i%4,j===j?j:$throwRuntimeError("integer divide by zero"))===0)&&(!(((k=i%100,k===k?k:$throwRuntimeError("integer divide by zero"))===0))||((l=i%400,l===l?l:$throwRuntimeError("integer divide by zero"))===0));};CG=function(i,j,k){var i,j,k,l,m,n,o,p,q,r,s;l=0;m=0;if(j<0){o=(n=((-j-1>>0))/k,(n===n&&n!==1/0&&n!==-1/0)?n>>0:$throwRuntimeError("integer divide by zero"))+1>>0;i=i-(o)>>0;j=j+(($imul(o,k)))>>0;}if(j>=k){q=(p=j/k,(p===p&&p!==1/0&&p!==-1/0)?p>>0:$throwRuntimeError("integer divide by zero"));i=i+(q)>>0;j=j-(($imul(q,k)))>>0;}r=i;s=j;l=r;m=s;return[l,m];};CH=function(i,j,k,l,m,n,o,p){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(p===DU.nil){$panic(new $String("time: missing Location in call to Date"));}q=((j>>0))-1>>0;r=CG(i,q,12);i=r[0];q=r[1];j=((q>>0))+1>>0;s=CG(n,o,1000000000);n=s[0];o=s[1];t=CG(m,n,60);m=t[0];n=t[1];u=CG(l,m,60);l=u[0];m=u[1];v=CG(k,l,24);k=v[0];l=v[1];y=((w=(x=(new $Int64(0,i)),new $Int64(x.$high- -69,x.$low-4075721025)),new $Uint64(w.$high,w.$low)));z=$div64(y,new $Uint64(0,400),false);y=(aa=$mul64(new $Uint64(0,400),z),new $Uint64(y.$high-aa.$high,y.$low-aa.$low));ab=$mul64(new $Uint64(0,146097),z);z=$div64(y,new $Uint64(0,100),false);y=(ac=$mul64(new $Uint64(0,100),z),new $Uint64(y.$high-ac.$high,y.$low-ac.$low));ab=(ad=$mul64(new $Uint64(0,36524),z),new $Uint64(ab.$high+ad.$high,ab.$low+ad.$low));z=$div64(y,new $Uint64(0,4),false);y=(ae=$mul64(new $Uint64(0,4),z),new $Uint64(y.$high-ae.$high,y.$low-ae.$low));ab=(af=$mul64(new $Uint64(0,1461),z),new $Uint64(ab.$high+af.$high,ab.$low+af.$low));z=y;ab=(ag=$mul64(new $Uint64(0,365),z),new $Uint64(ab.$high+ag.$high,ab.$low+ag.$low));ab=(ah=(new $Uint64(0,(ai=j-1>>0,((ai<0||ai>=BZ.length)?($throwRuntimeError("index out of range"),undefined):BZ[ai])))),new $Uint64(ab.$high+ah.$high,ab.$low+ah.$low));if(CF(i)&&j>=3){ab=(aj=new $Uint64(0,1),new $Uint64(ab.$high+aj.$high,ab.$low+aj.$low));}ab=(ak=(new $Uint64(0,(k-1>>0))),new $Uint64(ab.$high+ak.$high,ab.$low+ak.$low));al=$mul64(ab,new $Uint64(0,86400));al=(am=(new $Uint64(0,((($imul(l,3600))+($imul(m,60))>>0)+n>>0))),new $Uint64(al.$high+am.$high,al.$low+am.$low));ao=(an=(new $Int64(al.$high,al.$low)),new $Int64(an.$high+-2147483647,an.$low+3844486912));aq=p.lookup(ao);$s=1;case 1:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ap=aq;ar=ap[1];as=ap[2];at=ap[3];if(!((ar===0))){$s=2;continue;}$s=3;continue;case 2:av=(au=(new $Int64(0,ar)),new $Int64(ao.$high-au.$high,ao.$low-au.$low));if((av.$highat.$high||(av.$high===at.$high&&av.$low>=at.$low))){$s=6;continue;}$s=7;continue;case 5:ax=p.lookup(new $Int64(as.$high-0,as.$low-1));$s=8;case 8:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}aw=ax;ar=aw[1];$s=7;continue;case 6:az=p.lookup(at);$s=9;case 9:if($c){$c=false;az=az.$blk();}if(az&&az.$blk!==undefined){break s;}ay=az;ar=ay[1];case 7:case 4:ao=(ba=(new $Int64(0,ar)),new $Int64(ao.$high-ba.$high,ao.$low-ba.$low));case 3:bb=$clone(CD(ao,((o>>0))),BL);bb.setLoc(p);$s=-1;return bb;}return;}if($f===undefined){$f={$blk:CH};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Date=CH;BL.ptr.prototype.Truncate=function(i){var i,j,k,l;j=this;j.stripMono();if((i.$high<0||(i.$high===0&&i.$low<=0))){return j;}k=CI($clone(j,BL),i);l=k[1];return $clone(j,BL).Add(new BS(-l.$high,-l.$low));};BL.prototype.Truncate=function(i){return this.$val.Truncate(i);};BL.ptr.prototype.Round=function(i){var i,j,k,l;j=this;j.stripMono();if((i.$high<0||(i.$high===0&&i.$low<=0))){return j;}k=CI($clone(j,BL),i);l=k[1];if(BV(l,i)){return $clone(j,BL).Add(new BS(-l.$high,-l.$low));}return $clone(j,BL).Add(new BS(i.$high-l.$high,i.$low-l.$low));};BL.prototype.Round=function(i){return this.$val.Round(i);};CI=function(i,j){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;k=0;l=new BS(0,0);m=false;n=i.nsec();o=i.sec();if((o.$high<0||(o.$high===0&&o.$low<0))){m=true;o=new $Int64(-o.$high,-o.$low);n=-n;if(n<0){n=n+(1000000000)>>0;o=(p=new $Int64(0,1),new $Int64(o.$high-p.$high,o.$low-p.$low));}}if((j.$high<0||(j.$high===0&&j.$low<1000000000))&&(q=$div64(new BS(0,1000000000),(new BS(j.$high+j.$high,j.$low+j.$low)),true),(q.$high===0&&q.$low===0))){k=(((s=n/(((j.$low+((j.$high>>31)*4294967296))>>0)),(s===s&&s!==1/0&&s!==-1/0)?s>>0:$throwRuntimeError("integer divide by zero"))>>0))&1;l=(new BS(0,(t=n%(((j.$low+((j.$high>>31)*4294967296))>>0)),t===t?t:$throwRuntimeError("integer divide by zero"))));}else if((r=$div64(j,new BS(0,1000000000),true),(r.$high===0&&r.$low===0))){v=((u=$div64(j,new BS(0,1000000000),false),new $Int64(u.$high,u.$low)));k=(((w=$div64(o,v,false),w.$low+((w.$high>>31)*4294967296))>>0))&1;l=(x=$mul64(((y=$div64(o,v,true),new BS(y.$high,y.$low))),new BS(0,1000000000)),z=(new BS(0,n)),new BS(x.$high+z.$high,x.$low+z.$low));}else{aa=(new $Uint64(o.$high,o.$low));ab=$mul64(($shiftRightUint64(aa,32)),new $Uint64(0,1000000000));ac=$shiftRightUint64(ab,32);ad=$shiftLeft64(ab,32);ab=$mul64((new $Uint64(aa.$high&0,(aa.$low&4294967295)>>>0)),new $Uint64(0,1000000000));ae=ad;af=new $Uint64(ad.$high+ab.$high,ad.$low+ab.$low);ag=ae;ad=af;if((ad.$higham.$high||(ac.$high===am.$high&&ac.$low>am.$low))||(ac.$high===am.$high&&ac.$low===am.$low)&&(ad.$high>ao.$high||(ad.$high===ao.$high&&ad.$low>=ao.$low))){k=1;ap=ad;aq=new $Uint64(ad.$high-ao.$high,ad.$low-ao.$low);ag=ap;ad=aq;if((ad.$high>ag.$high||(ad.$high===ag.$high&&ad.$low>ag.$low))){ac=(ar=new $Uint64(0,1),new $Uint64(ac.$high-ar.$high,ac.$low-ar.$low));}ac=(as=am,new $Uint64(ac.$high-as.$high,ac.$low-as.$low));}if((am.$high===0&&am.$low===0)&&(at=(new $Uint64(j.$high,j.$low)),(ao.$high===at.$high&&ao.$low===at.$low))){break;}ao=$shiftRightUint64(ao,(1));ao=(au=$shiftLeft64((new $Uint64(am.$high&0,(am.$low&1)>>>0)),63),new $Uint64(ao.$high|au.$high,(ao.$low|au.$low)>>>0));am=$shiftRightUint64(am,(1));}l=(new BS(ad.$high,ad.$low));}if(m&&!((l.$high===0&&l.$low===0))){k=(k^(1))>>0;l=new BS(j.$high-l.$high,j.$low-l.$low);}return[k,l];};CJ.ptr.prototype.get=function(){var i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;if(i===DU.nil){$s=-1;return CM;}if(i===CN){$s=1;continue;}$s=2;continue;case 1:$r=CO.Do(H);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 2:$s=-1;return i;}return;}if($f===undefined){$f={$blk:CJ.ptr.prototype.get};}$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};CJ.prototype.get=function(){return this.$val.get();};CJ.ptr.prototype.String=function(){var i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:i=this;j=i.get();$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}$s=-1;return j.name;}return;}if($f===undefined){$f={$blk:CJ.ptr.prototype.String};}$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};CJ.prototype.String=function(){return this.$val.String();};CP=function(i,j){var i,j,k,l;k=new CJ.ptr(i,new DK([new CK.ptr(i,j,false)]),new DL([new CL.ptr(new $Int64(-2147483648,0),0,false,false)]),new $Int64(-2147483648,0),new $Int64(2147483647,4294967295),DM.nil);k.cacheZone=(l=k.zone,(0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0]));return k;};$pkg.FixedZone=CP;CJ.ptr.prototype.lookup=function(i){var aa,ab,ac,ad,ae,af,ag,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:j="";k=0;l=new $Int64(0,0);m=new $Int64(0,0);n=this;o=n.get();$s=1;case 1:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;if(n.zone.$length===0){j="UTC";k=0;l=new $Int64(-2147483648,0);m=new $Int64(2147483647,4294967295);$s=-1;return[j,k,l,m];}p=n.cacheZone;if(!(p===DM.nil)&&(q=n.cacheStart,(q.$high=t.$length?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+0])).when,(i.$high=u.$length)?($throwRuntimeError("index out of range"),undefined):u.$array[u.$offset+v]));j=w.name;k=w.offset;l=new $Int64(-2147483648,0);if(n.tx.$length>0){m=(x=n.tx,(0>=x.$length?($throwRuntimeError("index out of range"),undefined):x.$array[x.$offset+0])).when;}else{m=new $Int64(2147483647,4294967295);}$s=-1;return[j,k,l,m];}y=n.tx;m=new $Int64(2147483647,4294967295);z=0;aa=y.$length;while(true){if(!((aa-z>>0)>1)){break;}ac=z+(ab=((aa-z>>0))/2,(ab===ab&&ab!==1/0&&ab!==-1/0)?ab>>0:$throwRuntimeError("integer divide by zero"))>>0;ad=((ac<0||ac>=y.$length)?($throwRuntimeError("index out of range"),undefined):y.$array[y.$offset+ac]).when;if((i.$high=y.$length)?($throwRuntimeError("index out of range"),undefined):y.$array[y.$offset+z]).index,((af<0||af>=ae.$length)?($throwRuntimeError("index out of range"),undefined):ae.$array[ae.$offset+af]));j=ag.name;k=ag.offset;l=((z<0||z>=y.$length)?($throwRuntimeError("index out of range"),undefined):y.$array[y.$offset+z]).when;$s=-1;return[j,k,l,m];}return;}if($f===undefined){$f={$blk:CJ.ptr.prototype.lookup};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};CJ.prototype.lookup=function(i){return this.$val.lookup(i);};CJ.ptr.prototype.lookupFirstZone=function(){var i,j,k,l,m,n,o,p,q,r,s;i=this;if(!i.firstZoneUsed()){return 0;}if(i.tx.$length>0&&(j=i.zone,k=(l=i.tx,(0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])).index,((k<0||k>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+k])).isDST){n=(((m=i.tx,(0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0])).index>>0))-1>>0;while(true){if(!(n>=0)){break;}if(!(o=i.zone,((n<0||n>=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+n])).isDST){return n;}n=n-(1)>>0;}}p=i.zone;q=0;while(true){if(!(q=s.$length)?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+r])).isDST){return r;}q++;}return 0;};CJ.prototype.lookupFirstZone=function(){return this.$val.lookupFirstZone();};CJ.ptr.prototype.firstZoneUsed=function(){var i,j,k,l;i=this;j=i.tx;k=0;while(true){if(!(k=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+k]),CL);if(l.index===0){return true;}k++;}return false;};CJ.prototype.firstZoneUsed=function(){return this.$val.firstZoneUsed();};CJ.ptr.prototype.lookupName=function(i,j){var aa,ab,ac,ad,ae,af,ag,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:k=0;l=false;m=this;n=m.get();$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m.zone;p=0;case 2:if(!(p=r.$length)?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+q]));if(s.name===i){$s=4;continue;}$s=5;continue;case 4:v=m.lookup((u=(new $Int64(0,s.offset)),new $Int64(j.$high-u.$high,j.$low-u.$low)));$s=6;case 6:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}t=v;w=t[0];x=t[1];if(w===s.name){y=x;z=true;k=y;l=z;$s=-1;return[k,l];}case 5:p++;$s=2;continue;case 3:aa=m.zone;ab=0;while(true){if(!(ab=ad.$length)?($throwRuntimeError("index out of range"),undefined):ad.$array[ad.$offset+ac]));if(ae.name===i){af=ae.offset;ag=true;k=af;l=ag;$s=-1;return[k,l];}ab++;}$s=-1;return[k,l];}return;}if($f===undefined){$f={$blk:CJ.ptr.prototype.lookupName};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};CJ.prototype.lookupName=function(i,j){return this.$val.lookupName(i,j);};EC.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];ED.methods=[{prop:"Stop",name:"Stop",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Reset",name:"Reset",pkg:"",typ:$funcType([BS],[$Bool],false)}];EF.methods=[{prop:"Stop",name:"Stop",pkg:"",typ:$funcType([],[],false)}];BL.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"Format",name:"Format",pkg:"",typ:$funcType([$String],[$String],false)},{prop:"AppendFormat",name:"AppendFormat",pkg:"",typ:$funcType([DR,$String],[DR],false)},{prop:"After",name:"After",pkg:"",typ:$funcType([BL],[$Bool],false)},{prop:"Before",name:"Before",pkg:"",typ:$funcType([BL],[$Bool],false)},{prop:"Equal",name:"Equal",pkg:"",typ:$funcType([BL],[$Bool],false)},{prop:"IsZero",name:"IsZero",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"abs",name:"abs",pkg:"time",typ:$funcType([],[$Uint64],false)},{prop:"locabs",name:"locabs",pkg:"time",typ:$funcType([],[$String,$Int,$Uint64],false)},{prop:"Date",name:"Date",pkg:"",typ:$funcType([],[$Int,BM,$Int],false)},{prop:"Year",name:"Year",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Month",name:"Month",pkg:"",typ:$funcType([],[BM],false)},{prop:"Day",name:"Day",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Weekday",name:"Weekday",pkg:"",typ:$funcType([],[BO],false)},{prop:"ISOWeek",name:"ISOWeek",pkg:"",typ:$funcType([],[$Int,$Int],false)},{prop:"Clock",name:"Clock",pkg:"",typ:$funcType([],[$Int,$Int,$Int],false)},{prop:"Hour",name:"Hour",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Minute",name:"Minute",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Second",name:"Second",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Nanosecond",name:"Nanosecond",pkg:"",typ:$funcType([],[$Int],false)},{prop:"YearDay",name:"YearDay",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Add",name:"Add",pkg:"",typ:$funcType([BS],[BL],false)},{prop:"Sub",name:"Sub",pkg:"",typ:$funcType([BL],[BS],false)},{prop:"AddDate",name:"AddDate",pkg:"",typ:$funcType([$Int,$Int,$Int],[BL],false)},{prop:"date",name:"date",pkg:"time",typ:$funcType([$Bool],[$Int,BM,$Int,$Int],false)},{prop:"UTC",name:"UTC",pkg:"",typ:$funcType([],[BL],false)},{prop:"Local",name:"Local",pkg:"",typ:$funcType([],[BL],false)},{prop:"In",name:"In",pkg:"",typ:$funcType([DU],[BL],false)},{prop:"Location",name:"Location",pkg:"",typ:$funcType([],[DU],false)},{prop:"Zone",name:"Zone",pkg:"",typ:$funcType([],[$String,$Int],false)},{prop:"Unix",name:"Unix",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"UnixNano",name:"UnixNano",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"MarshalBinary",name:"MarshalBinary",pkg:"",typ:$funcType([],[DR,$error],false)},{prop:"GobEncode",name:"GobEncode",pkg:"",typ:$funcType([],[DR,$error],false)},{prop:"MarshalJSON",name:"MarshalJSON",pkg:"",typ:$funcType([],[DR,$error],false)},{prop:"MarshalText",name:"MarshalText",pkg:"",typ:$funcType([],[DR,$error],false)},{prop:"Truncate",name:"Truncate",pkg:"",typ:$funcType([BS],[BL],false)},{prop:"Round",name:"Round",pkg:"",typ:$funcType([BS],[BL],false)}];EG.methods=[{prop:"nsec",name:"nsec",pkg:"time",typ:$funcType([],[$Int32],false)},{prop:"sec",name:"sec",pkg:"time",typ:$funcType([],[$Int64],false)},{prop:"unixSec",name:"unixSec",pkg:"time",typ:$funcType([],[$Int64],false)},{prop:"addSec",name:"addSec",pkg:"time",typ:$funcType([$Int64],[],false)},{prop:"setLoc",name:"setLoc",pkg:"time",typ:$funcType([DU],[],false)},{prop:"stripMono",name:"stripMono",pkg:"time",typ:$funcType([],[],false)},{prop:"setMono",name:"setMono",pkg:"time",typ:$funcType([$Int64],[],false)},{prop:"mono",name:"mono",pkg:"time",typ:$funcType([],[$Int64],false)},{prop:"UnmarshalBinary",name:"UnmarshalBinary",pkg:"",typ:$funcType([DR],[$error],false)},{prop:"GobDecode",name:"GobDecode",pkg:"",typ:$funcType([DR],[$error],false)},{prop:"UnmarshalJSON",name:"UnmarshalJSON",pkg:"",typ:$funcType([DR],[$error],false)},{prop:"UnmarshalText",name:"UnmarshalText",pkg:"",typ:$funcType([DR],[$error],false)}];BM.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];BO.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];BS.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"Nanoseconds",name:"Nanoseconds",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Seconds",name:"Seconds",pkg:"",typ:$funcType([],[$Float64],false)},{prop:"Minutes",name:"Minutes",pkg:"",typ:$funcType([],[$Float64],false)},{prop:"Hours",name:"Hours",pkg:"",typ:$funcType([],[$Float64],false)},{prop:"Truncate",name:"Truncate",pkg:"",typ:$funcType([BS],[BS],false)},{prop:"Round",name:"Round",pkg:"",typ:$funcType([BS],[BS],false)}];DU.methods=[{prop:"get",name:"get",pkg:"time",typ:$funcType([],[DU],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"lookup",name:"lookup",pkg:"time",typ:$funcType([$Int64],[$String,$Int,$Int64,$Int64],false)},{prop:"lookupFirstZone",name:"lookupFirstZone",pkg:"time",typ:$funcType([],[$Int],false)},{prop:"firstZoneUsed",name:"firstZoneUsed",pkg:"time",typ:$funcType([],[$Bool],false)},{prop:"lookupName",name:"lookupName",pkg:"time",typ:$funcType([$String,$Int64],[$Int,$Bool],false)}];G.init("time",[{prop:"i",name:"i",embedded:false,exported:false,typ:$Int32,tag:""},{prop:"when",name:"when",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"period",name:"period",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"f",name:"f",embedded:false,exported:false,typ:EA,tag:""},{prop:"arg",name:"arg",embedded:false,exported:false,typ:$emptyInterface,tag:""},{prop:"timeout",name:"timeout",embedded:false,exported:false,typ:EB,tag:""},{prop:"active",name:"active",embedded:false,exported:false,typ:$Bool,tag:""}]);AE.init("",[{prop:"Layout",name:"Layout",embedded:false,exported:true,typ:$String,tag:""},{prop:"Value",name:"Value",embedded:false,exported:true,typ:$String,tag:""},{prop:"LayoutElem",name:"LayoutElem",embedded:false,exported:true,typ:$String,tag:""},{prop:"ValueElem",name:"ValueElem",embedded:false,exported:true,typ:$String,tag:""},{prop:"Message",name:"Message",embedded:false,exported:true,typ:$String,tag:""}]);AX.init("time",[{prop:"C",name:"C",embedded:false,exported:true,typ:EE,tag:""},{prop:"r",name:"r",embedded:false,exported:false,typ:G,tag:""}]);BI.init("time",[{prop:"C",name:"C",embedded:false,exported:true,typ:EE,tag:""},{prop:"r",name:"r",embedded:false,exported:false,typ:G,tag:""}]);BL.init("time",[{prop:"wall",name:"wall",embedded:false,exported:false,typ:$Uint64,tag:""},{prop:"ext",name:"ext",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"loc",name:"loc",embedded:false,exported:false,typ:DU,tag:""}]);CJ.init("time",[{prop:"name",name:"name",embedded:false,exported:false,typ:$String,tag:""},{prop:"zone",name:"zone",embedded:false,exported:false,typ:DK,tag:""},{prop:"tx",name:"tx",embedded:false,exported:false,typ:DL,tag:""},{prop:"cacheStart",name:"cacheStart",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"cacheEnd",name:"cacheEnd",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"cacheZone",name:"cacheZone",embedded:false,exported:false,typ:DM,tag:""}]);CK.init("time",[{prop:"name",name:"name",embedded:false,exported:false,typ:$String,tag:""},{prop:"offset",name:"offset",embedded:false,exported:false,typ:$Int,tag:""},{prop:"isDST",name:"isDST",embedded:false,exported:false,typ:$Bool,tag:""}]);CL.init("time",[{prop:"when",name:"when",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"index",name:"index",embedded:false,exported:false,typ:$Uint8,tag:""},{prop:"isstd",name:"isstd",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"isutc",name:"isutc",embedded:false,exported:false,typ:$Bool,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=C.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}CN=new CJ.ptr("",DK.nil,DL.nil,new $Int64(0,0),new $Int64(0,0),DM.nil);CO=new E.Once.ptr(false,false);O=new DO([A.GOROOT()+"/lib/time/zoneinfo.zip"]);Q=$toNativeArray($kindInt,[260,265,524,526,528,274]);T=new DO(["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]);U=new DO(["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]);V=new DO(["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]);W=new DO(["January","February","March","April","May","June","July","August","September","October","November","December"]);AA=C.New("time: invalid number");AD=C.New("bad value for field");AR=C.New("time: bad [0-9]*");BN=$toNativeArray($kindString,["January","February","March","April","May","June","July","August","September","October","November","December"]);BP=$toNativeArray($kindString,["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]);BZ=$toNativeArray($kindInt32,[0,31,59,90,120,151,181,212,243,273,304,334,365]);CB=(h=I(),new $Int64(h.$high-0,h.$low-1));CM=new CJ.ptr("UTC",DK.nil,DL.nil,new $Int64(0,0),new $Int64(0,0),DM.nil);$pkg.UTC=CM;$pkg.Local=CN;CQ=C.New("time: invalid location name");CY=C.New("malformed time zone information");$unused(new DO(["/usr/share/zoneinfo/","/usr/share/lib/zoneinfo/","/usr/lib/locale/TZ/",A.GOROOT()+"/lib/time/zoneinfo.zip"]));F();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["internal/poll"]=(function(){var $pkg={},$init,B,E,F,D,C,A,G,M,P,Q,AG,AH,AI,AJ,AK,AM,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BE,BF,BG,BH,K,R,AE,I,J,L,N,O,S,T,U,W;B=$packages["errors"];E=$packages["io"];F=$packages["runtime"];D=$packages["sync/atomic"];C=$packages["syscall"];A=$packages["time"];G=$pkg.pollDesc=$newType(0,$kindStruct,"poll.pollDesc",true,"internal/poll",false,function(closing_){this.$val=this;if(arguments.length===0){this.closing=false;return;}this.closing=closing_;});M=$pkg.TimeoutError=$newType(0,$kindStruct,"poll.TimeoutError",true,"internal/poll",true,function(){this.$val=this;if(arguments.length===0){return;}});P=$pkg.fdMutex=$newType(0,$kindStruct,"poll.fdMutex",true,"internal/poll",false,function(state_,rsema_,wsema_){this.$val=this;if(arguments.length===0){this.state=new $Uint64(0,0);this.rsema=0;this.wsema=0;return;}this.state=state_;this.rsema=rsema_;this.wsema=wsema_;});Q=$pkg.FD=$newType(0,$kindStruct,"poll.FD",true,"internal/poll",true,function(fdmu_,Sysfd_,pd_,iovecs_,csema_,isBlocking_,IsStream_,ZeroReadIsEOF_,isFile_){this.$val=this;if(arguments.length===0){this.fdmu=new P.ptr(new $Uint64(0,0),0,0);this.Sysfd=0;this.pd=new G.ptr(false);this.iovecs=AS.nil;this.csema=0;this.isBlocking=0;this.IsStream=false;this.ZeroReadIsEOF=false;this.isFile=false;return;}this.fdmu=fdmu_;this.Sysfd=Sysfd_;this.pd=pd_;this.iovecs=iovecs_;this.csema=csema_;this.isBlocking=isBlocking_;this.IsStream=IsStream_;this.ZeroReadIsEOF=ZeroReadIsEOF_;this.isFile=isFile_;});AG=$ptrType($Uint32);AH=$chanType($Bool,false,false);AI=$sliceType(AH);AJ=$ptrType($Uint64);AK=$ptrType($Int32);AM=$arrayType($Uint8,4);AR=$sliceType(C.Iovec);AS=$ptrType(AR);AT=$ptrType($Uint8);AU=$ptrType(Q);AV=$ptrType(G);AW=$ptrType(M);AX=$ptrType(P);AY=$sliceType($Uint8);AZ=$ptrType(C.Stat_t);BA=$funcType([$Uintptr],[],false);BB=$funcType([$Uintptr],[$Bool],false);BC=$ptrType(C.Linger);BD=$ptrType(C.IPMreqn);BE=$ptrType(C.IPMreq);BF=$ptrType(C.IPv6Mreq);BG=$sliceType(AY);BH=$ptrType(BG);G.ptr.prototype.init=function(c){var c,d;d=this;return $ifaceNil;};G.prototype.init=function(c){return this.$val.init(c);};G.ptr.prototype.close=function(){var c;c=this;};G.prototype.close=function(){return this.$val.close();};G.ptr.prototype.evict=function(){var c;c=this;c.closing=true;};G.prototype.evict=function(){return this.$val.evict();};G.ptr.prototype.prepare=function(c,d){var c,d,e;e=this;if(e.closing){return L(d);}return $ifaceNil;};G.prototype.prepare=function(c,d){return this.$val.prepare(c,d);};G.ptr.prototype.prepareRead=function(c){var c,d;d=this;return d.prepare(114,c);};G.prototype.prepareRead=function(c){return this.$val.prepareRead(c);};G.ptr.prototype.prepareWrite=function(c){var c,d;d=this;return d.prepare(119,c);};G.prototype.prepareWrite=function(c){return this.$val.prepareWrite(c);};G.ptr.prototype.wait=function(c,d){var c,d,e;e=this;if(e.closing){return L(d);}return $pkg.ErrTimeout;};G.prototype.wait=function(c,d){return this.$val.wait(c,d);};G.ptr.prototype.waitRead=function(c){var c,d;d=this;return d.wait(114,c);};G.prototype.waitRead=function(c){return this.$val.waitRead(c);};G.ptr.prototype.waitWrite=function(c){var c,d;d=this;return d.wait(119,c);};G.prototype.waitWrite=function(c){return this.$val.waitWrite(c);};G.ptr.prototype.pollable=function(){return true;};G.prototype.pollable=function(){return this.$val.pollable();};Q.ptr.prototype.SetDeadline=function(c){var c;return $ifaceNil;};Q.prototype.SetDeadline=function(c){return this.$val.SetDeadline(c);};Q.ptr.prototype.SetReadDeadline=function(c){var c;return $ifaceNil;};Q.prototype.SetReadDeadline=function(c){return this.$val.SetReadDeadline(c);};Q.ptr.prototype.SetWriteDeadline=function(c){var c;return $ifaceNil;};Q.prototype.SetWriteDeadline=function(c){return this.$val.SetWriteDeadline(c);};I=function(c){var c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(c.$get()===0){$s=1;continue;}$s=2;continue;case 1:d=new $Chan($Bool,0);e=c;(K||$throwRuntimeError("assignment to entry in nil map"))[AG.keyFor(e)]={k:e,v:$append((f=K[AG.keyFor(c)],f!==undefined?f.v:AI.nil),d)};g=$recv(d);$s=3;case 3:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}g[0];case 2:c.$set(c.$get()-(1)>>>0);$s=-1;return;}return;}if($f===undefined){$f={$blk:I};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};J=function(c){var c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c.$set(c.$get()+(1)>>>0);e=(d=K[AG.keyFor(c)],d!==undefined?d.v:AI.nil);if(e.$length===0){$s=-1;return;}f=(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]);e=$subslice(e,1);g=c;(K||$throwRuntimeError("assignment to entry in nil map"))[AG.keyFor(g)]={k:g,v:e};if(e.$length===0){delete K[AG.keyFor(c)];}$r=$send(f,true);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:J};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};L=function(c){var c;if(c){return $pkg.ErrFileClosing;}return $pkg.ErrNetClosing;};M.ptr.prototype.Error=function(){var c;c=this;return"i/o timeout";};M.prototype.Error=function(){return this.$val.Error();};M.ptr.prototype.Timeout=function(){var c;c=this;return true;};M.prototype.Timeout=function(){return this.$val.Timeout();};M.ptr.prototype.Temporary=function(){var c;c=this;return true;};M.prototype.Temporary=function(){return this.$val.Temporary();};N=function(c,d){var c,d,e,f,g,h,i;while(true){if(!(c.$get().$length>0)){break;}f=(new $Int64(0,(e=c.$get(),(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0])).$length));if((f.$high>d.$high||(f.$high===d.$high&&f.$low>d.$low))){(h=c.$get(),(0>=h.$length?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+0]=$subslice((g=c.$get(),(0>=g.$length?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+0])),$flatten64(d))));return;}d=(i=f,new $Int64(d.$high-i.$high,d.$low-i.$low));c.$set($subslice((c.$get()),1));}};Q.ptr.prototype.Fsync=function(){var c,d,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);c=this;d=c.incref();if(!($interfaceIsEqual(d,$ifaceNil))){$s=-1;return d;}$deferred.push([$methodVal(c,"decref"),[]]);$s=-1;return C.Fsync(c.Sysfd);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Fsync};}$f.c=c;$f.d=d;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Fsync=function(){return this.$val.Fsync();};O=function(c,d,e){var c,d,e,f,g,h;f=C.Syscall(72,((c>>>0)),((d>>>0)),((e>>>0)));g=f[0];h=f[2];if(!((h===0))){return[((g>>0)),new C.Errno((h))];}return[((g>>0)),$ifaceNil];};P.ptr.prototype.incref=function(){var c,d,e,f,g;c=this;while(true){d=D.LoadUint64((c.$ptr_state||(c.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},c))));if(!((e=new $Uint64(d.$high&0,(d.$low&1)>>>0),(e.$high===0&&e.$low===0)))){return false;}f=new $Uint64(d.$high+0,d.$low+8);if((g=new $Uint64(f.$high&0,(f.$low&8388600)>>>0),(g.$high===0&&g.$low===0))){$panic(new $String("too many concurrent operations on a single file or socket (max 1048575)"));}if(D.CompareAndSwapUint64((c.$ptr_state||(c.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},c))),d,f)){return true;}}};P.prototype.incref=function(){return this.$val.incref();};P.ptr.prototype.increfAndClose=function(){var c,d,e,f,g,h,i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;case 1:d=D.LoadUint64((c.$ptr_state||(c.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},c))));if(!((e=new $Uint64(d.$high&0,(d.$low&1)>>>0),(e.$high===0&&e.$low===0)))){$s=-1;return false;}g=(f=new $Uint64(d.$high|0,(d.$low|1)>>>0),new $Uint64(f.$high+0,f.$low+8));if((h=new $Uint64(g.$high&0,(g.$low&8388600)>>>0),(h.$high===0&&h.$low===0))){$panic(new $String("too many concurrent operations on a single file or socket (max 1048575)"));}g=(i=new $Uint64(2147483647,4286578688),new $Uint64(g.$high&~i.$high,(g.$low&~i.$low)>>>0));if(D.CompareAndSwapUint64((c.$ptr_state||(c.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},c))),d,g)){$s=3;continue;}$s=4;continue;case 3:case 5:if(!(!((j=new $Uint64(d.$high&2047,(d.$low&4286578688)>>>0),(j.$high===0&&j.$low===0))))){$s=6;continue;}d=(k=new $Uint64(0,8388608),new $Uint64(d.$high-k.$high,d.$low-k.$low));$r=J((c.$ptr_rsema||(c.$ptr_rsema=new AG(function(){return this.$target.rsema;},function($v){this.$target.rsema=$v;},c))));$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=5;continue;case 6:case 8:if(!(!((l=new $Uint64(d.$high&2147481600,(d.$low&0)>>>0),(l.$high===0&&l.$low===0))))){$s=9;continue;}d=(m=new $Uint64(2048,0),new $Uint64(d.$high-m.$high,d.$low-m.$low));$r=J((c.$ptr_wsema||(c.$ptr_wsema=new AG(function(){return this.$target.wsema;},function($v){this.$target.wsema=$v;},c))));$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=8;continue;case 9:$s=-1;return true;case 4:$s=1;continue;case 2:$s=-1;return false;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.increfAndClose};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.increfAndClose=function(){return this.$val.increfAndClose();};P.ptr.prototype.decref=function(){var c,d,e,f,g;c=this;while(true){d=D.LoadUint64((c.$ptr_state||(c.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},c))));if((e=new $Uint64(d.$high&0,(d.$low&8388600)>>>0),(e.$high===0&&e.$low===0))){$panic(new $String("inconsistent poll.fdMutex"));}f=new $Uint64(d.$high-0,d.$low-8);if(D.CompareAndSwapUint64((c.$ptr_state||(c.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},c))),d,f)){return(g=new $Uint64(f.$high&0,(f.$low&8388601)>>>0),(g.$high===0&&g.$low===1));}}};P.prototype.decref=function(){return this.$val.decref();};P.ptr.prototype.rwlock=function(c){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=new $Uint64(0,0);f=new $Uint64(0,0);g=new $Uint64(0,0);h=e;i=f;j=g;k=AG.nil;if(c){h=new $Uint64(0,2);i=new $Uint64(0,8388608);j=new $Uint64(2047,4286578688);k=(d.$ptr_rsema||(d.$ptr_rsema=new AG(function(){return this.$target.rsema;},function($v){this.$target.rsema=$v;},d)));}else{h=new $Uint64(0,4);i=new $Uint64(2048,0);j=new $Uint64(2147481600,0);k=(d.$ptr_wsema||(d.$ptr_wsema=new AG(function(){return this.$target.wsema;},function($v){this.$target.wsema=$v;},d)));}case 1:l=D.LoadUint64((d.$ptr_state||(d.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},d))));if(!((m=new $Uint64(l.$high&0,(l.$low&1)>>>0),(m.$high===0&&m.$low===0)))){$s=-1;return false;}n=new $Uint64(0,0);if((o=new $Uint64(l.$high&h.$high,(l.$low&h.$low)>>>0),(o.$high===0&&o.$low===0))){n=(p=new $Uint64(l.$high|h.$high,(l.$low|h.$low)>>>0),new $Uint64(p.$high+0,p.$low+8));if((q=new $Uint64(n.$high&0,(n.$low&8388600)>>>0),(q.$high===0&&q.$low===0))){$panic(new $String("too many concurrent operations on a single file or socket (max 1048575)"));}}else{n=new $Uint64(l.$high+i.$high,l.$low+i.$low);if((r=new $Uint64(n.$high&j.$high,(n.$low&j.$low)>>>0),(r.$high===0&&r.$low===0))){$panic(new $String("too many concurrent operations on a single file or socket (max 1048575)"));}}if(D.CompareAndSwapUint64((d.$ptr_state||(d.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},d))),l,n)){$s=3;continue;}$s=4;continue;case 3:if((s=new $Uint64(l.$high&h.$high,(l.$low&h.$low)>>>0),(s.$high===0&&s.$low===0))){$s=-1;return true;}$r=I(k);$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 4:$s=1;continue;case 2:$s=-1;return false;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.rwlock};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.rwlock=function(c){return this.$val.rwlock(c);};P.ptr.prototype.rwunlock=function(c){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=new $Uint64(0,0);f=new $Uint64(0,0);g=new $Uint64(0,0);h=e;i=f;j=g;k=AG.nil;if(c){h=new $Uint64(0,2);i=new $Uint64(0,8388608);j=new $Uint64(2047,4286578688);k=(d.$ptr_rsema||(d.$ptr_rsema=new AG(function(){return this.$target.rsema;},function($v){this.$target.rsema=$v;},d)));}else{h=new $Uint64(0,4);i=new $Uint64(2048,0);j=new $Uint64(2147481600,0);k=(d.$ptr_wsema||(d.$ptr_wsema=new AG(function(){return this.$target.wsema;},function($v){this.$target.wsema=$v;},d)));}case 1:l=D.LoadUint64((d.$ptr_state||(d.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},d))));if((m=new $Uint64(l.$high&h.$high,(l.$low&h.$low)>>>0),(m.$high===0&&m.$low===0))||(n=new $Uint64(l.$high&0,(l.$low&8388600)>>>0),(n.$high===0&&n.$low===0))){$panic(new $String("inconsistent poll.fdMutex"));}p=(o=new $Uint64(l.$high&~h.$high,(l.$low&~h.$low)>>>0),new $Uint64(o.$high-0,o.$low-8));if(!((q=new $Uint64(l.$high&j.$high,(l.$low&j.$low)>>>0),(q.$high===0&&q.$low===0)))){p=(r=i,new $Uint64(p.$high-r.$high,p.$low-r.$low));}if(D.CompareAndSwapUint64((d.$ptr_state||(d.$ptr_state=new AJ(function(){return this.$target.state;},function($v){this.$target.state=$v;},d))),l,p)){$s=3;continue;}$s=4;continue;case 3:if(!((s=new $Uint64(l.$high&j.$high,(l.$low&j.$low)>>>0),(s.$high===0&&s.$low===0)))){$s=5;continue;}$s=6;continue;case 5:$r=J(k);$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 6:$s=-1;return(t=new $Uint64(p.$high&0,(p.$low&8388601)>>>0),(t.$high===0&&t.$low===1));case 4:$s=1;continue;case 2:$s=-1;return false;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.rwunlock};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.rwunlock=function(c){return this.$val.rwunlock(c);};Q.ptr.prototype.incref=function(){var c;c=this;if(!c.fdmu.incref()){return L(c.isFile);}return $ifaceNil;};Q.prototype.incref=function(){return this.$val.incref();};Q.ptr.prototype.decref=function(){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(c.fdmu.decref()){$s=1;continue;}$s=2;continue;case 1:d=c.destroy();$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;case 2:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.decref};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.decref=function(){return this.$val.decref();};Q.ptr.prototype.readLock=function(){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.fdmu.rwlock(true);$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}if(!d){$s=1;continue;}$s=2;continue;case 1:$s=-1;return L(c.isFile);case 2:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.readLock};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.readLock=function(){return this.$val.readLock();};Q.ptr.prototype.readUnlock=function(){var c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.fdmu.rwunlock(true);$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}if(d){$s=1;continue;}$s=2;continue;case 1:e=c.destroy();$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}e;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.readUnlock};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.readUnlock=function(){return this.$val.readUnlock();};Q.ptr.prototype.writeLock=function(){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.fdmu.rwlock(false);$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}if(!d){$s=1;continue;}$s=2;continue;case 1:$s=-1;return L(c.isFile);case 2:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.writeLock};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.writeLock=function(){return this.$val.writeLock();};Q.ptr.prototype.writeUnlock=function(){var c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.fdmu.rwunlock(false);$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}if(d){$s=1;continue;}$s=2;continue;case 1:e=c.destroy();$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}e;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.writeUnlock};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.writeUnlock=function(){return this.$val.writeUnlock();};Q.ptr.prototype.eofError=function(c,d){var c,d,e;e=this;if((c===0)&&$interfaceIsEqual(d,$ifaceNil)&&e.ZeroReadIsEOF){return E.EOF;}return d;};Q.prototype.eofError=function(c,d){return this.$val.eofError(c,d);};Q.ptr.prototype.Fchmod=function(c){var c,d,e,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.incref();if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}$deferred.push([$methodVal(d,"decref"),[]]);$s=-1;return C.Fchmod(d.Sysfd,c);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Fchmod};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Fchmod=function(c){return this.$val.Fchmod(c);};Q.ptr.prototype.Fchown=function(c,d){var c,d,e,f,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);e=this;f=e.incref();if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return f;}$deferred.push([$methodVal(e,"decref"),[]]);$s=-1;return C.Fchown(e.Sysfd,c,d);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Fchown};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Fchown=function(c,d){return this.$val.Fchown(c,d);};Q.ptr.prototype.Ftruncate=function(c){var c,d,e,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.incref();if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}$deferred.push([$methodVal(d,"decref"),[]]);$s=-1;return C.Ftruncate(d.Sysfd,c);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Ftruncate};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Ftruncate=function(c){return this.$val.Ftruncate(c);};Q.ptr.prototype.Init=function(c,d){var c,d,e,f;e=this;if(c==="file"){e.isFile=true;}if(!d){e.isBlocking=1;return $ifaceNil;}f=e.pd.init(e);if(!($interfaceIsEqual(f,$ifaceNil))){e.isBlocking=1;}return f;};Q.prototype.Init=function(c,d){return this.$val.Init(c,d);};Q.ptr.prototype.destroy=function(){var c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;c.pd.close();d=$pkg.CloseFunc(c.Sysfd);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;c.Sysfd=-1;$r=J((c.$ptr_csema||(c.$ptr_csema=new AG(function(){return this.$target.csema;},function($v){this.$target.csema=$v;},c))));$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return e;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.destroy};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.destroy=function(){return this.$val.destroy();};Q.ptr.prototype.Close=function(){var c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.fdmu.increfAndClose();$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}if(!d){$s=1;continue;}$s=2;continue;case 1:$s=-1;return L(c.isFile);case 2:c.pd.evict();e=c.decref();$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(c.isBlocking===0){$s=5;continue;}$s=6;continue;case 5:$r=I((c.$ptr_csema||(c.$ptr_csema=new AG(function(){return this.$target.csema;},function($v){this.$target.csema=$v;},c))));$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 6:$s=-1;return f;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.Close};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.Close=function(){return this.$val.Close();};Q.ptr.prototype.Shutdown=function(c){var c,d,e,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.incref();if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}$deferred.push([$methodVal(d,"decref"),[]]);$s=-1;return C.Shutdown(d.Sysfd,c);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Shutdown};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Shutdown=function(c){return this.$val.Shutdown(c);};Q.ptr.prototype.SetBlocking=function(){var c,d,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);c=this;d=c.incref();if(!($interfaceIsEqual(d,$ifaceNil))){$s=-1;return d;}$deferred.push([$methodVal(c,"decref"),[]]);D.StoreUint32((c.$ptr_isBlocking||(c.$ptr_isBlocking=new AG(function(){return this.$target.isBlocking;},function($v){this.$target.isBlocking=$v;},c))),1);$s=-1;return C.SetNonblock(c.Sysfd,false);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.SetBlocking};}$f.c=c;$f.d=d;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.SetBlocking=function(){return this.$val.SetBlocking();};Q.ptr.prototype.Read=function(c){var c,d,e,f,g,h,i,j,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.readLock();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[0,f];}$deferred.push([$methodVal(d,"readUnlock"),[]]);if(c.$length===0){$s=-1;return[0,$ifaceNil];}g=d.pd.prepareRead(d.isFile);if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[0,g];}if(d.IsStream&&c.$length>1073741824){c=$subslice(c,0,1073741824);}while(true){h=C.Read(d.Sysfd,c);i=h[0];j=h[1];if(!($interfaceIsEqual(j,$ifaceNil))){i=0;if($interfaceIsEqual(j,new C.Errno(11))&&d.pd.pollable()){j=d.pd.waitRead(d.isFile);if($interfaceIsEqual(j,$ifaceNil)){continue;}}if(false&&$interfaceIsEqual(j,new C.Errno(4))){continue;}}j=d.eofError(i,j);$s=-1;return[i,j];}$s=-1;return[0,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Read};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Read=function(c){return this.$val.Read(c);};Q.ptr.prototype.Pread=function(c,d){var c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;f=e.incref();if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[0,f];}if(e.IsStream&&c.$length>1073741824){c=$subslice(c,0,1073741824);}g=C.Pread(e.Sysfd,c,d);h=g[0];i=g[1];if(!($interfaceIsEqual(i,$ifaceNil))){h=0;}j=e.decref();$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}j;i=e.eofError(h,i);$s=-1;return[h,i];}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.Pread};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.Pread=function(c,d){return this.$val.Pread(c,d);};Q.ptr.prototype.ReadFrom=function(c){var c,d,e,f,g,h,i,j,k,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.readLock();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[0,$ifaceNil,f];}$deferred.push([$methodVal(d,"readUnlock"),[]]);g=d.pd.prepareRead(d.isFile);if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[0,$ifaceNil,g];}while(true){h=C.Recvfrom(d.Sysfd,c,0);i=h[0];j=h[1];k=h[2];if(!($interfaceIsEqual(k,$ifaceNil))){i=0;if($interfaceIsEqual(k,new C.Errno(11))&&d.pd.pollable()){k=d.pd.waitRead(d.isFile);if($interfaceIsEqual(k,$ifaceNil)){continue;}}}k=d.eofError(i,k);$s=-1;return[i,j,k];}$s=-1;return[0,$ifaceNil,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,$ifaceNil,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.ReadFrom};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.ReadFrom=function(c){return this.$val.ReadFrom(c);};Q.ptr.prototype.ReadMsg=function(c,d){var c,d,e,f,g,h,i,j,k,l,m,n,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);e=this;f=e.readLock();$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[0,0,0,$ifaceNil,g];}$deferred.push([$methodVal(e,"readUnlock"),[]]);h=e.pd.prepareRead(e.isFile);if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return[0,0,0,$ifaceNil,h];}while(true){i=C.Recvmsg(e.Sysfd,c,d,0);j=i[0];k=i[1];l=i[2];m=i[3];n=i[4];if(!($interfaceIsEqual(n,$ifaceNil))){if($interfaceIsEqual(n,new C.Errno(11))&&e.pd.pollable()){n=e.pd.waitRead(e.isFile);if($interfaceIsEqual(n,$ifaceNil)){continue;}}}n=e.eofError(j,n);$s=-1;return[j,k,l,m,n];}$s=-1;return[0,0,0,$ifaceNil,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,0,0,$ifaceNil,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.ReadMsg};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.ReadMsg=function(c,d){return this.$val.ReadMsg(c,d);};Q.ptr.prototype.Write=function(c){var c,d,e,f,g,h,i,j,k,l,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.writeLock();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[0,f];}$deferred.push([$methodVal(d,"writeUnlock"),[]]);g=d.pd.prepareWrite(d.isFile);if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[0,g];}h=0;while(true){i=c.$length;if(d.IsStream&&(i-h>>0)>1073741824){i=h+1073741824>>0;}j=C.Write(d.Sysfd,$subslice(c,h,i));k=j[0];l=j[1];if(k>0){h=h+(k)>>0;}if(h===c.$length){$s=-1;return[h,l];}if($interfaceIsEqual(l,new C.Errno(11))&&d.pd.pollable()){l=d.pd.waitWrite(d.isFile);if($interfaceIsEqual(l,$ifaceNil)){continue;}}if(!($interfaceIsEqual(l,$ifaceNil))){$s=-1;return[h,l];}if(k===0){$s=-1;return[h,E.ErrUnexpectedEOF];}}$s=-1;return[0,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Write};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Write=function(c){return this.$val.Write(c);};Q.ptr.prototype.Pwrite=function(c,d){var c,d,e,f,g,h,i,j,k,l,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);e=this;f=e.incref();if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[0,f];}$deferred.push([$methodVal(e,"decref"),[]]);g=0;while(true){h=c.$length;if(e.IsStream&&(h-g>>0)>1073741824){h=g+1073741824>>0;}i=C.Pwrite(e.Sysfd,$subslice(c,g,h),(j=(new $Int64(0,g)),new $Int64(d.$high+j.$high,d.$low+j.$low)));k=i[0];l=i[1];if(k>0){g=g+(k)>>0;}if(g===c.$length){$s=-1;return[g,l];}if(!($interfaceIsEqual(l,$ifaceNil))){$s=-1;return[g,l];}if(k===0){$s=-1;return[g,E.ErrUnexpectedEOF];}}$s=-1;return[0,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Pwrite};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Pwrite=function(c,d){return this.$val.Pwrite(c,d);};Q.ptr.prototype.WriteTo=function(c,d){var c,d,e,f,g,h,i,j,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);e=this;f=e.writeLock();$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[0,g];}$deferred.push([$methodVal(e,"writeUnlock"),[]]);h=e.pd.prepareWrite(e.isFile);if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return[0,h];}case 2:i=C.Sendto(e.Sysfd,c,0,d);$s=4;case 4:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}j=i;if($interfaceIsEqual(j,new C.Errno(11))&&e.pd.pollable()){j=e.pd.waitWrite(e.isFile);if($interfaceIsEqual(j,$ifaceNil)){$s=2;continue;}}if(!($interfaceIsEqual(j,$ifaceNil))){$s=-1;return[0,j];}$s=-1;return[c.$length,$ifaceNil];$s=2;continue;case 3:$s=-1;return[0,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.WriteTo};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.WriteTo=function(c,d){return this.$val.WriteTo(c,d);};Q.ptr.prototype.WriteMsg=function(c,d,e){var c,d,e,f,g,h,i,j,k,l,m,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);f=this;g=f.writeLock();$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return[0,0,h];}$deferred.push([$methodVal(f,"writeUnlock"),[]]);i=f.pd.prepareWrite(f.isFile);if(!($interfaceIsEqual(i,$ifaceNil))){$s=-1;return[0,0,i];}case 2:k=C.SendmsgN(f.Sysfd,c,d,e,0);$s=4;case 4:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;l=j[0];m=j[1];if($interfaceIsEqual(m,new C.Errno(11))&&f.pd.pollable()){m=f.pd.waitWrite(f.isFile);if($interfaceIsEqual(m,$ifaceNil)){$s=2;continue;}}if(!($interfaceIsEqual(m,$ifaceNil))){$s=-1;return[l,0,m];}$s=-1;return[l,d.$length,m];$s=2;continue;case 3:$s=-1;return[0,0,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,0,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.WriteMsg};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.WriteMsg=function(c,d,e){return this.$val.WriteMsg(c,d,e);};Q.ptr.prototype.Accept=function(){var c,d,e,f,g,h,i,j,k,l,m,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);c=this;d=c.readLock();$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[-1,$ifaceNil,"",e];}$deferred.push([$methodVal(c,"readUnlock"),[]]);f=c.pd.prepareRead(c.isFile);if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[-1,$ifaceNil,"",f];}case 2:h=W(c.Sysfd);$s=4;case 4:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;i=g[0];j=g[1];k=g[2];l=g[3];if($interfaceIsEqual(l,$ifaceNil)){$s=-1;return[i,j,"",l];}m=l;if($interfaceIsEqual(m,new C.Errno((11)))){if(c.pd.pollable()){l=c.pd.waitRead(c.isFile);if($interfaceIsEqual(l,$ifaceNil)){$s=2;continue;}}}else if($interfaceIsEqual(m,new C.Errno((103)))){$s=2;continue;}$s=-1;return[-1,$ifaceNil,k,l];$s=2;continue;case 3:$s=-1;return[0,$ifaceNil,"",$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,$ifaceNil,"",$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Accept};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Accept=function(){return this.$val.Accept();};Q.ptr.prototype.Seek=function(c,d){var c,d,e,f,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);e=this;f=e.incref();if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[new $Int64(0,0),f];}$deferred.push([$methodVal(e,"decref"),[]]);$s=-1;return C.Seek(e.Sysfd,c,d);}return;}}catch(err){$err=err;$s=-1;return[new $Int64(0,0),$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Seek};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Seek=function(c,d){return this.$val.Seek(c,d);};Q.ptr.prototype.ReadDirent=function(c){var c,d,e,f,g,h,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.incref();if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[0,e];}$deferred.push([$methodVal(d,"decref"),[]]);while(true){f=C.ReadDirent(d.Sysfd,c);g=f[0];h=f[1];if(!($interfaceIsEqual(h,$ifaceNil))){g=0;if($interfaceIsEqual(h,new C.Errno(11))&&d.pd.pollable()){h=d.pd.waitRead(d.isFile);if($interfaceIsEqual(h,$ifaceNil)){continue;}}}$s=-1;return[g,h];}$s=-1;return[0,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.ReadDirent};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.ReadDirent=function(c){return this.$val.ReadDirent(c);};Q.ptr.prototype.Fchdir=function(){var c,d,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);c=this;d=c.incref();if(!($interfaceIsEqual(d,$ifaceNil))){$s=-1;return d;}$deferred.push([$methodVal(c,"decref"),[]]);$s=-1;return C.Fchdir(c.Sysfd);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Fchdir};}$f.c=c;$f.d=d;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Fchdir=function(){return this.$val.Fchdir();};Q.ptr.prototype.Fstat=function(c){var c,d,e,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.incref();if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}$deferred.push([$methodVal(d,"decref"),[]]);$s=-1;return C.Fstat(d.Sysfd,c);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Fstat};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Fstat=function(c){return this.$val.Fstat(c);};S=function(c){var c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(D.LoadInt32((AE||(AE=new AK(function(){return R;},function($v){R=$v;}))))===1){d=O(c,1030,0);e=d[0];f=d[1];if($interfaceIsEqual(f,$ifaceNil)){$s=-1;return[e,"",$ifaceNil];}g=$assertType(f,C.Errno);if((g===(22))||(g===(38))){D.StoreInt32((AE||(AE=new AK(function(){return R;},function($v){R=$v;}))),0);}else{$s=-1;return[-1,"fcntl",f];}}h=T(c);$s=1;case 1:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}$s=-1;return h;}return;}if($f===undefined){$f={$blk:S};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};$pkg.DupCloseOnExec=S;T=function(c){var c,d,e,f,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);$r=C.ForkLock.RLock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$deferred.push([$methodVal(C.ForkLock,"RUnlock"),[]]);d=C.Dup(c);e=d[0];f=d[1];if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[-1,"dup",f];}C.CloseOnExec(e);$s=-1;return[e,"",$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[0,"",$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:T};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.ptr.prototype.Dup=function(){var c,d,e,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);c=this;d=c.incref();if(!($interfaceIsEqual(d,$ifaceNil))){$s=-1;return[-1,"",d];}$deferred.push([$methodVal(c,"decref"),[]]);e=S(c.Sysfd);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;}return;}}catch(err){$err=err;$s=-1;return[0,"",$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Dup};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Dup=function(){return this.$val.Dup();};Q.ptr.prototype.WaitWrite=function(){var c;c=this;return c.pd.waitWrite(c.isFile);};Q.prototype.WaitWrite=function(){return this.$val.WaitWrite();};Q.ptr.prototype.WriteOnce=function(c){var c,d,e,f,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.writeLock();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[0,f];}$deferred.push([$methodVal(d,"writeUnlock"),[]]);$s=-1;return C.Write(d.Sysfd,c);}return;}}catch(err){$err=err;$s=-1;return[0,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.WriteOnce};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.WriteOnce=function(c){return this.$val.WriteOnce(c);};Q.ptr.prototype.RawControl=function(c){var c,d,e,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.incref();if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}$deferred.push([$methodVal(d,"decref"),[]]);$r=c(((d.Sysfd>>>0)));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return $ifaceNil;}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.RawControl};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.RawControl=function(c){return this.$val.RawControl(c);};Q.ptr.prototype.RawRead=function(c){var c,d,e,f,g,h,i,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.readLock();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return f;}$deferred.push([$methodVal(d,"readUnlock"),[]]);g=d.pd.prepareRead(d.isFile);if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}case 2:h=c(((d.Sysfd>>>0)));$s=6;case 6:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}if(h){$s=4;continue;}$s=5;continue;case 4:$s=-1;return $ifaceNil;case 5:i=d.pd.waitRead(d.isFile);if(!($interfaceIsEqual(i,$ifaceNil))){$s=-1;return i;}$s=2;continue;case 3:$s=-1;return $ifaceNil;}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.RawRead};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.RawRead=function(c){return this.$val.RawRead(c);};Q.ptr.prototype.RawWrite=function(c){var c,d,e,f,g,h,i,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=this;e=d.writeLock();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return f;}$deferred.push([$methodVal(d,"writeUnlock"),[]]);g=d.pd.prepareWrite(d.isFile);if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}case 2:h=c(((d.Sysfd>>>0)));$s=6;case 6:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}if(h){$s=4;continue;}$s=5;continue;case 4:$s=-1;return $ifaceNil;case 5:i=d.pd.waitWrite(d.isFile);if(!($interfaceIsEqual(i,$ifaceNil))){$s=-1;return i;}$s=2;continue;case 3:$s=-1;return $ifaceNil;}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.RawWrite};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.RawWrite=function(c){return this.$val.RawWrite(c);};U=function(c,d){var c,d,e,f,g;e=C.Syscall(20,((c>>>0)),(($sliceToArray(d))),((d.$length>>>0)));f=e[0];g=e[2];if(!((g===0))){return[f,new C.Errno((g))];}return[f,$ifaceNil];};W=function(c){var c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=$pkg.Accept4Func(c,526336);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;f=d[0];g=d[1];h=d[2];i=h;if($interfaceIsEqual(i,$ifaceNil)){$s=-1;return[f,g,"",$ifaceNil];}else if($interfaceIsEqual(i,new C.Errno((38)))){}else if($interfaceIsEqual(i,new C.Errno((22)))){}else if($interfaceIsEqual(i,new C.Errno((13)))){}else if($interfaceIsEqual(i,new C.Errno((14)))){}else{$s=-1;return[-1,g,"accept4",h];}k=$pkg.AcceptFunc(c);$s=2;case 2:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;f=j[0];g=j[1];h=j[2];if($interfaceIsEqual(h,$ifaceNil)){C.CloseOnExec(f);}if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return[-1,$ifaceNil,"accept",h];}h=C.SetNonblock(f,true);if(!($interfaceIsEqual(h,$ifaceNil))){$s=3;continue;}$s=4;continue;case 3:l=$pkg.CloseFunc(f);$s=5;case 5:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}l;$s=-1;return[-1,$ifaceNil,"setnonblock",h];case 4:$s=-1;return[f,g,"",$ifaceNil];}return;}if($f===undefined){$f={$blk:W};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};Q.ptr.prototype.SetsockoptInt=function(c,d,e){var c,d,e,f,g,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);f=this;g=f.incref();if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}$deferred.push([$methodVal(f,"decref"),[]]);$s=-1;return C.SetsockoptInt(f.Sysfd,c,d,e);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.SetsockoptInt};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.SetsockoptInt=function(c,d,e){return this.$val.SetsockoptInt(c,d,e);};Q.ptr.prototype.SetsockoptInet4Addr=function(c,d,e){var c,d,e,f,g,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);f=this;g=f.incref();if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}$deferred.push([$methodVal(f,"decref"),[]]);$s=-1;return C.SetsockoptInet4Addr(f.Sysfd,c,d,$clone(e,AM));}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.SetsockoptInet4Addr};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.SetsockoptInet4Addr=function(c,d,e){return this.$val.SetsockoptInet4Addr(c,d,e);};Q.ptr.prototype.SetsockoptLinger=function(c,d,e){var c,d,e,f,g,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);f=this;g=f.incref();if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}$deferred.push([$methodVal(f,"decref"),[]]);$s=-1;return C.SetsockoptLinger(f.Sysfd,c,d,e);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.SetsockoptLinger};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.SetsockoptLinger=function(c,d,e){return this.$val.SetsockoptLinger(c,d,e);};Q.ptr.prototype.SetsockoptIPMreqn=function(c,d,e){var c,d,e,f,g,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);f=this;g=f.incref();if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}$deferred.push([$methodVal(f,"decref"),[]]);$s=-1;return C.SetsockoptIPMreqn(f.Sysfd,c,d,e);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.SetsockoptIPMreqn};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.SetsockoptIPMreqn=function(c,d,e){return this.$val.SetsockoptIPMreqn(c,d,e);};Q.ptr.prototype.SetsockoptByte=function(c,d,e){var c,d,e,f,g,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);f=this;g=f.incref();if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}$deferred.push([$methodVal(f,"decref"),[]]);$s=-1;return C.SetsockoptByte(f.Sysfd,c,d,e);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.SetsockoptByte};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.SetsockoptByte=function(c,d,e){return this.$val.SetsockoptByte(c,d,e);};Q.ptr.prototype.SetsockoptIPMreq=function(c,d,e){var c,d,e,f,g,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);f=this;g=f.incref();if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}$deferred.push([$methodVal(f,"decref"),[]]);$s=-1;return C.SetsockoptIPMreq(f.Sysfd,c,d,e);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.SetsockoptIPMreq};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.SetsockoptIPMreq=function(c,d,e){return this.$val.SetsockoptIPMreq(c,d,e);};Q.ptr.prototype.SetsockoptIPv6Mreq=function(c,d,e){var c,d,e,f,g,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);f=this;g=f.incref();if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}$deferred.push([$methodVal(f,"decref"),[]]);$s=-1;return C.SetsockoptIPv6Mreq(f.Sysfd,c,d,e);}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.SetsockoptIPv6Mreq};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.SetsockoptIPv6Mreq=function(c,d,e){return this.$val.SetsockoptIPv6Mreq(c,d,e);};Q.ptr.prototype.Writev=function(c){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);d=[d];e=this;f=e.writeLock();$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[new $Int64(0,0),g];}$deferred.push([$methodVal(e,"writeUnlock"),[]]);h=e.pd.prepareWrite(e.isFile);if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return[new $Int64(0,0),h];}d[0]=AR.nil;if(!(e.iovecs===AS.nil)){d[0]=e.iovecs.$get();}i=1024;j=new $Int64(0,0);k=$ifaceNil;case 2:if(!(c.$get().$length>0)){$s=3;continue;}d[0]=$subslice(d[0],0,0);l=c.$get();m=0;case 4:if(!(m=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+m]);if(n.$length===0){m++;$s=4;continue;}d[0]=$append(d[0],new C.Iovec.ptr($indexPtr(n.$array,n.$offset+0,AT),new $Uint64(0,0)));if(e.IsStream&&n.$length>1073741824){(o=d[0].$length-1>>0,((o<0||o>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+o])).SetLen(1073741824);$s=5;continue;}(p=d[0].$length-1>>0,((p<0||p>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+p])).SetLen(n.$length);if(d[0].$length===i){$s=5;continue;}m++;$s=4;continue;case 5:if(d[0].$length===0){$s=3;continue;}e.iovecs=(d.$ptr||(d.$ptr=new AS(function(){return this.$target[0];},function($v){this.$target[0]=$v;},d)));q=0;r=U(e.Sysfd,d[0]);q=r[0];k=r[1];if(q===4294967295){q=0;}$r=$pkg.TestHookDidWritev(((q>>0)));$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}j=(s=(new $Int64(0,q.constructor===Number?q:1)),new $Int64(j.$high+s.$high,j.$low+s.$low));N(c,(new $Int64(0,q.constructor===Number?q:1)));if(!($interfaceIsEqual(k,$ifaceNil))){if($assertType(k,C.Errno)===11){k=e.pd.waitWrite(e.isFile);if($interfaceIsEqual(k,$ifaceNil)){$s=2;continue;}}$s=3;continue;}if((j.$high===0&&j.$low===0)){k=E.ErrUnexpectedEOF;$s=3;continue;}$s=2;continue;case 3:$s=-1;return[j,k];}return;}}catch(err){$err=err;$s=-1;return[new $Int64(0,0),$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.Writev};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.Writev=function(c){return this.$val.Writev(c);};AV.methods=[{prop:"init",name:"init",pkg:"internal/poll",typ:$funcType([AU],[$error],false)},{prop:"close",name:"close",pkg:"internal/poll",typ:$funcType([],[],false)},{prop:"evict",name:"evict",pkg:"internal/poll",typ:$funcType([],[],false)},{prop:"prepare",name:"prepare",pkg:"internal/poll",typ:$funcType([$Int,$Bool],[$error],false)},{prop:"prepareRead",name:"prepareRead",pkg:"internal/poll",typ:$funcType([$Bool],[$error],false)},{prop:"prepareWrite",name:"prepareWrite",pkg:"internal/poll",typ:$funcType([$Bool],[$error],false)},{prop:"wait",name:"wait",pkg:"internal/poll",typ:$funcType([$Int,$Bool],[$error],false)},{prop:"waitRead",name:"waitRead",pkg:"internal/poll",typ:$funcType([$Bool],[$error],false)},{prop:"waitWrite",name:"waitWrite",pkg:"internal/poll",typ:$funcType([$Bool],[$error],false)},{prop:"waitCanceled",name:"waitCanceled",pkg:"internal/poll",typ:$funcType([$Int],[],false)},{prop:"pollable",name:"pollable",pkg:"internal/poll",typ:$funcType([],[$Bool],false)}];AW.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Temporary",name:"Temporary",pkg:"",typ:$funcType([],[$Bool],false)}];AX.methods=[{prop:"incref",name:"incref",pkg:"internal/poll",typ:$funcType([],[$Bool],false)},{prop:"increfAndClose",name:"increfAndClose",pkg:"internal/poll",typ:$funcType([],[$Bool],false)},{prop:"decref",name:"decref",pkg:"internal/poll",typ:$funcType([],[$Bool],false)},{prop:"rwlock",name:"rwlock",pkg:"internal/poll",typ:$funcType([$Bool],[$Bool],false)},{prop:"rwunlock",name:"rwunlock",pkg:"internal/poll",typ:$funcType([$Bool],[$Bool],false)}];AU.methods=[{prop:"SetDeadline",name:"SetDeadline",pkg:"",typ:$funcType([A.Time],[$error],false)},{prop:"SetReadDeadline",name:"SetReadDeadline",pkg:"",typ:$funcType([A.Time],[$error],false)},{prop:"SetWriteDeadline",name:"SetWriteDeadline",pkg:"",typ:$funcType([A.Time],[$error],false)},{prop:"Fsync",name:"Fsync",pkg:"",typ:$funcType([],[$error],false)},{prop:"incref",name:"incref",pkg:"internal/poll",typ:$funcType([],[$error],false)},{prop:"decref",name:"decref",pkg:"internal/poll",typ:$funcType([],[$error],false)},{prop:"readLock",name:"readLock",pkg:"internal/poll",typ:$funcType([],[$error],false)},{prop:"readUnlock",name:"readUnlock",pkg:"internal/poll",typ:$funcType([],[],false)},{prop:"writeLock",name:"writeLock",pkg:"internal/poll",typ:$funcType([],[$error],false)},{prop:"writeUnlock",name:"writeUnlock",pkg:"internal/poll",typ:$funcType([],[],false)},{prop:"eofError",name:"eofError",pkg:"internal/poll",typ:$funcType([$Int,$error],[$error],false)},{prop:"Fchmod",name:"Fchmod",pkg:"",typ:$funcType([$Uint32],[$error],false)},{prop:"Fchown",name:"Fchown",pkg:"",typ:$funcType([$Int,$Int],[$error],false)},{prop:"Ftruncate",name:"Ftruncate",pkg:"",typ:$funcType([$Int64],[$error],false)},{prop:"Init",name:"Init",pkg:"",typ:$funcType([$String,$Bool],[$error],false)},{prop:"destroy",name:"destroy",pkg:"internal/poll",typ:$funcType([],[$error],false)},{prop:"Close",name:"Close",pkg:"",typ:$funcType([],[$error],false)},{prop:"Shutdown",name:"Shutdown",pkg:"",typ:$funcType([$Int],[$error],false)},{prop:"SetBlocking",name:"SetBlocking",pkg:"",typ:$funcType([],[$error],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([AY],[$Int,$error],false)},{prop:"Pread",name:"Pread",pkg:"",typ:$funcType([AY,$Int64],[$Int,$error],false)},{prop:"ReadFrom",name:"ReadFrom",pkg:"",typ:$funcType([AY],[$Int,C.Sockaddr,$error],false)},{prop:"ReadMsg",name:"ReadMsg",pkg:"",typ:$funcType([AY,AY],[$Int,$Int,$Int,C.Sockaddr,$error],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([AY],[$Int,$error],false)},{prop:"Pwrite",name:"Pwrite",pkg:"",typ:$funcType([AY,$Int64],[$Int,$error],false)},{prop:"WriteTo",name:"WriteTo",pkg:"",typ:$funcType([AY,C.Sockaddr],[$Int,$error],false)},{prop:"WriteMsg",name:"WriteMsg",pkg:"",typ:$funcType([AY,AY,C.Sockaddr],[$Int,$Int,$error],false)},{prop:"Accept",name:"Accept",pkg:"",typ:$funcType([],[$Int,C.Sockaddr,$String,$error],false)},{prop:"Seek",name:"Seek",pkg:"",typ:$funcType([$Int64,$Int],[$Int64,$error],false)},{prop:"ReadDirent",name:"ReadDirent",pkg:"",typ:$funcType([AY],[$Int,$error],false)},{prop:"Fchdir",name:"Fchdir",pkg:"",typ:$funcType([],[$error],false)},{prop:"Fstat",name:"Fstat",pkg:"",typ:$funcType([AZ],[$error],false)},{prop:"Dup",name:"Dup",pkg:"",typ:$funcType([],[$Int,$String,$error],false)},{prop:"WaitWrite",name:"WaitWrite",pkg:"",typ:$funcType([],[$error],false)},{prop:"WriteOnce",name:"WriteOnce",pkg:"",typ:$funcType([AY],[$Int,$error],false)},{prop:"RawControl",name:"RawControl",pkg:"",typ:$funcType([BA],[$error],false)},{prop:"RawRead",name:"RawRead",pkg:"",typ:$funcType([BB],[$error],false)},{prop:"RawWrite",name:"RawWrite",pkg:"",typ:$funcType([BB],[$error],false)},{prop:"SetsockoptInt",name:"SetsockoptInt",pkg:"",typ:$funcType([$Int,$Int,$Int],[$error],false)},{prop:"SetsockoptInet4Addr",name:"SetsockoptInet4Addr",pkg:"",typ:$funcType([$Int,$Int,AM],[$error],false)},{prop:"SetsockoptLinger",name:"SetsockoptLinger",pkg:"",typ:$funcType([$Int,$Int,BC],[$error],false)},{prop:"SetsockoptIPMreqn",name:"SetsockoptIPMreqn",pkg:"",typ:$funcType([$Int,$Int,BD],[$error],false)},{prop:"SetsockoptByte",name:"SetsockoptByte",pkg:"",typ:$funcType([$Int,$Int,$Uint8],[$error],false)},{prop:"SetsockoptIPMreq",name:"SetsockoptIPMreq",pkg:"",typ:$funcType([$Int,$Int,BE],[$error],false)},{prop:"SetsockoptIPv6Mreq",name:"SetsockoptIPv6Mreq",pkg:"",typ:$funcType([$Int,$Int,BF],[$error],false)},{prop:"Writev",name:"Writev",pkg:"",typ:$funcType([BH],[$Int64,$error],false)}];G.init("internal/poll",[{prop:"closing",name:"closing",embedded:false,exported:false,typ:$Bool,tag:""}]);M.init("",[]);P.init("internal/poll",[{prop:"state",name:"state",embedded:false,exported:false,typ:$Uint64,tag:""},{prop:"rsema",name:"rsema",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"wsema",name:"wsema",embedded:false,exported:false,typ:$Uint32,tag:""}]);Q.init("internal/poll",[{prop:"fdmu",name:"fdmu",embedded:false,exported:false,typ:P,tag:""},{prop:"Sysfd",name:"Sysfd",embedded:false,exported:true,typ:$Int,tag:""},{prop:"pd",name:"pd",embedded:false,exported:false,typ:G,tag:""},{prop:"iovecs",name:"iovecs",embedded:false,exported:false,typ:AS,tag:""},{prop:"csema",name:"csema",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"isBlocking",name:"isBlocking",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"IsStream",name:"IsStream",embedded:false,exported:true,typ:$Bool,tag:""},{prop:"ZeroReadIsEOF",name:"ZeroReadIsEOF",embedded:false,exported:true,typ:$Bool,tag:""},{prop:"isFile",name:"isFile",embedded:false,exported:false,typ:$Bool,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=B.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}K={};$pkg.ErrNetClosing=B.New("use of closed network connection");$pkg.ErrFileClosing=B.New("use of closed file");$pkg.ErrNoDeadline=B.New("file type does not support deadline");$pkg.ErrTimeout=new M.ptr();$pkg.TestHookDidWritev=(function(c){var c;});R=1;$pkg.Accept4Func=C.Accept4;$pkg.CloseFunc=C.Close;$pkg.AcceptFunc=C.Accept;}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["internal/syscall/unix"]=(function(){var $pkg={},$init,B,A,C;B=$packages["sync/atomic"];A=$packages["syscall"];C=function(b){var b,c,d,e,f;c=false;d=$ifaceNil;e=false;f=$ifaceNil;c=e;d=f;return[c,d];};$pkg.IsNonblock=C;$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=B.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["internal/testlog"]=(function(){var $pkg={},$init,A,B,I,C,E,G,H;A=$packages["sync/atomic"];B=$pkg.Interface=$newType(8,$kindInterface,"testlog.Interface",true,"internal/testlog",true,null);I=$ptrType(B);E=function(){var a;a=C.Load();if($interfaceIsEqual(a,$ifaceNil)){return $ifaceNil;}return $assertType(a,I).$get();};$pkg.Logger=E;G=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=E();if(!($interfaceIsEqual(b,$ifaceNil))){$s=1;continue;}$s=2;continue;case 1:$r=b.Open(a);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:G};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Open=G;H=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=E();if(!($interfaceIsEqual(b,$ifaceNil))){$s=1;continue;}$s=2;continue;case 1:$r=b.Stat(a);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:H};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Stat=H;B.init([{prop:"Chdir",name:"Chdir",pkg:"",typ:$funcType([$String],[],false)},{prop:"Getenv",name:"Getenv",pkg:"",typ:$funcType([$String],[],false)},{prop:"Open",name:"Open",pkg:"",typ:$funcType([$String],[],false)},{prop:"Stat",name:"Stat",pkg:"",typ:$funcType([$String],[],false)}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}C=new A.Value.ptr($ifaceNil);}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["os"]=(function(){var $pkg={},$init,A,B,G,K,F,C,D,H,I,E,J,P,AC,AD,AE,BC,BZ,DC,DT,DU,DV,DX,EA,EB,EC,ED,EE,EF,EG,EH,ET,EU,EV,EW,EX,FB,FG,FH,FL,FM,FN,AZ,BJ,L,M,N,AF,AH,AK,AL,AN,BE,BG,BI,BP,BQ,BS,BT,BX,CA,CC,CD,CE,CR,CV,DB,DD,DH,DI,DJ,DK,DM,DN;A=$packages["errors"];B=$packages["github.com/gopherjs/gopherjs/js"];G=$packages["internal/poll"];K=$packages["internal/syscall/unix"];F=$packages["internal/testlog"];C=$packages["io"];D=$packages["runtime"];H=$packages["sync"];I=$packages["sync/atomic"];E=$packages["syscall"];J=$packages["time"];P=$pkg.dirInfo=$newType(0,$kindStruct,"os.dirInfo",true,"os",false,function(buf_,nbuf_,bufp_){this.$val=this;if(arguments.length===0){this.buf=EE.nil;this.nbuf=0;this.bufp=0;return;}this.buf=buf_;this.nbuf=nbuf_;this.bufp=bufp_;});AC=$pkg.timeout=$newType(8,$kindInterface,"os.timeout",true,"os",false,null);AD=$pkg.PathError=$newType(0,$kindStruct,"os.PathError",true,"os",true,function(Op_,Path_,Err_){this.$val=this;if(arguments.length===0){this.Op="";this.Path="";this.Err=$ifaceNil;return;}this.Op=Op_;this.Path=Path_;this.Err=Err_;});AE=$pkg.SyscallError=$newType(0,$kindStruct,"os.SyscallError",true,"os",true,function(Syscall_,Err_){this.$val=this;if(arguments.length===0){this.Syscall="";this.Err=$ifaceNil;return;}this.Syscall=Syscall_;this.Err=Err_;});BC=$pkg.LinkError=$newType(0,$kindStruct,"os.LinkError",true,"os",true,function(Op_,Old_,New_,Err_){this.$val=this;if(arguments.length===0){this.Op="";this.Old="";this.New="";this.Err=$ifaceNil;return;}this.Op=Op_;this.Old=Old_;this.New=New_;this.Err=Err_;});BZ=$pkg.file=$newType(0,$kindStruct,"os.file",true,"os",false,function(pfd_,name_,dirinfo_,nonblock_,stdoutOrErr_){this.$val=this;if(arguments.length===0){this.pfd=new G.FD.ptr(new G.fdMutex.ptr(new $Uint64(0,0),0,0),0,new G.pollDesc.ptr(false),EU.nil,0,0,false,false,false);this.name="";this.dirinfo=ED.nil;this.nonblock=false;this.stdoutOrErr=false;return;}this.pfd=pfd_;this.name=name_;this.dirinfo=dirinfo_;this.nonblock=nonblock_;this.stdoutOrErr=stdoutOrErr_;});DC=$pkg.rawConn=$newType(0,$kindStruct,"os.rawConn",true,"os",false,function(file_){this.$val=this;if(arguments.length===0){this.file=EB.nil;return;}this.file=file_;});DT=$pkg.File=$newType(0,$kindStruct,"os.File",true,"os",true,function(file_){this.$val=this;if(arguments.length===0){this.file=EW.nil;return;}this.file=file_;});DU=$pkg.FileInfo=$newType(8,$kindInterface,"os.FileInfo",true,"os",true,null);DV=$pkg.FileMode=$newType(4,$kindUint32,"os.FileMode",true,"os",true,null);DX=$pkg.fileStat=$newType(0,$kindStruct,"os.fileStat",true,"os",false,function(name_,size_,mode_,modTime_,sys_){this.$val=this;if(arguments.length===0){this.name="";this.size=new $Int64(0,0);this.mode=0;this.modTime=new J.Time.ptr(new $Uint64(0,0),new $Int64(0,0),FB.nil);this.sys=new E.Stat_t.ptr(new $Uint64(0,0),new $Uint64(0,0),new $Uint64(0,0),0,0,0,0,new $Uint64(0,0),new $Int64(0,0),new $Int64(0,0),new $Int64(0,0),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),EV.zero());return;}this.name=name_;this.size=size_;this.mode=mode_;this.modTime=modTime_;this.sys=sys_;});EA=$sliceType($String);EB=$ptrType(DT);EC=$sliceType(DU);ED=$ptrType(P);EE=$sliceType($Uint8);EF=$ptrType(AD);EG=$ptrType(BC);EH=$ptrType(AE);ET=$sliceType(E.Iovec);EU=$ptrType(ET);EV=$arrayType($Int64,3);EW=$ptrType(BZ);EX=$funcType([EW],[$error],false);FB=$ptrType(J.Location);FG=$arrayType($Uint8,32);FH=$ptrType(DX);FL=$funcType([$Uintptr],[],false);FM=$funcType([$Uintptr],[$Bool],false);FN=$ptrType(DC);L=function(){return $pkg.Args;};M=function(){var c,d,e;c=$global.process;if(!(c===undefined)){d=c.argv;$pkg.Args=$makeSlice(EA,($parseInt(d.length)-1>>0));e=0;while(true){if(!(e<($parseInt(d.length)-1>>0))){break;}((e<0||e>=$pkg.Args.$length)?($throwRuntimeError("index out of range"),undefined):$pkg.Args.$array[$pkg.Args.$offset+e]=$internalize(d[(e+1>>0)],$String));e=e+(1)>>0;}}if($pkg.Args.$length===0){$pkg.Args=new EA(["?"]);}};N=function(){};DT.ptr.prototype.Readdir=function(c){var c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;if(d===EB.nil){$s=-1;return[EC.nil,$pkg.ErrInvalid];}e=d.readdir(c);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Readdir};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Readdir=function(c){return this.$val.Readdir(c);};DT.ptr.prototype.Readdirnames=function(c){var c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=EA.nil;e=$ifaceNil;f=this;if(f===EB.nil){g=EA.nil;h=$pkg.ErrInvalid;d=g;e=h;$s=-1;return[d,e];}j=f.readdirnames(c);$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}i=j;d=i[0];e=i[1];$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Readdirnames};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Readdirnames=function(c){return this.$val.Readdirnames(c);};P.ptr.prototype.close=function(){var c;c=this;};P.prototype.close=function(){return this.$val.close();};DT.ptr.prototype.readdirnames=function(c){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=EA.nil;e=$ifaceNil;f=this;if(f.file.dirinfo===ED.nil){f.file.dirinfo=new P.ptr(EE.nil,0,0);f.file.dirinfo.buf=$makeSlice(EE,8192);}g=f.file.dirinfo;h=c;if(h<=0){h=100;c=-1;}d=$makeSlice(EA,0,h);case 1:if(!(!((c===0)))){$s=2;continue;}if(g.bufp>=g.nbuf){$s=3;continue;}$s=4;continue;case 3:g.bufp=0;i=$ifaceNil;k=f.file.pfd.ReadDirent(g.buf);$s=5;case 5:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;g.nbuf=j[0];i=j[1];D.KeepAlive(f);if(!($interfaceIsEqual(i,$ifaceNil))){l=d;m=AL("readdirent",i);d=l;e=m;$s=-1;return[d,e];}if(g.nbuf<=0){$s=2;continue;}case 4:n=0;o=0;p=n;q=o;r=E.ParseDirent($subslice(g.buf,g.bufp,g.nbuf),c,d);p=r[0];q=r[1];d=r[2];g.bufp=g.bufp+(p)>>0;c=c-(q)>>0;$s=1;continue;case 2:if(c>=0&&(d.$length===0)){s=d;t=C.EOF;d=s;e=t;$s=-1;return[d,e];}u=d;v=$ifaceNil;d=u;e=v;$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.readdirnames};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.readdirnames=function(c){return this.$val.readdirnames(c);};AD.ptr.prototype.Error=function(){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.Err.Error();$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return c.Op+" "+c.Path+": "+d;}return;}if($f===undefined){$f={$blk:AD.ptr.prototype.Error};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AD.prototype.Error=function(){return this.$val.Error();};AD.ptr.prototype.Timeout=function(){var c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=$assertType(c.Err,AC,true);e=d[0];f=d[1];if(!(f)){g=false;$s=1;continue s;}h=e.Timeout();$s=2;case 2:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;case 1:$s=-1;return g;}return;}if($f===undefined){$f={$blk:AD.ptr.prototype.Timeout};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};AD.prototype.Timeout=function(){return this.$val.Timeout();};AE.ptr.prototype.Error=function(){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.Err.Error();$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return c.Syscall+": "+d;}return;}if($f===undefined){$f={$blk:AE.ptr.prototype.Error};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AE.prototype.Error=function(){return this.$val.Error();};AE.ptr.prototype.Timeout=function(){var c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=$assertType(c.Err,AC,true);e=d[0];f=d[1];if(!(f)){g=false;$s=1;continue s;}h=e.Timeout();$s=2;case 2:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;case 1:$s=-1;return g;}return;}if($f===undefined){$f={$blk:AE.ptr.prototype.Timeout};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};AE.prototype.Timeout=function(){return this.$val.Timeout();};AF=function(c,d){var c,d;if($interfaceIsEqual(d,$ifaceNil)){return $ifaceNil;}return new AE.ptr(c,d);};$pkg.NewSyscallError=AF;AH=function(c){var c;return AN(c);};$pkg.IsNotExist=AH;AK=function(c){var c,d,e,f,g;d=c;if($assertType(d,EF,true)[1]){e=d.$val;return e.Err;}else if($assertType(d,EG,true)[1]){f=d.$val;return f.Err;}else if($assertType(d,EH,true)[1]){g=d.$val;return g.Err;}return c;};AL=function(c,d){var c,d,e,f;e=$assertType(d,E.Errno,true);f=e[1];if(f){d=AF(c,d);}return d;};AN=function(c){var c;c=AK(c);return $interfaceIsEqual(c,new E.Errno(2))||$interfaceIsEqual(c,$pkg.ErrNotExist);};DT.ptr.prototype.Name=function(){var c;c=this;return c.file.name;};DT.prototype.Name=function(){return this.$val.Name();};BC.ptr.prototype.Error=function(){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.Err.Error();$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return c.Op+" "+c.Old+" "+c.New+": "+d;}return;}if($f===undefined){$f={$blk:BC.ptr.prototype.Error};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};BC.prototype.Error=function(){return this.$val.Error();};DT.ptr.prototype.Read=function(c){var c,d,e,f,g,h,i,j,k,l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=0;e=$ifaceNil;f=this;g=f.checkValid("read");if(!($interfaceIsEqual(g,$ifaceNil))){h=0;i=g;d=h;e=i;$s=-1;return[d,e];}k=f.read(c);$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;d=j[0];l=j[1];m=d;n=f.wrapErr("read",l);d=m;e=n;$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Read};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Read=function(c){return this.$val.Read(c);};DT.ptr.prototype.ReadAt=function(c,d){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=0;f=$ifaceNil;g=this;h=g.checkValid("read");if(!($interfaceIsEqual(h,$ifaceNil))){i=0;j=h;e=i;f=j;$s=-1;return[e,f];}if((d.$high<0||(d.$high===0&&d.$low<0))){k=0;l=new AD.ptr("readat",g.file.name,A.New("negative offset"));e=k;f=l;$s=-1;return[e,f];}case 1:if(!(c.$length>0)){$s=2;continue;}n=g.pread(c,d);$s=3;case 3:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m[0];p=m[1];if(!($interfaceIsEqual(p,$ifaceNil))){f=g.wrapErr("read",p);$s=2;continue;}e=e+(o)>>0;c=$subslice(c,o);d=(q=(new $Int64(0,o)),new $Int64(d.$high+q.$high,d.$low+q.$low));$s=1;continue;case 2:$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.ReadAt};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.ReadAt=function(c,d){return this.$val.ReadAt(c,d);};DT.ptr.prototype.Write=function(c){var c,d,e,f,g,h,i,j,k,l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=0;e=$ifaceNil;f=this;g=f.checkValid("write");if(!($interfaceIsEqual(g,$ifaceNil))){h=0;i=g;d=h;e=i;$s=-1;return[d,e];}k=f.write(c);$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;d=j[0];l=j[1];if(d<0){d=0;}if(!((d===c.$length))){e=C.ErrShortWrite;}CD(f,l);if(!($interfaceIsEqual(l,$ifaceNil))){e=f.wrapErr("write",l);}m=d;n=e;d=m;e=n;$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Write};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Write=function(c){return this.$val.Write(c);};DT.ptr.prototype.WriteAt=function(c,d){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=0;f=$ifaceNil;g=this;h=g.checkValid("write");if(!($interfaceIsEqual(h,$ifaceNil))){i=0;j=h;e=i;f=j;$s=-1;return[e,f];}if((d.$high<0||(d.$high===0&&d.$low<0))){k=0;l=new AD.ptr("writeat",g.file.name,A.New("negative offset"));e=k;f=l;$s=-1;return[e,f];}case 1:if(!(c.$length>0)){$s=2;continue;}n=g.pwrite(c,d);$s=3;case 3:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m[0];p=m[1];if(!($interfaceIsEqual(p,$ifaceNil))){f=g.wrapErr("write",p);$s=2;continue;}e=e+(o)>>0;c=$subslice(c,o);d=(q=(new $Int64(0,o)),new $Int64(d.$high+q.$high,d.$low+q.$low));$s=1;continue;case 2:$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.WriteAt};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.WriteAt=function(c,d){return this.$val.WriteAt(c,d);};DT.ptr.prototype.Seek=function(c,d){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=new $Int64(0,0);f=$ifaceNil;g=this;h=g.checkValid("seek");if(!($interfaceIsEqual(h,$ifaceNil))){i=new $Int64(0,0);j=h;e=i;f=j;$s=-1;return[e,f];}l=g.seek(c,d);$s=1;case 1:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}k=l;m=k[0];n=k[1];if($interfaceIsEqual(n,$ifaceNil)&&!(g.file.dirinfo===ED.nil)&&!((m.$high===0&&m.$low===0))){n=new E.Errno(21);}if(!($interfaceIsEqual(n,$ifaceNil))){o=new $Int64(0,0);p=g.wrapErr("seek",n);e=o;f=p;$s=-1;return[e,f];}q=m;r=$ifaceNil;e=q;f=r;$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Seek};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Seek=function(c,d){return this.$val.Seek(c,d);};DT.ptr.prototype.WriteString=function(c){var c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=0;e=$ifaceNil;f=this;h=f.Write((new EE($stringToBytes(c))));$s=1;case 1:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;d=g[0];e=g[1];$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.WriteString};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.WriteString=function(c){return this.$val.WriteString(c);};BE=function(c){var c,d,e,f,g,h,i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=DH(c);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;f=d[0];g=d[1];if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return g;}h=c;i=f.Mode();$s=2;case 2:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}j=(i|1048576)>>>0;k=BP(h,j);$s=3;case 3:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}$s=-1;return k;}return;}if($f===undefined){$f={$blk:BE};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};BG=function(c){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=BI(c,0,0);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:BG};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Open=BG;BI=function(c,d,e){var c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=F.Open(c);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}f=CE(c,d,e);$s=2;case 2:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$s=-1;return f;}return;}if($f===undefined){$f={$blk:BI};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};$pkg.OpenFile=BI;DT.ptr.prototype.wrapErr=function(c,d){var c,d,e;e=this;if($interfaceIsEqual(d,$ifaceNil)||$interfaceIsEqual(d,C.EOF)){return d;}if($interfaceIsEqual(d,G.ErrFileClosing)){d=$pkg.ErrClosed;}return new AD.ptr(c,e.file.name,d);};DT.prototype.wrapErr=function(c,d){return this.$val.wrapErr(c,d);};BP=function(c,d){var c,d;return BT(c,d);};$pkg.Chmod=BP;DT.ptr.prototype.Chmod=function(c){var c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=d.chmod(c);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Chmod};}$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Chmod=function(c){return this.$val.Chmod(c);};DT.ptr.prototype.SetDeadline=function(c){var c,d;d=this;return d.setDeadline($clone(c,J.Time));};DT.prototype.SetDeadline=function(c){return this.$val.SetDeadline(c);};DT.ptr.prototype.SetReadDeadline=function(c){var c,d;d=this;return d.setReadDeadline($clone(c,J.Time));};DT.prototype.SetReadDeadline=function(c){return this.$val.SetReadDeadline(c);};DT.ptr.prototype.SetWriteDeadline=function(c){var c,d;d=this;return d.setWriteDeadline($clone(c,J.Time));};DT.prototype.SetWriteDeadline=function(c){return this.$val.SetWriteDeadline(c);};DT.ptr.prototype.SyscallConn=function(){var c,d,e;c=this;d=c.checkValid("SyscallConn");if(!($interfaceIsEqual(d,$ifaceNil))){return[$ifaceNil,d];}e=DD(c);return[e[0],e[1]];};DT.prototype.SyscallConn=function(){return this.$val.SyscallConn();};BQ=function(){$throwRuntimeError("native function not implemented: os.sigpipe");};BS=function(c){var c,d;d=0;d=(d|(((new DV(c).Perm()>>>0))))>>>0;if(!((((c&8388608)>>>0)===0))){d=(d|(2048))>>>0;}if(!((((c&4194304)>>>0)===0))){d=(d|(1024))>>>0;}if(!((((c&1048576)>>>0)===0))){d=(d|(512))>>>0;}return d;};BT=function(c,d){var c,d,e;e=E.Chmod(BX(c),BS(d));if(!($interfaceIsEqual(e,$ifaceNil))){return new AD.ptr("chmod",c,e);}return $ifaceNil;};DT.ptr.prototype.chmod=function(c){var c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=d.checkValid("chmod");if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}f=d.file.pfd.Fchmod(BS(c));$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return d.wrapErr("chmod",g);}$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.chmod};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.chmod=function(c){return this.$val.chmod(c);};DT.ptr.prototype.Chown=function(c,d){var c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;f=e.checkValid("chown");if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return f;}g=e.file.pfd.Fchown(c,d);$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return e.wrapErr("chown",h);}$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Chown};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Chown=function(c,d){return this.$val.Chown(c,d);};DT.ptr.prototype.Truncate=function(c){var c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=d.checkValid("truncate");if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}f=d.file.pfd.Ftruncate(c);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return d.wrapErr("truncate",g);}$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Truncate};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Truncate=function(c){return this.$val.Truncate(c);};DT.ptr.prototype.Sync=function(){var c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.checkValid("sync");if(!($interfaceIsEqual(d,$ifaceNil))){$s=-1;return d;}e=c.file.pfd.Fsync();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return c.wrapErr("sync",f);}$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Sync};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Sync=function(){return this.$val.Sync();};DT.ptr.prototype.Chdir=function(){var c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=c.checkValid("chdir");if(!($interfaceIsEqual(d,$ifaceNil))){$s=-1;return d;}e=c.file.pfd.Fchdir();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return c.wrapErr("chdir",f);}$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Chdir};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Chdir=function(){return this.$val.Chdir();};DT.ptr.prototype.setDeadline=function(c){var c,d,e;d=this;e=d.checkValid("SetDeadline");if(!($interfaceIsEqual(e,$ifaceNil))){return e;}return d.file.pfd.SetDeadline($clone(c,J.Time));};DT.prototype.setDeadline=function(c){return this.$val.setDeadline(c);};DT.ptr.prototype.setReadDeadline=function(c){var c,d,e;d=this;e=d.checkValid("SetReadDeadline");if(!($interfaceIsEqual(e,$ifaceNil))){return e;}return d.file.pfd.SetReadDeadline($clone(c,J.Time));};DT.prototype.setReadDeadline=function(c){return this.$val.setReadDeadline(c);};DT.ptr.prototype.setWriteDeadline=function(c){var c,d,e;d=this;e=d.checkValid("SetWriteDeadline");if(!($interfaceIsEqual(e,$ifaceNil))){return e;}return d.file.pfd.SetWriteDeadline($clone(c,J.Time));};DT.prototype.setWriteDeadline=function(c){return this.$val.setWriteDeadline(c);};DT.ptr.prototype.checkValid=function(c){var c,d;d=this;if(d===EB.nil){return $pkg.ErrInvalid;}return $ifaceNil;};DT.prototype.checkValid=function(c){return this.$val.checkValid(c);};BX=function(c){var c;return c;};DT.ptr.prototype.Fd=function(){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(c===EB.nil){$s=-1;return 4294967295;}if(c.file.nonblock){$s=1;continue;}$s=2;continue;case 1:d=c.file.pfd.SetBlocking();$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}d;case 2:$s=-1;return((c.file.pfd.Sysfd>>>0));}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Fd};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Fd=function(){return this.$val.Fd();};CA=function(c,d){var c,d,e,f,g,h;e=0;f=K.IsNonblock(((c>>0)));g=f[0];h=f[1];if($interfaceIsEqual(h,$ifaceNil)&&g){e=3;}return CC(c,d,e);};$pkg.NewFile=CA;CC=function(c,d,e){var c,d,e,f,g,h,i,j,k,l,m,n;f=((c>>0));if(f<0){return EB.nil;}g=new DT.ptr(new BZ.ptr(new G.FD.ptr(new G.fdMutex.ptr(new $Uint64(0,0),0,0),f,new G.pollDesc.ptr(false),EU.nil,0,0,true,true,false),d,ED.nil,false,(f===1)||(f===2)));h=(e===1)||(e===2)||(e===3);if(e===1){i=new E.Stat_t.ptr(new $Uint64(0,0),new $Uint64(0,0),new $Uint64(0,0),0,0,0,0,new $Uint64(0,0),new $Int64(0,0),new $Int64(0,0),new $Int64(0,0),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),EV.zero());j="linux";if(j===("freebsd")){h=false;}else if(j===("dragonfly")||j===("netbsd")||j===("openbsd")){k=E.Fstat(f,i);if($interfaceIsEqual(k,$ifaceNil)&&(((i.Mode&61440)>>>0)===32768)){h=false;}}else if(j===("darwin")){l=E.Fstat(f,i);if($interfaceIsEqual(l,$ifaceNil)&&((((i.Mode&61440)>>>0)===4096)||(((i.Mode&61440)>>>0)===32768))){h=false;}}}m=g.file.pfd.Init("file",h);if(!($interfaceIsEqual(m,$ifaceNil))){}else if(h){n=E.SetNonblock(f,true);if($interfaceIsEqual(n,$ifaceNil)){g.file.nonblock=true;}}D.SetFinalizer(g.file,new EX($methodExpr(EW,"close")));return g;};CD=function(c,d){var c,d;if($interfaceIsEqual(d,new E.Errno(32))&&c.file.stdoutOrErr){BQ();}};CE=function(c,d,e){var c,d,e,f,g,h,i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:f=false;if(false&&!(((d&64)===0))&&!((((e&1048576)>>>0)===0))){$s=1;continue;}$s=2;continue;case 1:h=DH(c);$s=3;case 3:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;i=g[1];if(AH(i)){f=true;}case 2:j=0;case 4:k=$ifaceNil;l=E.Open(c,d|524288,BS(e));j=l[0];k=l[1];if($interfaceIsEqual(k,$ifaceNil)){$s=5;continue;}if(false&&$interfaceIsEqual(k,new E.Errno(4))){$s=4;continue;}$s=-1;return[EB.nil,new AD.ptr("open",c,k)];$s=4;continue;case 5:if(f){$s=6;continue;}$s=7;continue;case 6:m=BE(c);$s=8;case 8:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}m;case 7:if(false){E.CloseOnExec(j);}$s=-1;return[CC(((j>>>0)),c,1),$ifaceNil];}return;}if($f===undefined){$f={$blk:CE};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};DT.ptr.prototype.Close=function(){var c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(c===EB.nil){$s=-1;return $pkg.ErrInvalid;}d=c.file.close();$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Close};}$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Close=function(){return this.$val.Close();};BZ.ptr.prototype.close=function(){var c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(c===EW.nil){$s=-1;return new E.Errno(22);}if(!(c.dirinfo===ED.nil)){c.dirinfo.close();}d=$ifaceNil;e=c.pfd.Close();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){if($interfaceIsEqual(f,G.ErrFileClosing)){f=$pkg.ErrClosed;}d=new AD.ptr("close",c.name,f);}D.SetFinalizer(c,$ifaceNil);$s=-1;return d;}return;}if($f===undefined){$f={$blk:BZ.ptr.prototype.close};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};BZ.prototype.close=function(){return this.$val.close();};DT.ptr.prototype.read=function(c){var c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=0;e=$ifaceNil;f=this;h=f.file.pfd.Read(c);$s=1;case 1:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;d=g[0];e=g[1];D.KeepAlive(f);i=d;j=e;d=i;e=j;$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.read};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.read=function(c){return this.$val.read(c);};DT.ptr.prototype.pread=function(c,d){var c,d,e,f,g,h,i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=0;f=$ifaceNil;g=this;i=g.file.pfd.Pread(c,d);$s=1;case 1:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;e=h[0];f=h[1];D.KeepAlive(g);j=e;k=f;e=j;f=k;$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.pread};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.pread=function(c,d){return this.$val.pread(c,d);};DT.ptr.prototype.write=function(c){var c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=0;e=$ifaceNil;f=this;h=f.file.pfd.Write(c);$s=1;case 1:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;d=g[0];e=g[1];D.KeepAlive(f);i=d;j=e;d=i;e=j;$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.write};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.write=function(c){return this.$val.write(c);};DT.ptr.prototype.pwrite=function(c,d){var c,d,e,f,g,h,i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=0;f=$ifaceNil;g=this;i=g.file.pfd.Pwrite(c,d);$s=1;case 1:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;e=h[0];f=h[1];D.KeepAlive(g);j=e;k=f;e=j;f=k;$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.pwrite};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.pwrite=function(c,d){return this.$val.pwrite(c,d);};DT.ptr.prototype.seek=function(c,d){var c,d,e,f,g,h,i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=new $Int64(0,0);f=$ifaceNil;g=this;i=g.file.pfd.Seek(c,d);$s=1;case 1:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;e=h[0];f=h[1];D.KeepAlive(g);j=e;k=f;e=j;f=k;$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.seek};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.seek=function(c,d){return this.$val.seek(c,d);};DT.ptr.prototype.readdir=function(c){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=EC.nil;e=$ifaceNil;f=this;g=f.file.name;if(g===""){g=".";}i=f.Readdirnames(c);$s=1;case 1:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;j=h[0];e=h[1];d=$makeSlice(EC,0,j.$length);k=j;l=0;case 2:if(!(l=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+l]);o=BJ(g+"/"+m);$s=4;case 4:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;p=n[0];q=n[1];if(AH(q)){l++;$s=2;continue;}if(!($interfaceIsEqual(q,$ifaceNil))){r=d;s=q;d=r;e=s;$s=-1;return[d,e];}d=$append(d,p);l++;$s=2;continue;case 3:if((d.$length===0)&&$interfaceIsEqual(e,$ifaceNil)&&c>0){e=C.EOF;}t=d;u=e;d=t;e=u;$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.readdir};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.readdir=function(c){return this.$val.readdir(c);};CR=function(c){var c,d;d=c.length-1>>0;while(true){if(!(d>0&&(c.charCodeAt(d)===47))){break;}c=$substring(c,0,d);d=d-(1)>>0;}d=d-(1)>>0;while(true){if(!(d>=0)){break;}if(c.charCodeAt(d)===47){c=$substring(c,(d+1>>0));break;}d=d-(1)>>0;}return c;};CV=function(){if(false){return;}$pkg.Args=L();};DB=function(c){var c;if(c===0){N();}E.Exit(c);};$pkg.Exit=DB;DC.ptr.prototype.Control=function(c){var c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=d.file.checkValid("SyscallConn.Control");if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}f=d.file.file.pfd.RawControl(c);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;D.KeepAlive(d.file);$s=-1;return g;}return;}if($f===undefined){$f={$blk:DC.ptr.prototype.Control};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};DC.prototype.Control=function(c){return this.$val.Control(c);};DC.ptr.prototype.Read=function(c){var c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=d.file.checkValid("SyscallConn.Read");if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}f=d.file.file.pfd.RawRead(c);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;D.KeepAlive(d.file);$s=-1;return g;}return;}if($f===undefined){$f={$blk:DC.ptr.prototype.Read};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};DC.prototype.Read=function(c){return this.$val.Read(c);};DC.ptr.prototype.Write=function(c){var c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=d.file.checkValid("SyscallConn.Write");if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return e;}f=d.file.file.pfd.RawWrite(c);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;D.KeepAlive(d.file);$s=-1;return g;}return;}if($f===undefined){$f={$blk:DC.ptr.prototype.Write};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};DC.prototype.Write=function(c){return this.$val.Write(c);};DD=function(c){var c;return[new DC.ptr(c),$ifaceNil];};DH=function(c){var c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=F.Stat(c);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return DM(c);}return;}if($f===undefined){$f={$blk:DH};}$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Stat=DH;DI=function(c){var c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=F.Stat(c);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return DN(c);}return;}if($f===undefined){$f={$blk:DI};}$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Lstat=DI;DJ=function(c,d){var c,d,e;c.name=CR(d);c.size=c.sys.Size;J.Time.copy(c.modTime,DK($clone(c.sys.Mtim,E.Timespec)));c.mode=((((c.sys.Mode&511)>>>0)>>>0));e=(c.sys.Mode&61440)>>>0;if(e===(24576)){c.mode=(c.mode|(67108864))>>>0;}else if(e===(8192)){c.mode=(c.mode|(69206016))>>>0;}else if(e===(16384)){c.mode=(c.mode|(2147483648))>>>0;}else if(e===(4096)){c.mode=(c.mode|(33554432))>>>0;}else if(e===(40960)){c.mode=(c.mode|(134217728))>>>0;}else if(e===(32768)){}else if(e===(49152)){c.mode=(c.mode|(16777216))>>>0;}if(!((((c.sys.Mode&1024)>>>0)===0))){c.mode=(c.mode|(4194304))>>>0;}if(!((((c.sys.Mode&2048)>>>0)===0))){c.mode=(c.mode|(8388608))>>>0;}if(!((((c.sys.Mode&512)>>>0)===0))){c.mode=(c.mode|(1048576))>>>0;}};DK=function(c){var c;return J.Unix((c.Sec),(c.Nsec));};DT.ptr.prototype.Stat=function(){var c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=[c];d=this;if(d===EB.nil){$s=-1;return[$ifaceNil,$pkg.ErrInvalid];}c[0]=new DX.ptr("",new $Int64(0,0),0,new J.Time.ptr(new $Uint64(0,0),new $Int64(0,0),FB.nil),new E.Stat_t.ptr(new $Uint64(0,0),new $Uint64(0,0),new $Uint64(0,0),0,0,0,0,new $Uint64(0,0),new $Int64(0,0),new $Int64(0,0),new $Int64(0,0),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),EV.zero()));e=d.file.pfd.Fstat(c[0].sys);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[$ifaceNil,new AD.ptr("stat",d.file.name,f)];}DJ(c[0],d.file.name);$s=-1;return[c[0],$ifaceNil];}return;}if($f===undefined){$f={$blk:DT.ptr.prototype.Stat};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};DT.prototype.Stat=function(){return this.$val.Stat();};DM=function(c){var c,d,e;d=new DX.ptr("",new $Int64(0,0),0,new J.Time.ptr(new $Uint64(0,0),new $Int64(0,0),FB.nil),new E.Stat_t.ptr(new $Uint64(0,0),new $Uint64(0,0),new $Uint64(0,0),0,0,0,0,new $Uint64(0,0),new $Int64(0,0),new $Int64(0,0),new $Int64(0,0),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),EV.zero()));e=E.Stat(c,d.sys);if(!($interfaceIsEqual(e,$ifaceNil))){return[$ifaceNil,new AD.ptr("stat",c,e)];}DJ(d,c);return[d,$ifaceNil];};DN=function(c){var c,d,e;d=new DX.ptr("",new $Int64(0,0),0,new J.Time.ptr(new $Uint64(0,0),new $Int64(0,0),FB.nil),new E.Stat_t.ptr(new $Uint64(0,0),new $Uint64(0,0),new $Uint64(0,0),0,0,0,0,new $Uint64(0,0),new $Int64(0,0),new $Int64(0,0),new $Int64(0,0),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),new E.Timespec.ptr(new $Int64(0,0),new $Int64(0,0)),EV.zero()));e=E.Lstat(c,d.sys);if(!($interfaceIsEqual(e,$ifaceNil))){return[$ifaceNil,new AD.ptr("lstat",c,e)];}DJ(d,c);return[d,$ifaceNil];};DV.prototype.String=function(){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;c=this.$val;d=FG.zero();e=0;f="dalTLDpSugct?";g=0;while(true){if(!(g>0)>>>0)),k<32?(1<>>0)))>>>0)===0))){((e<0||e>=d.length)?($throwRuntimeError("index out of range"),undefined):d[e]=((j<<24>>>24)));e=e+(1)>>0;}g+=h[1];}if(e===0){((e<0||e>=d.length)?($throwRuntimeError("index out of range"),undefined):d[e]=45);e=e+(1)>>0;}l="rwxrwxrwx";m=0;while(true){if(!(m>0)>>>0)),q<32?(1<>>0)))>>>0)===0))){((e<0||e>=d.length)?($throwRuntimeError("index out of range"),undefined):d[e]=((p<<24>>>24)));}else{((e<0||e>=d.length)?($throwRuntimeError("index out of range"),undefined):d[e]=45);}e=e+(1)>>0;m+=n[1];}return($bytesToString($subslice(new EE(d),0,e)));};$ptrType(DV).prototype.String=function(){return new DV(this.$get()).String();};DV.prototype.IsDir=function(){var c;c=this.$val;return!((((c&2147483648)>>>0)===0));};$ptrType(DV).prototype.IsDir=function(){return new DV(this.$get()).IsDir();};DV.prototype.IsRegular=function(){var c;c=this.$val;return((c&2401763328)>>>0)===0;};$ptrType(DV).prototype.IsRegular=function(){return new DV(this.$get()).IsRegular();};DV.prototype.Perm=function(){var c;c=this.$val;return(c&511)>>>0;};$ptrType(DV).prototype.Perm=function(){return new DV(this.$get()).Perm();};DX.ptr.prototype.Name=function(){var c;c=this;return c.name;};DX.prototype.Name=function(){return this.$val.Name();};DX.ptr.prototype.IsDir=function(){var c;c=this;return new DV(c.Mode()).IsDir();};DX.prototype.IsDir=function(){return this.$val.IsDir();};DX.ptr.prototype.Size=function(){var c;c=this;return c.size;};DX.prototype.Size=function(){return this.$val.Size();};DX.ptr.prototype.Mode=function(){var c;c=this;return c.mode;};DX.prototype.Mode=function(){return this.$val.Mode();};DX.ptr.prototype.ModTime=function(){var c;c=this;return c.modTime;};DX.prototype.ModTime=function(){return this.$val.ModTime();};DX.ptr.prototype.Sys=function(){var c;c=this;return c.sys;};DX.prototype.Sys=function(){return this.$val.Sys();};ED.methods=[{prop:"close",name:"close",pkg:"os",typ:$funcType([],[],false)}];EF.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)}];EH.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)}];EG.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];EW.methods=[{prop:"close",name:"close",pkg:"os",typ:$funcType([],[$error],false)}];FN.methods=[{prop:"Control",name:"Control",pkg:"",typ:$funcType([FL],[$error],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([FM],[$error],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([FM],[$error],false)}];EB.methods=[{prop:"Readdir",name:"Readdir",pkg:"",typ:$funcType([$Int],[EC,$error],false)},{prop:"Readdirnames",name:"Readdirnames",pkg:"",typ:$funcType([$Int],[EA,$error],false)},{prop:"readdirnames",name:"readdirnames",pkg:"os",typ:$funcType([$Int],[EA,$error],false)},{prop:"Name",name:"Name",pkg:"",typ:$funcType([],[$String],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([EE],[$Int,$error],false)},{prop:"ReadAt",name:"ReadAt",pkg:"",typ:$funcType([EE,$Int64],[$Int,$error],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([EE],[$Int,$error],false)},{prop:"WriteAt",name:"WriteAt",pkg:"",typ:$funcType([EE,$Int64],[$Int,$error],false)},{prop:"Seek",name:"Seek",pkg:"",typ:$funcType([$Int64,$Int],[$Int64,$error],false)},{prop:"WriteString",name:"WriteString",pkg:"",typ:$funcType([$String],[$Int,$error],false)},{prop:"wrapErr",name:"wrapErr",pkg:"os",typ:$funcType([$String,$error],[$error],false)},{prop:"Chmod",name:"Chmod",pkg:"",typ:$funcType([DV],[$error],false)},{prop:"SetDeadline",name:"SetDeadline",pkg:"",typ:$funcType([J.Time],[$error],false)},{prop:"SetReadDeadline",name:"SetReadDeadline",pkg:"",typ:$funcType([J.Time],[$error],false)},{prop:"SetWriteDeadline",name:"SetWriteDeadline",pkg:"",typ:$funcType([J.Time],[$error],false)},{prop:"SyscallConn",name:"SyscallConn",pkg:"",typ:$funcType([],[E.RawConn,$error],false)},{prop:"chmod",name:"chmod",pkg:"os",typ:$funcType([DV],[$error],false)},{prop:"Chown",name:"Chown",pkg:"",typ:$funcType([$Int,$Int],[$error],false)},{prop:"Truncate",name:"Truncate",pkg:"",typ:$funcType([$Int64],[$error],false)},{prop:"Sync",name:"Sync",pkg:"",typ:$funcType([],[$error],false)},{prop:"Chdir",name:"Chdir",pkg:"",typ:$funcType([],[$error],false)},{prop:"setDeadline",name:"setDeadline",pkg:"os",typ:$funcType([J.Time],[$error],false)},{prop:"setReadDeadline",name:"setReadDeadline",pkg:"os",typ:$funcType([J.Time],[$error],false)},{prop:"setWriteDeadline",name:"setWriteDeadline",pkg:"os",typ:$funcType([J.Time],[$error],false)},{prop:"checkValid",name:"checkValid",pkg:"os",typ:$funcType([$String],[$error],false)},{prop:"Fd",name:"Fd",pkg:"",typ:$funcType([],[$Uintptr],false)},{prop:"Close",name:"Close",pkg:"",typ:$funcType([],[$error],false)},{prop:"read",name:"read",pkg:"os",typ:$funcType([EE],[$Int,$error],false)},{prop:"pread",name:"pread",pkg:"os",typ:$funcType([EE,$Int64],[$Int,$error],false)},{prop:"write",name:"write",pkg:"os",typ:$funcType([EE],[$Int,$error],false)},{prop:"pwrite",name:"pwrite",pkg:"os",typ:$funcType([EE,$Int64],[$Int,$error],false)},{prop:"seek",name:"seek",pkg:"os",typ:$funcType([$Int64,$Int],[$Int64,$error],false)},{prop:"readdir",name:"readdir",pkg:"os",typ:$funcType([$Int],[EC,$error],false)},{prop:"Stat",name:"Stat",pkg:"",typ:$funcType([],[DU,$error],false)}];DV.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"IsDir",name:"IsDir",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"IsRegular",name:"IsRegular",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Perm",name:"Perm",pkg:"",typ:$funcType([],[DV],false)}];FH.methods=[{prop:"Name",name:"Name",pkg:"",typ:$funcType([],[$String],false)},{prop:"IsDir",name:"IsDir",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Size",name:"Size",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Mode",name:"Mode",pkg:"",typ:$funcType([],[DV],false)},{prop:"ModTime",name:"ModTime",pkg:"",typ:$funcType([],[J.Time],false)},{prop:"Sys",name:"Sys",pkg:"",typ:$funcType([],[$emptyInterface],false)}];P.init("os",[{prop:"buf",name:"buf",embedded:false,exported:false,typ:EE,tag:""},{prop:"nbuf",name:"nbuf",embedded:false,exported:false,typ:$Int,tag:""},{prop:"bufp",name:"bufp",embedded:false,exported:false,typ:$Int,tag:""}]);AC.init([{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)}]);AD.init("",[{prop:"Op",name:"Op",embedded:false,exported:true,typ:$String,tag:""},{prop:"Path",name:"Path",embedded:false,exported:true,typ:$String,tag:""},{prop:"Err",name:"Err",embedded:false,exported:true,typ:$error,tag:""}]);AE.init("",[{prop:"Syscall",name:"Syscall",embedded:false,exported:true,typ:$String,tag:""},{prop:"Err",name:"Err",embedded:false,exported:true,typ:$error,tag:""}]);BC.init("",[{prop:"Op",name:"Op",embedded:false,exported:true,typ:$String,tag:""},{prop:"Old",name:"Old",embedded:false,exported:true,typ:$String,tag:""},{prop:"New",name:"New",embedded:false,exported:true,typ:$String,tag:""},{prop:"Err",name:"Err",embedded:false,exported:true,typ:$error,tag:""}]);BZ.init("os",[{prop:"pfd",name:"pfd",embedded:false,exported:false,typ:G.FD,tag:""},{prop:"name",name:"name",embedded:false,exported:false,typ:$String,tag:""},{prop:"dirinfo",name:"dirinfo",embedded:false,exported:false,typ:ED,tag:""},{prop:"nonblock",name:"nonblock",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"stdoutOrErr",name:"stdoutOrErr",embedded:false,exported:false,typ:$Bool,tag:""}]);DC.init("os",[{prop:"file",name:"file",embedded:false,exported:false,typ:EB,tag:""}]);DT.init("os",[{prop:"file",name:"file",embedded:true,exported:false,typ:EW,tag:""}]);DU.init([{prop:"IsDir",name:"IsDir",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"ModTime",name:"ModTime",pkg:"",typ:$funcType([],[J.Time],false)},{prop:"Mode",name:"Mode",pkg:"",typ:$funcType([],[DV],false)},{prop:"Name",name:"Name",pkg:"",typ:$funcType([],[$String],false)},{prop:"Size",name:"Size",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Sys",name:"Sys",pkg:"",typ:$funcType([],[$emptyInterface],false)}]);DX.init("os",[{prop:"name",name:"name",embedded:false,exported:false,typ:$String,tag:""},{prop:"size",name:"size",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"mode",name:"mode",embedded:false,exported:false,typ:DV,tag:""},{prop:"modTime",name:"modTime",embedded:false,exported:false,typ:J.Time,tag:""},{prop:"sys",name:"sys",embedded:false,exported:false,typ:E.Stat_t,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=G.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=K.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=H.$init();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=I.$init();$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=J.$init();$s=11;case 11:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$pkg.Args=EA.nil;$pkg.ErrInvalid=A.New("invalid argument");$pkg.ErrPermission=A.New("permission denied");$pkg.ErrExist=A.New("file already exists");$pkg.ErrNotExist=A.New("file does not exist");$pkg.ErrClosed=A.New("file already closed");AZ=A.New("os: process already finished");$pkg.Stdin=CA(((E.Stdin>>>0)),"/dev/stdin");$pkg.Stdout=CA(((E.Stdout>>>0)),"/dev/stdout");$pkg.Stderr=CA(((E.Stderr>>>0)),"/dev/stderr");BJ=DI;M();CV();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["fmt"]=(function(){var $pkg={},$init,C,D,E,I,F,G,A,H,B,J,K,L,M,N,O,P,Q,AU,AV,AW,BI,BJ,BK,BL,BM,BN,BO,BP,BQ,BT,CO,CP,R,AX,BB,BD,BE,S,V,W,Z,AA,AB,AC,AD,AE,AF,AG,AH,AY,AZ,BF;C=$packages["errors"];D=$packages["internal/fmtsort"];E=$packages["io"];I=$packages["math"];F=$packages["os"];G=$packages["reflect"];A=$packages["strconv"];H=$packages["sync"];B=$packages["unicode/utf8"];J=$pkg.fmtFlags=$newType(0,$kindStruct,"fmt.fmtFlags",true,"fmt",false,function(widPresent_,precPresent_,minus_,plus_,sharp_,space_,zero_,plusV_,sharpV_){this.$val=this;if(arguments.length===0){this.widPresent=false;this.precPresent=false;this.minus=false;this.plus=false;this.sharp=false;this.space=false;this.zero=false;this.plusV=false;this.sharpV=false;return;}this.widPresent=widPresent_;this.precPresent=precPresent_;this.minus=minus_;this.plus=plus_;this.sharp=sharp_;this.space=space_;this.zero=zero_;this.plusV=plusV_;this.sharpV=sharpV_;});K=$pkg.fmt=$newType(0,$kindStruct,"fmt.fmt",true,"fmt",false,function(buf_,fmtFlags_,wid_,prec_,intbuf_){this.$val=this;if(arguments.length===0){this.buf=BK.nil;this.fmtFlags=new J.ptr(false,false,false,false,false,false,false,false,false);this.wid=0;this.prec=0;this.intbuf=BL.zero();return;}this.buf=buf_;this.fmtFlags=fmtFlags_;this.wid=wid_;this.prec=prec_;this.intbuf=intbuf_;});L=$pkg.State=$newType(8,$kindInterface,"fmt.State",true,"fmt",true,null);M=$pkg.Formatter=$newType(8,$kindInterface,"fmt.Formatter",true,"fmt",true,null);N=$pkg.Stringer=$newType(8,$kindInterface,"fmt.Stringer",true,"fmt",true,null);O=$pkg.GoStringer=$newType(8,$kindInterface,"fmt.GoStringer",true,"fmt",true,null);P=$pkg.buffer=$newType(12,$kindSlice,"fmt.buffer",true,"fmt",false,null);Q=$pkg.pp=$newType(0,$kindStruct,"fmt.pp",true,"fmt",false,function(buf_,arg_,value_,fmt_,reordered_,goodArgNum_,panicking_,erroring_){this.$val=this;if(arguments.length===0){this.buf=P.nil;this.arg=$ifaceNil;this.value=new G.Value.ptr(BJ.nil,0,0);this.fmt=new K.ptr(BK.nil,new J.ptr(false,false,false,false,false,false,false,false,false),0,0,BL.zero());this.reordered=false;this.goodArgNum=false;this.panicking=false;this.erroring=false;return;}this.buf=buf_;this.arg=arg_;this.value=value_;this.fmt=fmt_;this.reordered=reordered_;this.goodArgNum=goodArgNum_;this.panicking=panicking_;this.erroring=erroring_;});AU=$pkg.scanError=$newType(0,$kindStruct,"fmt.scanError",true,"fmt",false,function(err_){this.$val=this;if(arguments.length===0){this.err=$ifaceNil;return;}this.err=err_;});AV=$pkg.ss=$newType(0,$kindStruct,"fmt.ss",true,"fmt",false,function(rs_,buf_,count_,atEOF_,ssave_){this.$val=this;if(arguments.length===0){this.rs=$ifaceNil;this.buf=P.nil;this.count=0;this.atEOF=false;this.ssave=new AW.ptr(false,false,false,0,0,0);return;}this.rs=rs_;this.buf=buf_;this.count=count_;this.atEOF=atEOF_;this.ssave=ssave_;});AW=$pkg.ssave=$newType(0,$kindStruct,"fmt.ssave",true,"fmt",false,function(validSave_,nlIsEnd_,nlIsSpace_,argLimit_,limit_,maxWid_){this.$val=this;if(arguments.length===0){this.validSave=false;this.nlIsEnd=false;this.nlIsSpace=false;this.argLimit=0;this.limit=0;this.maxWid=0;return;}this.validSave=validSave_;this.nlIsEnd=nlIsEnd_;this.nlIsSpace=nlIsSpace_;this.argLimit=argLimit_;this.limit=limit_;this.maxWid=maxWid_;});BI=$sliceType($emptyInterface);BJ=$ptrType(G.rtype);BK=$ptrType(P);BL=$arrayType($Uint8,68);BM=$arrayType($Uint16,2);BN=$sliceType(BM);BO=$sliceType($Uint8);BP=$arrayType($Uint8,5);BQ=$ptrType(Q);BT=$ptrType(AV);CO=$ptrType(K);CP=$funcType([$Int32],[$Bool],false);K.ptr.prototype.clearflags=function(){var a;a=this;J.copy(a.fmtFlags,new J.ptr(false,false,false,false,false,false,false,false,false));};K.prototype.clearflags=function(){return this.$val.clearflags();};K.ptr.prototype.init=function(a){var a,b;b=this;b.buf=a;b.clearflags();};K.prototype.init=function(a){return this.$val.init(a);};K.ptr.prototype.writePadding=function(a){var a,b,c,d,e,f,g,h,i,j;b=this;if(a<=0){return;}c=b.buf.$get();d=c.$length;e=d+a>>0;if(e>c.$capacity){c=$makeSlice(P,(($imul(c.$capacity,2))+a>>0));$copySlice(c,b.buf.$get());}f=32;if(b.fmtFlags.zero){f=48;}g=$subslice(c,d,e);h=g;i=0;while(true){if(!(i=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+j]=f);i++;}b.buf.$set($subslice(c,0,e));};K.prototype.writePadding=function(a){return this.$val.writePadding(a);};K.ptr.prototype.pad=function(a){var a,b,c;b=this;if(!b.fmtFlags.widPresent||(b.wid===0)){b.buf.Write(a);return;}c=b.wid-B.RuneCount(a)>>0;if(!b.fmtFlags.minus){b.writePadding(c);b.buf.Write(a);}else{b.buf.Write(a);b.writePadding(c);}};K.prototype.pad=function(a){return this.$val.pad(a);};K.ptr.prototype.padString=function(a){var a,b,c;b=this;if(!b.fmtFlags.widPresent||(b.wid===0)){b.buf.WriteString(a);return;}c=b.wid-B.RuneCountInString(a)>>0;if(!b.fmtFlags.minus){b.writePadding(c);b.buf.WriteString(a);}else{b.buf.WriteString(a);b.writePadding(c);}};K.prototype.padString=function(a){return this.$val.padString(a);};K.ptr.prototype.fmtBoolean=function(a){var a,b;b=this;if(a){b.padString("true");}else{b.padString("false");}};K.prototype.fmtBoolean=function(a){return this.$val.fmtBoolean(a);};K.ptr.prototype.fmtUnicode=function(a){var a,b,c,d,e,f,g;b=this;c=$subslice(new BO(b.intbuf),0);d=4;if(b.fmtFlags.precPresent&&b.prec>4){d=b.prec;e=(((2+d>>0)+2>>0)+4>>0)+1>>0;if(e>c.$length){c=$makeSlice(BO,e);}}f=c.$length;if(b.fmtFlags.sharp&&(a.$high<0||(a.$high===0&&a.$low<=1114111))&&A.IsPrint(((a.$low>>0)))){f=f-(1)>>0;((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=39);f=f-(B.RuneLen(((a.$low>>0))))>>0;B.EncodeRune($subslice(c,f),((a.$low>>0)));f=f-(1)>>0;((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=39);f=f-(1)>>0;((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=32);}while(true){if(!((a.$high>0||(a.$high===0&&a.$low>=16)))){break;}f=f-(1)>>0;((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]="0123456789ABCDEFX".charCodeAt($flatten64(new $Uint64(a.$high&0,(a.$low&15)>>>0))));d=d-(1)>>0;a=$shiftRightUint64(a,(4));}f=f-(1)>>0;((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]="0123456789ABCDEFX".charCodeAt($flatten64(a)));d=d-(1)>>0;while(true){if(!(d>0)){break;}f=f-(1)>>0;((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=48);d=d-(1)>>0;}f=f-(1)>>0;((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=43);f=f-(1)>>0;((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=85);g=b.fmtFlags.zero;b.fmtFlags.zero=false;b.pad($subslice(c,f));b.fmtFlags.zero=g;};K.prototype.fmtUnicode=function(a){return this.$val.fmtUnicode(a);};K.ptr.prototype.fmtInteger=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t;e=this;g=c&&(f=(new $Int64(a.$high,a.$low)),(f.$high<0||(f.$high===0&&f.$low<0)));if(g){a=new $Uint64(-a.$high,-a.$low);}h=$subslice(new BO(e.intbuf),0);if(e.fmtFlags.widPresent||e.fmtFlags.precPresent){i=(3+e.wid>>0)+e.prec>>0;if(i>h.$length){h=$makeSlice(BO,i);}}j=0;if(e.fmtFlags.precPresent){j=e.prec;if((j===0)&&(a.$high===0&&a.$low===0)){k=e.fmtFlags.zero;e.fmtFlags.zero=false;e.writePadding(e.wid);e.fmtFlags.zero=k;return;}}else if(e.fmtFlags.zero&&e.fmtFlags.widPresent){j=e.wid;if(g||e.fmtFlags.plus||e.fmtFlags.space){j=j-(1)>>0;}}l=h.$length;m=b;if(m===(10)){while(true){if(!((a.$high>0||(a.$high===0&&a.$low>=10)))){break;}l=l-(1)>>0;n=$div64(a,new $Uint64(0,10),false);((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=(((o=new $Uint64(0+a.$high,48+a.$low),p=$mul64(n,new $Uint64(0,10)),new $Uint64(o.$high-p.$high,o.$low-p.$low)).$low<<24>>>24)));a=n;}}else if(m===(16)){while(true){if(!((a.$high>0||(a.$high===0&&a.$low>=16)))){break;}l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=d.charCodeAt($flatten64(new $Uint64(a.$high&0,(a.$low&15)>>>0))));a=$shiftRightUint64(a,(4));}}else if(m===(8)){while(true){if(!((a.$high>0||(a.$high===0&&a.$low>=8)))){break;}l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=(((q=new $Uint64(a.$high&0,(a.$low&7)>>>0),new $Uint64(0+q.$high,48+q.$low)).$low<<24>>>24)));a=$shiftRightUint64(a,(3));}}else if(m===(2)){while(true){if(!((a.$high>0||(a.$high===0&&a.$low>=2)))){break;}l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=(((r=new $Uint64(a.$high&0,(a.$low&1)>>>0),new $Uint64(0+r.$high,48+r.$low)).$low<<24>>>24)));a=$shiftRightUint64(a,(1));}}else{$panic(new $String("fmt: unknown base; can't happen"));}l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=d.charCodeAt($flatten64(a)));while(true){if(!(l>0&&j>(h.$length-l>>0))){break;}l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=48);}if(e.fmtFlags.sharp){s=b;if(s===(8)){if(!((((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l])===48))){l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=48);}}else if(s===(16)){l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=d.charCodeAt(16));l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=48);}}if(g){l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=45);}else if(e.fmtFlags.plus){l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=43);}else if(e.fmtFlags.space){l=l-(1)>>0;((l<0||l>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+l]=32);}t=e.fmtFlags.zero;e.fmtFlags.zero=false;e.pad($subslice(h,l));e.fmtFlags.zero=t;};K.prototype.fmtInteger=function(a,b,c,d){return this.$val.fmtInteger(a,b,c,d);};K.ptr.prototype.truncateString=function(a){var a,b,c,d,e,f,g;b=this;if(b.fmtFlags.precPresent){c=b.prec;d=a;e=0;while(true){if(!(e>0;if(c<0){return $substring(a,0,g);}e+=f[1];}}return a;};K.prototype.truncateString=function(a){return this.$val.truncateString(a);};K.ptr.prototype.truncate=function(a){var a,b,c,d,e,f;b=this;if(b.fmtFlags.precPresent){c=b.prec;d=0;while(true){if(!(d>0;if(c<0){return $subslice(a,0,d);}e=1;if(((d<0||d>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+d])>=128){f=B.DecodeRune($subslice(a,d));e=f[1];}d=d+(e)>>0;}}return a;};K.prototype.truncate=function(a){return this.$val.truncate(a);};K.ptr.prototype.fmtS=function(a){var a,b;b=this;a=b.truncateString(a);b.padString(a);};K.prototype.fmtS=function(a){return this.$val.fmtS(a);};K.ptr.prototype.fmtBs=function(a){var a,b;b=this;a=b.truncate(a);b.pad(a);};K.prototype.fmtBs=function(a){return this.$val.fmtBs(a);};K.ptr.prototype.fmtSbx=function(a,b,c){var a,b,c,d,e,f,g,h,i;d=this;e=b.$length;if(b===BO.nil){e=a.length;}if(d.fmtFlags.precPresent&&d.prec0){if(d.fmtFlags.space){if(d.fmtFlags.sharp){f=$imul(f,(2));}f=f+((e-1>>0))>>0;}else if(d.fmtFlags.sharp){f=f+(2)>>0;}}else{if(d.fmtFlags.widPresent){d.writePadding(d.wid);}return;}if(d.fmtFlags.widPresent&&d.wid>f&&!d.fmtFlags.minus){d.writePadding(d.wid-f>>0);}g=d.buf.$get();if(d.fmtFlags.sharp){g=$append(g,48,c.charCodeAt(16));}h=0;i=0;while(true){if(!(i0){g=$append(g,32);if(d.fmtFlags.sharp){g=$append(g,48,c.charCodeAt(16));}}if(!(b===BO.nil)){h=((i<0||i>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+i]);}else{h=a.charCodeAt(i);}g=$append(g,c.charCodeAt((h>>>4<<24>>>24)),c.charCodeAt(((h&15)>>>0)));i=i+(1)>>0;}d.buf.$set(g);if(d.fmtFlags.widPresent&&d.wid>f&&d.fmtFlags.minus){d.writePadding(d.wid-f>>0);}};K.prototype.fmtSbx=function(a,b,c){return this.$val.fmtSbx(a,b,c);};K.ptr.prototype.fmtSx=function(a,b){var a,b,c;c=this;c.fmtSbx(a,BO.nil,b);};K.prototype.fmtSx=function(a,b){return this.$val.fmtSx(a,b);};K.ptr.prototype.fmtBx=function(a,b){var a,b,c;c=this;c.fmtSbx("",a,b);};K.prototype.fmtBx=function(a,b){return this.$val.fmtBx(a,b);};K.ptr.prototype.fmtQ=function(a){var a,b,c;b=this;a=b.truncateString(a);if(b.fmtFlags.sharp&&A.CanBackquote(a)){b.padString("`"+a+"`");return;}c=$subslice(new BO(b.intbuf),0,0);if(b.fmtFlags.plus){b.pad(A.AppendQuoteToASCII(c,a));}else{b.pad(A.AppendQuote(c,a));}};K.prototype.fmtQ=function(a){return this.$val.fmtQ(a);};K.ptr.prototype.fmtC=function(a){var a,b,c,d,e;b=this;c=((a.$low>>0));if((a.$high>0||(a.$high===0&&a.$low>1114111))){c=65533;}d=$subslice(new BO(b.intbuf),0,0);e=B.EncodeRune($subslice(d,0,4),c);b.pad($subslice(d,0,e));};K.prototype.fmtC=function(a){return this.$val.fmtC(a);};K.ptr.prototype.fmtQc=function(a){var a,b,c,d;b=this;c=((a.$low>>0));if((a.$high>0||(a.$high===0&&a.$low>1114111))){c=65533;}d=$subslice(new BO(b.intbuf),0,0);if(b.fmtFlags.plus){b.pad(A.AppendQuoteRuneToASCII(d,c));}else{b.pad(A.AppendQuoteRune(d,c));}};K.prototype.fmtQc=function(a){return this.$val.fmtQc(a);};K.ptr.prototype.fmtFloat=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,k,l,m,n;e=this;if(e.fmtFlags.precPresent){d=e.prec;}f=A.AppendFloat($subslice(new BO(e.intbuf),0,1),a,((c<<24>>>24)),d,b);if(((1>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+1])===45)||((1>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+1])===43)){f=$subslice(f,1);}else{(0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0]=43);}if(e.fmtFlags.space&&((0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0])===43)&&!e.fmtFlags.plus){(0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0]=32);}if(((1>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+1])===73)||((1>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+1])===78)){g=e.fmtFlags.zero;e.fmtFlags.zero=false;if(((1>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+1])===78)&&!e.fmtFlags.space&&!e.fmtFlags.plus){f=$subslice(f,1);}e.pad(f);e.fmtFlags.zero=g;return;}if(e.fmtFlags.sharp&&!((c===98))){h=0;i=c;if((i===(118))||(i===(103))||(i===(71))){h=d;if(h===-1){h=6;}}j=BP.zero();k=$subslice(new BO(j),0,0);l=false;m=1;while(true){if(!(m=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+m]);if(n===(46)){l=true;}else if((n===(101))||(n===(69))){k=$appendSlice(k,$subslice(f,m));f=$subslice(f,0,m);}else{h=h-(1)>>0;}m=m+(1)>>0;}if(!l){f=$append(f,46);}while(true){if(!(h>0)){break;}f=$append(f,48);h=h-(1)>>0;}f=$appendSlice(f,k);}if(e.fmtFlags.plus||!(((0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0])===43))){if(e.fmtFlags.zero&&e.fmtFlags.widPresent&&e.wid>f.$length){e.buf.WriteByte((0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0]));e.writePadding(e.wid-f.$length>>0);e.buf.Write($subslice(f,1));return;}e.pad(f);return;}e.pad($subslice(f,1));};K.prototype.fmtFloat=function(a,b,c,d){return this.$val.fmtFloat(a,b,c,d);};$ptrType(P).prototype.Write=function(a){var a,b;b=this;b.$set($appendSlice(b.$get(),a));};$ptrType(P).prototype.WriteString=function(a){var a,b;b=this;b.$set($appendSlice(b.$get(),a));};$ptrType(P).prototype.WriteByte=function(a){var a,b;b=this;b.$set($append(b.$get(),a));};$ptrType(P).prototype.WriteRune=function(a){var a,b,c,d,e,f;b=this;if(a<128){b.$set($append(b.$get(),((a<<24>>>24))));return;}c=b.$get();d=c.$length;while(true){if(!((d+4>>0)>c.$capacity)){break;}c=$append(c,0);}f=B.EncodeRune((e=$subslice(c,d,(d+4>>0)),$subslice(new BO(e.$array),e.$offset,e.$offset+e.$length)),a);b.$set($subslice(c,0,(d+f>>0)));};S=function(){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=R.Get();$s=1;case 1:if($c){$c=false;a=a.$blk();}if(a&&a.$blk!==undefined){break s;}b=$assertType(a,BQ);b.panicking=false;b.erroring=false;b.fmt.init((b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))));$s=-1;return b;}return;}if($f===undefined){$f={$blk:S};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};Q.ptr.prototype.free=function(){var a;a=this;if(a.buf.$capacity>65536){return;}a.buf=$subslice(a.buf,0,0);a.arg=$ifaceNil;a.value=new G.Value.ptr(BJ.nil,0,0);R.Put(a);};Q.prototype.free=function(){return this.$val.free();};Q.ptr.prototype.Width=function(){var a,b,c,d,e;a=0;b=false;c=this;d=c.fmt.wid;e=c.fmt.fmtFlags.widPresent;a=d;b=e;return[a,b];};Q.prototype.Width=function(){return this.$val.Width();};Q.ptr.prototype.Precision=function(){var a,b,c,d,e;a=0;b=false;c=this;d=c.fmt.prec;e=c.fmt.fmtFlags.precPresent;a=d;b=e;return[a,b];};Q.prototype.Precision=function(){return this.$val.Precision();};Q.ptr.prototype.Flag=function(a){var a,b,c;b=this;c=a;if(c===(45)){return b.fmt.fmtFlags.minus;}else if(c===(43)){return b.fmt.fmtFlags.plus||b.fmt.fmtFlags.plusV;}else if(c===(35)){return b.fmt.fmtFlags.sharp||b.fmt.fmtFlags.sharpV;}else if(c===(32)){return b.fmt.fmtFlags.space;}else if(c===(48)){return b.fmt.fmtFlags.zero;}return false;};Q.prototype.Flag=function(a){return this.$val.Flag(a);};Q.ptr.prototype.Write=function(a){var a,b,c,d,e,f;b=0;c=$ifaceNil;d=this;(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).Write(a);e=a.$length;f=$ifaceNil;b=e;c=f;return[b,c];};Q.prototype.Write=function(a){return this.$val.Write(a);};Q.ptr.prototype.WriteString=function(a){var a,b,c,d,e,f;b=0;c=$ifaceNil;d=this;(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(a);e=a.length;f=$ifaceNil;b=e;c=f;return[b,c];};Q.prototype.WriteString=function(a){return this.$val.WriteString(a);};V=function(a,b){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=S();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;$r=d.doPrintf(a,b);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}e=($bytesToString(d.buf));d.free();$s=-1;return e;}return;}if($f===undefined){$f={$blk:V};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Sprintf=V;W=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=V(a,b);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=C.New(c);$s=2;case 2:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:W};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Errorf=W;Z=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=S();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;$r=c.doPrint(a);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d=($bytesToString(c.buf));c.free();$s=-1;return d;}return;}if($f===undefined){$f={$blk:Z};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Sprint=Z;AA=function(a,b){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=0;d=$ifaceNil;e=S();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;$r=f.doPrintln(b);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}i=a.Write((h=f.buf,$subslice(new BO(h.$array),h.$offset,h.$offset+h.$length)));$s=3;case 3:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}g=i;c=g[0];d=g[1];f.free();$s=-1;return[c,d];}return;}if($f===undefined){$f={$blk:AA};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Fprintln=AA;AB=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;e=AA(F.Stdout,a);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;b=d[0];c=d[1];$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AB};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Println=AB;AC=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=S();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;$r=c.doPrintln(a);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d=($bytesToString(c.buf));c.free();$s=-1;return d;}return;}if($f===undefined){$f={$blk:AC};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Sprintln=AC;AD=function(a,b){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=$clone(a,G.Value).Field(b);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;if(($clone(d,G.Value).Kind()===20)&&!$clone(d,G.Value).IsNil()){$s=2;continue;}$s=3;continue;case 2:e=$clone(d,G.Value).Elem();$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;case 3:$s=-1;return d;}return;}if($f===undefined){$f={$blk:AD};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AE=function(a){var a;return a>1000000||a<-1000000;};AF=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,k,l;d=0;e=false;f=0;if(b>=c){g=0;h=false;i=c;d=g;e=h;f=i;return[d,e,f];}f=b;while(true){if(!(f>>24)>>0))>>0;e=true;f=f+(1)>>0;}return[d,e,f];};Q.ptr.prototype.unknownType=function(a){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;if(!$clone(a,G.Value).IsValid()){(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString("");$s=-1;return;}(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(63);c=$clone(a,G.Value).Type().String();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$r=(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString(c);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(63);$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.unknownType};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.unknownType=function(a){return this.$val.unknownType(a);};Q.ptr.prototype.badVerb=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;b.erroring=true;(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString("%!");(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteRune(a);(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(40);if(!($interfaceIsEqual(b.arg,$ifaceNil))){$s=2;continue;}if($clone(b.value,G.Value).IsValid()){$s=3;continue;}$s=4;continue;case 2:c=G.TypeOf(b.arg).String();$s=6;case 6:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$r=(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString(c);$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(61);$r=b.printArg(b.arg,118);$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=5;continue;case 3:d=$clone(b.value,G.Value).Type().String();$s=9;case 9:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$r=(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString(d);$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(61);$r=b.printValue($clone(b.value,G.Value),118,0);$s=11;case 11:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=5;continue;case 4:(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString("");case 5:case 1:(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(41);b.erroring=false;$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.badVerb};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.badVerb=function(a){return this.$val.badVerb(a);};Q.ptr.prototype.fmtBool=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=b;if((d===(116))||(d===(118))){$s=2;continue;}$s=3;continue;case 2:c.fmt.fmtBoolean(a);$s=4;continue;case 3:$r=c.badVerb(b);$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 4:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.fmtBool};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.fmtBool=function(a,b){return this.$val.fmtBool(a,b);};Q.ptr.prototype.fmt0x64=function(a,b){var a,b,c,d;c=this;d=c.fmt.fmtFlags.sharp;c.fmt.fmtFlags.sharp=b;c.fmt.fmtInteger(a,16,false,"0123456789abcdefx");c.fmt.fmtFlags.sharp=d;};Q.prototype.fmt0x64=function(a,b){return this.$val.fmt0x64(a,b);};Q.ptr.prototype.fmtInteger=function(a,b,c){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=c;if(e===(118)){$s=2;continue;}if(e===(100)){$s=3;continue;}if(e===(98)){$s=4;continue;}if(e===(111)){$s=5;continue;}if(e===(120)){$s=6;continue;}if(e===(88)){$s=7;continue;}if(e===(99)){$s=8;continue;}if(e===(113)){$s=9;continue;}if(e===(85)){$s=10;continue;}$s=11;continue;case 2:if(d.fmt.fmtFlags.sharpV&&!b){d.fmt0x64(a,true);}else{d.fmt.fmtInteger(a,10,b,"0123456789abcdefx");}$s=12;continue;case 3:d.fmt.fmtInteger(a,10,b,"0123456789abcdefx");$s=12;continue;case 4:d.fmt.fmtInteger(a,2,b,"0123456789abcdefx");$s=12;continue;case 5:d.fmt.fmtInteger(a,8,b,"0123456789abcdefx");$s=12;continue;case 6:d.fmt.fmtInteger(a,16,b,"0123456789abcdefx");$s=12;continue;case 7:d.fmt.fmtInteger(a,16,b,"0123456789ABCDEFX");$s=12;continue;case 8:d.fmt.fmtC(a);$s=12;continue;case 9:if((a.$high<0||(a.$high===0&&a.$low<=1114111))){$s=13;continue;}$s=14;continue;case 13:d.fmt.fmtQc(a);$s=15;continue;case 14:$r=d.badVerb(c);$s=16;case 16:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 15:$s=12;continue;case 10:d.fmt.fmtUnicode(a);$s=12;continue;case 11:$r=d.badVerb(c);$s=17;case 17:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 12:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.fmtInteger};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.fmtInteger=function(a,b,c){return this.$val.fmtInteger(a,b,c);};Q.ptr.prototype.fmtFloat=function(a,b,c){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=c;if(e===(118)){$s=2;continue;}if((e===(98))||(e===(103))||(e===(71))){$s=3;continue;}if((e===(102))||(e===(101))||(e===(69))){$s=4;continue;}if(e===(70)){$s=5;continue;}$s=6;continue;case 2:d.fmt.fmtFloat(a,b,103,-1);$s=7;continue;case 3:d.fmt.fmtFloat(a,b,c,-1);$s=7;continue;case 4:d.fmt.fmtFloat(a,b,c,6);$s=7;continue;case 5:d.fmt.fmtFloat(a,b,102,6);$s=7;continue;case 6:$r=d.badVerb(c);$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 7:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.fmtFloat};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.fmtFloat=function(a,b,c){return this.$val.fmtFloat(a,b,c);};Q.ptr.prototype.fmtComplex=function(a,b,c){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=c;if((e===(118))||(e===(98))||(e===(103))||(e===(71))||(e===(102))||(e===(70))||(e===(101))||(e===(69))){$s=2;continue;}$s=3;continue;case 2:f=d.fmt.fmtFlags.plus;(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(40);$r=d.fmtFloat(a.$real,(g=b/2,(g===g&&g!==1/0&&g!==-1/0)?g>>0:$throwRuntimeError("integer divide by zero")),c);$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d.fmt.fmtFlags.plus=true;$r=d.fmtFloat(a.$imag,(h=b/2,(h===h&&h!==1/0&&h!==-1/0)?h>>0:$throwRuntimeError("integer divide by zero")),c);$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("i)");d.fmt.fmtFlags.plus=f;$s=4;continue;case 3:$r=d.badVerb(c);$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 4:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.fmtComplex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.fmtComplex=function(a,b,c){return this.$val.fmtComplex(a,b,c);};Q.ptr.prototype.fmtString=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=b;if(d===(118)){$s=2;continue;}if(d===(115)){$s=3;continue;}if(d===(120)){$s=4;continue;}if(d===(88)){$s=5;continue;}if(d===(113)){$s=6;continue;}$s=7;continue;case 2:if(c.fmt.fmtFlags.sharpV){c.fmt.fmtQ(a);}else{c.fmt.fmtS(a);}$s=8;continue;case 3:c.fmt.fmtS(a);$s=8;continue;case 4:c.fmt.fmtSx(a,"0123456789abcdefx");$s=8;continue;case 5:c.fmt.fmtSx(a,"0123456789ABCDEFX");$s=8;continue;case 6:c.fmt.fmtQ(a);$s=8;continue;case 7:$r=c.badVerb(b);$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 8:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.fmtString};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.fmtString=function(a,b){return this.$val.fmtString(a,b);};Q.ptr.prototype.fmtBytes=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=b;if((e===(118))||(e===(100))){$s=2;continue;}if(e===(115)){$s=3;continue;}if(e===(120)){$s=4;continue;}if(e===(88)){$s=5;continue;}if(e===(113)){$s=6;continue;}$s=7;continue;case 2:if(d.fmt.fmtFlags.sharpV){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(c);if(a===BO.nil){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("(nil)");$s=-1;return;}(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(123);f=a;g=0;while(true){if(!(g=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+g]);if(h>0){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(", ");}d.fmt0x64((new $Uint64(0,i)),true);g++;}(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(125);}else{(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(91);j=a;k=0;while(true){if(!(k=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+k]);if(l>0){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(32);}d.fmt.fmtInteger((new $Uint64(0,m)),10,false,"0123456789abcdefx");k++;}(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(93);}$s=8;continue;case 3:d.fmt.fmtBs(a);$s=8;continue;case 4:d.fmt.fmtBx(a,"0123456789abcdefx");$s=8;continue;case 5:d.fmt.fmtBx(a,"0123456789ABCDEFX");$s=8;continue;case 6:d.fmt.fmtQ(($bytesToString(a)));$s=8;continue;case 7:n=G.ValueOf(a);$s=9;case 9:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}$r=d.printValue($clone(n,G.Value),b,0);$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 8:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.fmtBytes};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.fmtBytes=function(a,b,c){return this.$val.fmtBytes(a,b,c);};Q.ptr.prototype.fmtPointer=function(a,b){var a,b,c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=0;e=$clone(a,G.Value).Kind();if((e===(18))||(e===(19))||(e===(21))||(e===(22))||(e===(23))||(e===(26))){$s=2;continue;}$s=3;continue;case 2:d=$clone(a,G.Value).Pointer();$s=4;continue;case 3:$r=c.badVerb(b);$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 4:case 1:f=b;if(f===(118)){$s=7;continue;}if(f===(112)){$s=8;continue;}if((f===(98))||(f===(111))||(f===(100))||(f===(120))||(f===(88))){$s=9;continue;}$s=10;continue;case 7:if(c.fmt.fmtFlags.sharpV){$s=12;continue;}$s=13;continue;case 12:(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteByte(40);g=$clone(a,G.Value).Type().String();$s=15;case 15:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}$r=(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString(g);$s=16;case 16:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString(")(");if(d===0){(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString("nil");}else{c.fmt0x64((new $Uint64(0,d.constructor===Number?d:1)),true);}(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteByte(41);$s=14;continue;case 13:if(d===0){c.fmt.padString("");}else{c.fmt0x64((new $Uint64(0,d.constructor===Number?d:1)),!c.fmt.fmtFlags.sharp);}case 14:$s=11;continue;case 8:c.fmt0x64((new $Uint64(0,d.constructor===Number?d:1)),!c.fmt.fmtFlags.sharp);$s=11;continue;case 9:$r=c.fmtInteger((new $Uint64(0,d.constructor===Number?d:1)),false,b);$s=17;case 17:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=11;continue;case 10:$r=c.badVerb(b);$s=18;case 18:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 11:case 6:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.fmtPointer};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.fmtPointer=function(a,b){return this.$val.fmtPointer(a,b);};Q.ptr.prototype.catchPanic=function(a,b,c){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=$recover();if(!($interfaceIsEqual(e,$ifaceNil))){$s=1;continue;}$s=2;continue;case 1:f=G.ValueOf(a);$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;if(($clone(g,G.Value).Kind()===22)&&$clone(g,G.Value).IsNil()){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("");$s=-1;return;}if(d.panicking){$panic(e);}h=$clone(d.fmt.fmtFlags,J);d.fmt.clearflags();(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("%!");(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteRune(b);(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("(PANIC=");(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(c);(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(" method: ");d.panicking=true;$r=d.printArg(e,118);$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d.panicking=false;(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(41);J.copy(d.fmt.fmtFlags,h);case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.catchPanic};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.catchPanic=function(a,b,c){return this.$val.catchPanic(a,b,c);};Q.ptr.prototype.handleMethods=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=false;c=this;if(c.erroring){$s=-1;return b;}d=$assertType(c.arg,M,true);e=d[0];f=d[1];if(f){$s=1;continue;}$s=2;continue;case 1:b=true;$deferred.push([$methodVal(c,"catchPanic"),[c.arg,a,"Format"]]);$r=e.Format(c,a);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return b;case 2:if(c.fmt.fmtFlags.sharpV){$s=4;continue;}$s=5;continue;case 4:g=$assertType(c.arg,O,true);h=g[0];i=g[1];if(i){$s=7;continue;}$s=8;continue;case 7:b=true;$deferred.push([$methodVal(c,"catchPanic"),[c.arg,a,"GoString"]]);j=h.GoString();$s=9;case 9:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}$r=c.fmt.fmtS(j);$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return b;case 8:$s=6;continue;case 5:k=a;if((k===(118))||(k===(115))||(k===(120))||(k===(88))||(k===(113))){$s=12;continue;}$s=13;continue;case 12:l=c.arg;if($assertType(l,$error,true)[1]){$s=14;continue;}if($assertType(l,N,true)[1]){$s=15;continue;}$s=16;continue;case 14:m=l;b=true;$deferred.push([$methodVal(c,"catchPanic"),[c.arg,a,"Error"]]);o=m.Error();$s=17;case 17:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}$r=c.fmtString(o,a);$s=18;case 18:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return b;case 15:n=l;b=true;$deferred.push([$methodVal(c,"catchPanic"),[c.arg,a,"String"]]);p=n.String();$s=19;case 19:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}$r=c.fmtString(p,a);$s=20;case 20:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return b;case 16:case 13:case 11:case 6:b=false;$s=-1;return b;}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if(!$curGoroutine.asleep){return b;}if($curGoroutine.asleep){if($f===undefined){$f={$blk:Q.ptr.prototype.handleMethods};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};Q.prototype.handleMethods=function(a){return this.$val.handleMethods(a);};Q.ptr.prototype.printArg=function(a,b){var a,aa,ab,ac,ad,ae,af,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;c.arg=a;c.value=new G.Value.ptr(BJ.nil,0,0);if($interfaceIsEqual(a,$ifaceNil)){$s=1;continue;}$s=2;continue;case 1:d=b;if((d===(84))||(d===(118))){$s=4;continue;}$s=5;continue;case 4:c.fmt.padString("");$s=6;continue;case 5:$r=c.badVerb(b);$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 6:case 3:$s=-1;return;case 2:e=b;if(e===(84)){$s=9;continue;}if(e===(112)){$s=10;continue;}$s=11;continue;case 9:f=G.TypeOf(a).String();$s=12;case 12:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$r=c.fmt.fmtS(f);$s=13;case 13:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 10:g=G.ValueOf(a);$s=14;case 14:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}$r=c.fmtPointer($clone(g,G.Value),112);$s=15;case 15:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 11:case 8:h=a;if($assertType(h,$Bool,true)[1]){$s=16;continue;}if($assertType(h,$Float32,true)[1]){$s=17;continue;}if($assertType(h,$Float64,true)[1]){$s=18;continue;}if($assertType(h,$Complex64,true)[1]){$s=19;continue;}if($assertType(h,$Complex128,true)[1]){$s=20;continue;}if($assertType(h,$Int,true)[1]){$s=21;continue;}if($assertType(h,$Int8,true)[1]){$s=22;continue;}if($assertType(h,$Int16,true)[1]){$s=23;continue;}if($assertType(h,$Int32,true)[1]){$s=24;continue;}if($assertType(h,$Int64,true)[1]){$s=25;continue;}if($assertType(h,$Uint,true)[1]){$s=26;continue;}if($assertType(h,$Uint8,true)[1]){$s=27;continue;}if($assertType(h,$Uint16,true)[1]){$s=28;continue;}if($assertType(h,$Uint32,true)[1]){$s=29;continue;}if($assertType(h,$Uint64,true)[1]){$s=30;continue;}if($assertType(h,$Uintptr,true)[1]){$s=31;continue;}if($assertType(h,$String,true)[1]){$s=32;continue;}if($assertType(h,BO,true)[1]){$s=33;continue;}if($assertType(h,G.Value,true)[1]){$s=34;continue;}$s=35;continue;case 16:i=h.$val;$r=c.fmtBool(i,b);$s=37;case 37:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 17:j=h.$val;$r=c.fmtFloat((j),32,b);$s=38;case 38:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 18:k=h.$val;$r=c.fmtFloat(k,64,b);$s=39;case 39:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 19:l=h.$val;$r=c.fmtComplex((new $Complex128(l.$real,l.$imag)),64,b);$s=40;case 40:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 20:m=h.$val;$r=c.fmtComplex(m,128,b);$s=41;case 41:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 21:n=h.$val;$r=c.fmtInteger((new $Uint64(0,n)),true,b);$s=42;case 42:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 22:o=h.$val;$r=c.fmtInteger((new $Uint64(0,o)),true,b);$s=43;case 43:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 23:p=h.$val;$r=c.fmtInteger((new $Uint64(0,p)),true,b);$s=44;case 44:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 24:q=h.$val;$r=c.fmtInteger((new $Uint64(0,q)),true,b);$s=45;case 45:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 25:r=h.$val;$r=c.fmtInteger((new $Uint64(r.$high,r.$low)),true,b);$s=46;case 46:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 26:s=h.$val;$r=c.fmtInteger((new $Uint64(0,s)),false,b);$s=47;case 47:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 27:t=h.$val;$r=c.fmtInteger((new $Uint64(0,t)),false,b);$s=48;case 48:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 28:u=h.$val;$r=c.fmtInteger((new $Uint64(0,u)),false,b);$s=49;case 49:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 29:v=h.$val;$r=c.fmtInteger((new $Uint64(0,v)),false,b);$s=50;case 50:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 30:w=h.$val;$r=c.fmtInteger(w,false,b);$s=51;case 51:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 31:x=h.$val;$r=c.fmtInteger((new $Uint64(0,x.constructor===Number?x:1)),false,b);$s=52;case 52:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 32:y=h.$val;$r=c.fmtString(y,b);$s=53;case 53:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 33:z=h.$val;$r=c.fmtBytes(z,b,"[]byte");$s=54;case 54:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 34:aa=h.$val;if($clone(aa,G.Value).IsValid()&&$clone(aa,G.Value).CanInterface()){$s=55;continue;}$s=56;continue;case 55:ac=$clone(aa,G.Value).Interface();$s=57;case 57:if($c){$c=false;ac=ac.$blk();}if(ac&&ac.$blk!==undefined){break s;}c.arg=ac;ad=c.handleMethods(b);$s=60;case 60:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}if(ad){$s=58;continue;}$s=59;continue;case 58:$s=-1;return;case 59:case 56:$r=c.printValue($clone(aa,G.Value),b,0);$s=61;case 61:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=36;continue;case 35:ab=h;ae=c.handleMethods(b);$s=64;case 64:if($c){$c=false;ae=ae.$blk();}if(ae&&ae.$blk!==undefined){break s;}if(!ae){$s=62;continue;}$s=63;continue;case 62:af=G.ValueOf(ab);$s=65;case 65:if($c){$c=false;af=af.$blk();}if(af&&af.$blk!==undefined){break s;}$r=c.printValue($clone(af,G.Value),b,0);$s=66;case 66:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 63:case 36:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.printArg};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.printArg=function(a,b){return this.$val.printArg(a,b);};Q.ptr.prototype.printValue=function(a,b,c){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;if(c>0&&$clone(a,G.Value).IsValid()&&$clone(a,G.Value).CanInterface()){$s=1;continue;}$s=2;continue;case 1:e=$clone(a,G.Value).Interface();$s=3;case 3:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d.arg=e;f=d.handleMethods(b);$s=6;case 6:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}if(f){$s=4;continue;}$s=5;continue;case 4:$s=-1;return;case 5:case 2:d.arg=$ifaceNil;d.value=a;g=a;h=$clone(a,G.Value).Kind();if(h===(0)){$s=8;continue;}if(h===(1)){$s=9;continue;}if((h===(2))||(h===(3))||(h===(4))||(h===(5))||(h===(6))){$s=10;continue;}if((h===(7))||(h===(8))||(h===(9))||(h===(10))||(h===(11))||(h===(12))){$s=11;continue;}if(h===(13)){$s=12;continue;}if(h===(14)){$s=13;continue;}if(h===(15)){$s=14;continue;}if(h===(16)){$s=15;continue;}if(h===(24)){$s=16;continue;}if(h===(21)){$s=17;continue;}if(h===(25)){$s=18;continue;}if(h===(20)){$s=19;continue;}if((h===(17))||(h===(23))){$s=20;continue;}if(h===(22)){$s=21;continue;}if((h===(18))||(h===(19))||(h===(26))){$s=22;continue;}$s=23;continue;case 8:if(c===0){$s=25;continue;}$s=26;continue;case 25:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("");$s=27;continue;case 26:i=b;if(i===(118)){$s=29;continue;}$s=30;continue;case 29:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("");$s=31;continue;case 30:$r=d.badVerb(b);$s=32;case 32:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 31:case 28:case 27:$s=24;continue;case 9:$r=d.fmtBool($clone(g,G.Value).Bool(),b);$s=33;case 33:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 10:$r=d.fmtInteger(((j=$clone(g,G.Value).Int(),new $Uint64(j.$high,j.$low))),true,b);$s=34;case 34:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 11:$r=d.fmtInteger($clone(g,G.Value).Uint(),false,b);$s=35;case 35:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 12:$r=d.fmtFloat($clone(g,G.Value).Float(),32,b);$s=36;case 36:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 13:$r=d.fmtFloat($clone(g,G.Value).Float(),64,b);$s=37;case 37:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 14:$r=d.fmtComplex($clone(g,G.Value).Complex(),64,b);$s=38;case 38:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 15:$r=d.fmtComplex($clone(g,G.Value).Complex(),128,b);$s=39;case 39:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 16:k=$clone(g,G.Value).String();$s=40;case 40:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}$r=d.fmtString(k,b);$s=41;case 41:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 17:if(d.fmt.fmtFlags.sharpV){$s=42;continue;}$s=43;continue;case 42:l=$clone(g,G.Value).Type().String();$s=45;case 45:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}$r=(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(l);$s=46;case 46:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if($clone(g,G.Value).IsNil()){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("(nil)");$s=-1;return;}(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(123);$s=44;continue;case 43:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("map[");case 44:m=D.Sort($clone(g,G.Value));$s=47;case 47:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}n=m;o=n.Key;p=0;case 48:if(!(p=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]);if(q>0){if(d.fmt.fmtFlags.sharpV){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(", ");}else{(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(32);}}$r=d.printValue($clone(r,G.Value),b,c+1>>0);$s=50;case 50:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(58);$r=d.printValue($clone((s=n.Value,((q<0||q>=s.$length)?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+q])),G.Value),b,c+1>>0);$s=51;case 51:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}p++;$s=48;continue;case 49:if(d.fmt.fmtFlags.sharpV){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(125);}else{(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(93);}$s=24;continue;case 18:if(d.fmt.fmtFlags.sharpV){$s=52;continue;}$s=53;continue;case 52:t=$clone(g,G.Value).Type().String();$s=54;case 54:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}$r=(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(t);$s=55;case 55:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 53:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(123);u=0;case 56:if(!(u<$clone(g,G.Value).NumField())){$s=57;continue;}if(u>0){if(d.fmt.fmtFlags.sharpV){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(", ");}else{(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(32);}}if(d.fmt.fmtFlags.plusV||d.fmt.fmtFlags.sharpV){$s=58;continue;}$s=59;continue;case 58:v=$clone(g,G.Value).Type().Field(u);$s=60;case 60:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}w=v.Name;if(!(w==="")){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(w);(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(58);}case 59:x=AD($clone(g,G.Value),u);$s=61;case 61:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}$r=d.printValue($clone(x,G.Value),b,c+1>>0);$s=62;case 62:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}u=u+(1)>>0;$s=56;continue;case 57:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(125);$s=24;continue;case 19:y=$clone(g,G.Value).Elem();$s=63;case 63:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}z=y;if(!$clone(z,G.Value).IsValid()){$s=64;continue;}$s=65;continue;case 64:if(d.fmt.fmtFlags.sharpV){$s=67;continue;}$s=68;continue;case 67:aa=$clone(g,G.Value).Type().String();$s=70;case 70:if($c){$c=false;aa=aa.$blk();}if(aa&&aa.$blk!==undefined){break s;}$r=(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(aa);$s=71;case 71:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("(nil)");$s=69;continue;case 68:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("");case 69:$s=66;continue;case 65:$r=d.printValue($clone(z,G.Value),b,c+1>>0);$s=72;case 72:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 66:$s=24;continue;case 20:ab=b;if((ab===(115))||(ab===(113))||(ab===(120))||(ab===(88))){$s=74;continue;}$s=75;continue;case 74:ac=$clone(g,G.Value).Type();ad=ac.Elem();$s=78;case 78:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}ae=ad.Kind();$s=79;case 79:if($c){$c=false;ae=ae.$blk();}if(ae&&ae.$blk!==undefined){break s;}if(ae===8){$s=76;continue;}$s=77;continue;case 76:af=BO.nil;if($clone(g,G.Value).Kind()===23){$s=80;continue;}if($clone(g,G.Value).CanAddr()){$s=81;continue;}$s=82;continue;case 80:ag=$clone(g,G.Value).Bytes();$s=84;case 84:if($c){$c=false;ag=ag.$blk();}if(ag&&ag.$blk!==undefined){break s;}af=ag;$s=83;continue;case 81:ah=$clone(g,G.Value).Slice(0,$clone(g,G.Value).Len());$s=85;case 85:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}ai=$clone(ah,G.Value).Bytes();$s=86;case 86:if($c){$c=false;ai=ai.$blk();}if(ai&&ai.$blk!==undefined){break s;}af=ai;$s=83;continue;case 82:af=$makeSlice(BO,$clone(g,G.Value).Len());aj=af;ak=0;case 87:if(!(ak=af.$length)?($throwRuntimeError("index out of range"),undefined):af.$array[af.$offset+al]=((an.$low<<24>>>24)));ak++;$s=87;continue;case 88:case 83:ao=af;ap=b;aq=ac.String();$s=91;case 91:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;$r=d.fmtBytes(ao,ap,ar);$s=92;case 92:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 77:case 75:case 73:if(d.fmt.fmtFlags.sharpV){$s=93;continue;}$s=94;continue;case 93:as=$clone(g,G.Value).Type().String();$s=96;case 96:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}$r=(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(as);$s=97;case 97:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(($clone(g,G.Value).Kind()===23)&&$clone(g,G.Value).IsNil()){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString("(nil)");$s=-1;return;}(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(123);at=0;case 98:if(!(at<$clone(g,G.Value).Len())){$s=99;continue;}if(at>0){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteString(", ");}au=$clone(g,G.Value).Index(at);$s=100;case 100:if($c){$c=false;au=au.$blk();}if(au&&au.$blk!==undefined){break s;}$r=d.printValue($clone(au,G.Value),b,c+1>>0);$s=101;case 101:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}at=at+(1)>>0;$s=98;continue;case 99:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(125);$s=95;continue;case 94:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(91);av=0;case 102:if(!(av<$clone(g,G.Value).Len())){$s=103;continue;}if(av>0){(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(32);}aw=$clone(g,G.Value).Index(av);$s=104;case 104:if($c){$c=false;aw=aw.$blk();}if(aw&&aw.$blk!==undefined){break s;}$r=d.printValue($clone(aw,G.Value),b,c+1>>0);$s=105;case 105:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}av=av+(1)>>0;$s=102;continue;case 103:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(93);case 95:$s=24;continue;case 21:if((c===0)&&!(($clone(g,G.Value).Pointer()===0))){$s=106;continue;}$s=107;continue;case 106:ax=$clone(g,G.Value).Elem();$s=109;case 109:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}ay=ax;az=$clone(ay,G.Value).Kind();if((az===(17))||(az===(23))||(az===(25))||(az===(21))){$s=110;continue;}$s=111;continue;case 110:(d.$ptr_buf||(d.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},d))).WriteByte(38);$r=d.printValue($clone(ay,G.Value),b,c+1>>0);$s=112;case 112:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 111:case 108:case 107:$r=d.fmtPointer($clone(g,G.Value),b);$s=113;case 113:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 22:$r=d.fmtPointer($clone(g,G.Value),b);$s=114;case 114:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=24;continue;case 23:$r=d.unknownType($clone(g,G.Value));$s=115;case 115:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 24:case 7:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.printValue};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.printValue=function(a,b,c){return this.$val.printValue(a,b,c);};AG=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=0;d=false;e=0;e=b;if(b=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+b]),$Int,true);c=f[0];d=f[1];if(!d){$s=3;continue;}$s=4;continue;case 3:g=G.ValueOf(((b<0||b>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+b]));$s=6;case 6:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;i=$clone(h,G.Value).Kind();if((i===(2))||(i===(3))||(i===(4))||(i===(5))||(i===(6))){j=$clone(h,G.Value).Int();if((k=(new $Int64(0,(((j.$low+((j.$high>>31)*4294967296))>>0)))),(k.$high===j.$high&&k.$low===j.$low))){c=(((j.$low+((j.$high>>31)*4294967296))>>0));d=true;}}else if((i===(7))||(i===(8))||(i===(9))||(i===(10))||(i===(11))||(i===(12))){l=$clone(h,G.Value).Uint();if((m=(new $Int64(l.$high,l.$low)),(m.$high>0||(m.$high===0&&m.$low>=0)))&&(n=(new $Uint64(0,((l.$low>>0)))),(n.$high===l.$high&&n.$low===l.$low))){c=((l.$low>>0));d=true;}}case 5:case 4:e=b+1>>0;if(AE(c)){c=0;d=false;}case 2:$s=-1;return[c,d,e];}return;}if($f===undefined){$f={$blk:AG};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};AH=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;b=0;c=0;d=false;if(a.length<3){e=0;f=1;g=false;b=e;c=f;d=g;return[b,c,d];}h=1;while(true){if(!(h>0;o=false;b=m;c=n;d=o;return[b,c,d];}p=j-1>>0;q=h+1>>0;r=true;b=p;c=q;d=r;return[b,c,d];}h=h+(1)>>0;}s=0;t=1;u=false;b=s;c=t;d=u;return[b,c,d];};Q.ptr.prototype.argNumber=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;e=0;f=0;g=false;h=this;if(b.length<=c||!((b.charCodeAt(c)===91))){i=a;j=c;k=false;e=i;f=j;g=k;return[e,f,g];}h.reordered=true;l=AH($substring(b,c));m=l[0];n=l[1];o=l[2];if(o&&0<=m&&m>0;r=true;e=p;f=q;g=r;return[e,f,g];}h.goodArgNum=false;s=a;t=c+n>>0;u=o;e=s;f=t;g=u;return[e,f,g];};Q.prototype.argNumber=function(a,b,c,d){return this.$val.argNumber(a,b,c,d);};Q.ptr.prototype.badArgNum=function(a){var a,b;b=this;(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString("%!");(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteRune(a);(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString("(BADINDEX)");};Q.prototype.badArgNum=function(a){return this.$val.badArgNum(a);};Q.ptr.prototype.missingArg=function(a){var a,b;b=this;(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString("%!");(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteRune(a);(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteString("(MISSING)");};Q.prototype.missingArg=function(a){return this.$val.missingArg(a);};Q.ptr.prototype.doPrintf=function(a,b){var a,aa,ab,ac,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=a.length;e=0;f=false;c.reordered=false;g=0;case 1:if(!(g>0;}if(g>h){(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString($substring(a,h,g));}if(g>=d){$s=2;continue;}g=g+(1)>>0;c.fmt.clearflags();case 3:if(!(g=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+e]),((i>>0)));$s=15;case 15:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}e=e+(1)>>0;g=g+(1)>>0;$s=1;continue s;case 14:$s=4;continue s;case 12:case 5:g=g+(1)>>0;$s=3;continue;case 4:k=c.argNumber(e,a,g,b.$length);e=k[0];g=k[1];f=k[2];if(g>0;m=AG(b,e);$s=19;case 19:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;c.fmt.wid=l[0];c.fmt.fmtFlags.widPresent=l[1];e=l[2];if(!c.fmt.fmtFlags.widPresent){(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString("%!(BADWIDTH)");}if(c.fmt.wid<0){c.fmt.wid=-c.fmt.wid;c.fmt.fmtFlags.minus=true;c.fmt.fmtFlags.zero=false;}f=false;$s=18;continue;case 17:n=AF(a,g,d);c.fmt.wid=n[0];c.fmt.fmtFlags.widPresent=n[1];g=n[2];if(f&&c.fmt.fmtFlags.widPresent){c.goodArgNum=false;}case 18:if((g+1>>0)>0;if(f){c.goodArgNum=false;}o=c.argNumber(e,a,g,b.$length);e=o[0];g=o[1];f=o[2];if(g>0;q=AG(b,e);$s=25;case 25:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}p=q;c.fmt.prec=p[0];c.fmt.fmtFlags.precPresent=p[1];e=p[2];if(c.fmt.prec<0){c.fmt.prec=0;c.fmt.fmtFlags.precPresent=false;}if(!c.fmt.fmtFlags.precPresent){(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString("%!(BADPREC)");}f=false;$s=24;continue;case 23:r=AF(a,g,d);c.fmt.prec=r[0];c.fmt.fmtFlags.precPresent=r[1];g=r[2];if(!c.fmt.fmtFlags.precPresent){c.fmt.prec=0;c.fmt.fmtFlags.precPresent=true;}case 24:case 21:if(!f){s=c.argNumber(e,a,g,b.$length);e=s[0];g=s[1];f=s[2];}if(g>=d){(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString("%!(NOVERB)");$s=2;continue;}t=((a.charCodeAt(g)>>0));u=1;v=t;w=u;if(v>=128){x=B.DecodeRuneInString($substring(a,g));v=x[0];w=x[1];}g=g+(w)>>0;if((v===37)){$s=27;continue;}if(!c.goodArgNum){$s=28;continue;}if(e>=b.$length){$s=29;continue;}if((v===118)){$s=30;continue;}$s=31;continue;case 27:(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteByte(37);$s=32;continue;case 28:c.badArgNum(v);$s=32;continue;case 29:c.missingArg(v);$s=32;continue;case 30:c.fmt.fmtFlags.sharpV=c.fmt.fmtFlags.sharp;c.fmt.fmtFlags.sharp=false;c.fmt.fmtFlags.plusV=c.fmt.fmtFlags.plus;c.fmt.fmtFlags.plus=false;$r=c.printArg(((e<0||e>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+e]),v);$s=33;case 33:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}e=e+(1)>>0;$s=32;continue;case 31:$r=c.printArg(((e<0||e>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+e]),v);$s=34;case 34:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}e=e+(1)>>0;case 32:case 26:$s=1;continue;case 2:if(!c.reordered&&e=y.$length)?($throwRuntimeError("index out of range"),undefined):y.$array[y.$offset+z]);if(aa>0){(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString(", ");}if($interfaceIsEqual(ab,$ifaceNil)){$s=39;continue;}$s=40;continue;case 39:(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString("");$s=41;continue;case 40:ac=G.TypeOf(ab).String();$s=42;case 42:if($c){$c=false;ac=ac.$blk();}if(ac&&ac.$blk!==undefined){break s;}$r=(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteString(ac);$s=43;case 43:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteByte(61);$r=c.printArg(ab,118);$s=44;case 44:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 41:z++;$s=37;continue;case 38:(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteByte(41);case 36:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.doPrintf};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.doPrintf=function(a,b){return this.$val.doPrintf(a,b);};Q.ptr.prototype.doPrint=function(a){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=false;d=a;e=0;case 1:if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);if(!(!($interfaceIsEqual(g,$ifaceNil)))){h=false;$s=3;continue s;}i=G.TypeOf(g).Kind();$s=4;case 4:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i===24;case 3:j=h;if(f>0&&!j&&!c){(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(32);}$r=b.printArg(g,118);$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}c=j;e++;$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.doPrint};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.doPrint=function(a){return this.$val.doPrint(a);};Q.ptr.prototype.doPrintln=function(a){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=a;d=0;case 1:if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]);if(e>0){(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(32);}$r=b.printArg(f,118);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d++;$s=1;continue;case 2:(b.$ptr_buf||(b.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},b))).WriteByte(10);$s=-1;return;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.doPrintln};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.doPrintln=function(a){return this.$val.doPrintln(a);};AV.ptr.prototype.Read=function(a){var a,b,c,d,e,f;b=0;c=$ifaceNil;d=this;e=0;f=C.New("ScanState's Read should not be called. Use ReadRune");b=e;c=f;return[b,c];};AV.prototype.Read=function(a){return this.$val.Read(a);};AV.ptr.prototype.ReadRune=function(){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=0;b=0;c=$ifaceNil;d=this;if(d.atEOF||d.count>=d.ssave.argLimit){c=E.EOF;$s=-1;return[a,b,c];}f=d.rs.ReadRune();$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;a=e[0];b=e[1];c=e[2];if($interfaceIsEqual(c,$ifaceNil)){d.count=d.count+(1)>>0;if(d.ssave.nlIsEnd&&(a===10)){d.atEOF=true;}}else if($interfaceIsEqual(c,E.EOF)){d.atEOF=true;}$s=-1;return[a,b,c];}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.ReadRune};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.ReadRune=function(){return this.$val.ReadRune();};AV.ptr.prototype.Width=function(){var a,b,c,d,e,f,g;a=0;b=false;c=this;if(c.ssave.maxWid===1073741824){d=0;e=false;a=d;b=e;return[a,b];}f=c.ssave.maxWid;g=true;a=f;b=g;return[a,b];};AV.prototype.Width=function(){return this.$val.Width();};AV.ptr.prototype.getRune=function(){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=0;b=this;d=b.ReadRune();$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}c=d;a=c[0];e=c[2];if(!($interfaceIsEqual(e,$ifaceNil))){if($interfaceIsEqual(e,E.EOF)){a=-1;$s=-1;return a;}b.error(e);}$s=-1;return a;}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.getRune};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.getRune=function(){return this.$val.getRune();};AV.ptr.prototype.UnreadRune=function(){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.rs.UnreadRune();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}b;a.atEOF=false;a.count=a.count-(1)>>0;$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.UnreadRune};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.UnreadRune=function(){return this.$val.UnreadRune();};AV.ptr.prototype.error=function(a){var a,b,c;b=this;$panic((c=new AU.ptr(a),new c.constructor.elem(c)));};AV.prototype.error=function(a){return this.$val.error(a);};AV.ptr.prototype.errorString=function(a){var a,b,c;b=this;$panic((c=new AU.ptr(C.New(a)),new c.constructor.elem(c)));};AV.prototype.errorString=function(a){return this.$val.errorString(a);};AV.ptr.prototype.Token=function(a,b){var a,b,c,d,e,f,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);c=[c];d=BO.nil;c[0]=$ifaceNil;e=this;$deferred.push([(function(c){return function(){var f,g,h,i;f=$recover();if(!($interfaceIsEqual(f,$ifaceNil))){g=$assertType(f,AU,true);h=$clone(g[0],AU);i=g[1];if(i){c[0]=h.err;}else{$panic(f);}}};})(c),[]]);if(b===$throwNilPointerError){b=AZ;}e.buf=$subslice(e.buf,0,0);f=e.token(a,b);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}d=f;$s=-1;return[d,c[0]];}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if(!$curGoroutine.asleep){return[d,c[0]];}if($curGoroutine.asleep){if($f===undefined){$f={$blk:AV.ptr.prototype.Token};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AV.prototype.Token=function(a,b){return this.$val.Token(a,b);};AY=function(a){var a,b,c,d,e;if(a>=65536){return false;}b=((a<<16>>>16));c=AX;d=0;while(true){if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]),BM);if(b1024){return;}b.buf=$subslice(b.buf,0,0);b.rs=$ifaceNil;BB.Put(b);};AV.prototype.free=function(a){return this.$val.free(a);};AV.ptr.prototype.SkipSpace=function(){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;case 1:b=a.getRune();$s=3;case 3:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;if(c===-1){$s=-1;return;}if(!(c===13)){d=false;$s=6;continue s;}e=a.peek("\n");$s=7;case 7:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;case 6:if(d){$s=4;continue;}$s=5;continue;case 4:$s=1;continue;case 5:if(c===10){$s=8;continue;}$s=9;continue;case 8:if(a.ssave.nlIsSpace){$s=1;continue;}a.errorString("unexpected newline");$s=-1;return;case 9:if(!AY(c)){$s=10;continue;}$s=11;continue;case 10:f=a.UnreadRune();$s=12;case 12:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}f;$s=2;continue;case 11:$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.SkipSpace};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.SkipSpace=function(){return this.$val.SkipSpace();};AV.ptr.prototype.token=function(a,b){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(a){$s=1;continue;}$s=2;continue;case 1:$r=c.SkipSpace();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 2:case 4:d=c.getRune();$s=6;case 6:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(e===-1){$s=5;continue;}f=b(e);$s=9;case 9:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}if(!f){$s=7;continue;}$s=8;continue;case 7:g=c.UnreadRune();$s=10;case 10:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}g;$s=5;continue;case 8:(c.$ptr_buf||(c.$ptr_buf=new BK(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))).WriteRune(e);$s=4;continue;case 5:$s=-1;return(h=c.buf,$subslice(new BO(h.$array),h.$offset,h.$offset+h.$length));}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.token};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.token=function(a,b){return this.$val.token(a,b);};BF=function(a,b){var a,b,c,d,e,f,g;c=a;d=0;while(true){if(!(d=0;}return;}if($f===undefined){$f={$blk:AV.ptr.prototype.peek};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AV.prototype.peek=function(a){return this.$val.peek(a);};CO.methods=[{prop:"clearflags",name:"clearflags",pkg:"fmt",typ:$funcType([],[],false)},{prop:"init",name:"init",pkg:"fmt",typ:$funcType([BK],[],false)},{prop:"writePadding",name:"writePadding",pkg:"fmt",typ:$funcType([$Int],[],false)},{prop:"pad",name:"pad",pkg:"fmt",typ:$funcType([BO],[],false)},{prop:"padString",name:"padString",pkg:"fmt",typ:$funcType([$String],[],false)},{prop:"fmtBoolean",name:"fmtBoolean",pkg:"fmt",typ:$funcType([$Bool],[],false)},{prop:"fmtUnicode",name:"fmtUnicode",pkg:"fmt",typ:$funcType([$Uint64],[],false)},{prop:"fmtInteger",name:"fmtInteger",pkg:"fmt",typ:$funcType([$Uint64,$Int,$Bool,$String],[],false)},{prop:"truncateString",name:"truncateString",pkg:"fmt",typ:$funcType([$String],[$String],false)},{prop:"truncate",name:"truncate",pkg:"fmt",typ:$funcType([BO],[BO],false)},{prop:"fmtS",name:"fmtS",pkg:"fmt",typ:$funcType([$String],[],false)},{prop:"fmtBs",name:"fmtBs",pkg:"fmt",typ:$funcType([BO],[],false)},{prop:"fmtSbx",name:"fmtSbx",pkg:"fmt",typ:$funcType([$String,BO,$String],[],false)},{prop:"fmtSx",name:"fmtSx",pkg:"fmt",typ:$funcType([$String,$String],[],false)},{prop:"fmtBx",name:"fmtBx",pkg:"fmt",typ:$funcType([BO,$String],[],false)},{prop:"fmtQ",name:"fmtQ",pkg:"fmt",typ:$funcType([$String],[],false)},{prop:"fmtC",name:"fmtC",pkg:"fmt",typ:$funcType([$Uint64],[],false)},{prop:"fmtQc",name:"fmtQc",pkg:"fmt",typ:$funcType([$Uint64],[],false)},{prop:"fmtFloat",name:"fmtFloat",pkg:"fmt",typ:$funcType([$Float64,$Int,$Int32,$Int],[],false)}];BK.methods=[{prop:"Write",name:"Write",pkg:"",typ:$funcType([BO],[],false)},{prop:"WriteString",name:"WriteString",pkg:"",typ:$funcType([$String],[],false)},{prop:"WriteByte",name:"WriteByte",pkg:"",typ:$funcType([$Uint8],[],false)},{prop:"WriteRune",name:"WriteRune",pkg:"",typ:$funcType([$Int32],[],false)}];BQ.methods=[{prop:"free",name:"free",pkg:"fmt",typ:$funcType([],[],false)},{prop:"Width",name:"Width",pkg:"",typ:$funcType([],[$Int,$Bool],false)},{prop:"Precision",name:"Precision",pkg:"",typ:$funcType([],[$Int,$Bool],false)},{prop:"Flag",name:"Flag",pkg:"",typ:$funcType([$Int],[$Bool],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([BO],[$Int,$error],false)},{prop:"WriteString",name:"WriteString",pkg:"",typ:$funcType([$String],[$Int,$error],false)},{prop:"unknownType",name:"unknownType",pkg:"fmt",typ:$funcType([G.Value],[],false)},{prop:"badVerb",name:"badVerb",pkg:"fmt",typ:$funcType([$Int32],[],false)},{prop:"fmtBool",name:"fmtBool",pkg:"fmt",typ:$funcType([$Bool,$Int32],[],false)},{prop:"fmt0x64",name:"fmt0x64",pkg:"fmt",typ:$funcType([$Uint64,$Bool],[],false)},{prop:"fmtInteger",name:"fmtInteger",pkg:"fmt",typ:$funcType([$Uint64,$Bool,$Int32],[],false)},{prop:"fmtFloat",name:"fmtFloat",pkg:"fmt",typ:$funcType([$Float64,$Int,$Int32],[],false)},{prop:"fmtComplex",name:"fmtComplex",pkg:"fmt",typ:$funcType([$Complex128,$Int,$Int32],[],false)},{prop:"fmtString",name:"fmtString",pkg:"fmt",typ:$funcType([$String,$Int32],[],false)},{prop:"fmtBytes",name:"fmtBytes",pkg:"fmt",typ:$funcType([BO,$Int32,$String],[],false)},{prop:"fmtPointer",name:"fmtPointer",pkg:"fmt",typ:$funcType([G.Value,$Int32],[],false)},{prop:"catchPanic",name:"catchPanic",pkg:"fmt",typ:$funcType([$emptyInterface,$Int32,$String],[],false)},{prop:"handleMethods",name:"handleMethods",pkg:"fmt",typ:$funcType([$Int32],[$Bool],false)},{prop:"printArg",name:"printArg",pkg:"fmt",typ:$funcType([$emptyInterface,$Int32],[],false)},{prop:"printValue",name:"printValue",pkg:"fmt",typ:$funcType([G.Value,$Int32,$Int],[],false)},{prop:"argNumber",name:"argNumber",pkg:"fmt",typ:$funcType([$Int,$String,$Int,$Int],[$Int,$Int,$Bool],false)},{prop:"badArgNum",name:"badArgNum",pkg:"fmt",typ:$funcType([$Int32],[],false)},{prop:"missingArg",name:"missingArg",pkg:"fmt",typ:$funcType([$Int32],[],false)},{prop:"doPrintf",name:"doPrintf",pkg:"fmt",typ:$funcType([$String,BI],[],false)},{prop:"doPrint",name:"doPrint",pkg:"fmt",typ:$funcType([BI],[],false)},{prop:"doPrintln",name:"doPrintln",pkg:"fmt",typ:$funcType([BI],[],false)}];BT.methods=[{prop:"Read",name:"Read",pkg:"",typ:$funcType([BO],[$Int,$error],false)},{prop:"ReadRune",name:"ReadRune",pkg:"",typ:$funcType([],[$Int32,$Int,$error],false)},{prop:"Width",name:"Width",pkg:"",typ:$funcType([],[$Int,$Bool],false)},{prop:"getRune",name:"getRune",pkg:"fmt",typ:$funcType([],[$Int32],false)},{prop:"mustReadRune",name:"mustReadRune",pkg:"fmt",typ:$funcType([],[$Int32],false)},{prop:"UnreadRune",name:"UnreadRune",pkg:"",typ:$funcType([],[$error],false)},{prop:"error",name:"error",pkg:"fmt",typ:$funcType([$error],[],false)},{prop:"errorString",name:"errorString",pkg:"fmt",typ:$funcType([$String],[],false)},{prop:"Token",name:"Token",pkg:"",typ:$funcType([$Bool,CP],[BO,$error],false)},{prop:"free",name:"free",pkg:"fmt",typ:$funcType([AW],[],false)},{prop:"SkipSpace",name:"SkipSpace",pkg:"",typ:$funcType([],[],false)},{prop:"token",name:"token",pkg:"fmt",typ:$funcType([$Bool,CP],[BO],false)},{prop:"consume",name:"consume",pkg:"fmt",typ:$funcType([$String,$Bool],[$Bool],false)},{prop:"peek",name:"peek",pkg:"fmt",typ:$funcType([$String],[$Bool],false)},{prop:"notEOF",name:"notEOF",pkg:"fmt",typ:$funcType([],[],false)},{prop:"accept",name:"accept",pkg:"fmt",typ:$funcType([$String],[$Bool],false)},{prop:"okVerb",name:"okVerb",pkg:"fmt",typ:$funcType([$Int32,$String,$String],[$Bool],false)},{prop:"scanBool",name:"scanBool",pkg:"fmt",typ:$funcType([$Int32],[$Bool],false)},{prop:"getBase",name:"getBase",pkg:"fmt",typ:$funcType([$Int32],[$Int,$String],false)},{prop:"scanNumber",name:"scanNumber",pkg:"fmt",typ:$funcType([$String,$Bool],[$String],false)},{prop:"scanRune",name:"scanRune",pkg:"fmt",typ:$funcType([$Int],[$Int64],false)},{prop:"scanBasePrefix",name:"scanBasePrefix",pkg:"fmt",typ:$funcType([],[$Int,$String,$Bool],false)},{prop:"scanInt",name:"scanInt",pkg:"fmt",typ:$funcType([$Int32,$Int],[$Int64],false)},{prop:"scanUint",name:"scanUint",pkg:"fmt",typ:$funcType([$Int32,$Int],[$Uint64],false)},{prop:"floatToken",name:"floatToken",pkg:"fmt",typ:$funcType([],[$String],false)},{prop:"complexTokens",name:"complexTokens",pkg:"fmt",typ:$funcType([],[$String,$String],false)},{prop:"convertFloat",name:"convertFloat",pkg:"fmt",typ:$funcType([$String,$Int],[$Float64],false)},{prop:"scanComplex",name:"scanComplex",pkg:"fmt",typ:$funcType([$Int32,$Int],[$Complex128],false)},{prop:"convertString",name:"convertString",pkg:"fmt",typ:$funcType([$Int32],[$String],false)},{prop:"quotedString",name:"quotedString",pkg:"fmt",typ:$funcType([],[$String],false)},{prop:"hexByte",name:"hexByte",pkg:"fmt",typ:$funcType([],[$Uint8,$Bool],false)},{prop:"hexString",name:"hexString",pkg:"fmt",typ:$funcType([],[$String],false)},{prop:"scanOne",name:"scanOne",pkg:"fmt",typ:$funcType([$Int32,$emptyInterface],[],false)},{prop:"doScan",name:"doScan",pkg:"fmt",typ:$funcType([BI],[$Int,$error],false)},{prop:"advance",name:"advance",pkg:"fmt",typ:$funcType([$String],[$Int],false)},{prop:"doScanf",name:"doScanf",pkg:"fmt",typ:$funcType([$String,BI],[$Int,$error],false)}];J.init("fmt",[{prop:"widPresent",name:"widPresent",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"precPresent",name:"precPresent",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"minus",name:"minus",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"plus",name:"plus",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"sharp",name:"sharp",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"space",name:"space",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"zero",name:"zero",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"plusV",name:"plusV",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"sharpV",name:"sharpV",embedded:false,exported:false,typ:$Bool,tag:""}]);K.init("fmt",[{prop:"buf",name:"buf",embedded:false,exported:false,typ:BK,tag:""},{prop:"fmtFlags",name:"fmtFlags",embedded:true,exported:false,typ:J,tag:""},{prop:"wid",name:"wid",embedded:false,exported:false,typ:$Int,tag:""},{prop:"prec",name:"prec",embedded:false,exported:false,typ:$Int,tag:""},{prop:"intbuf",name:"intbuf",embedded:false,exported:false,typ:BL,tag:""}]);L.init([{prop:"Flag",name:"Flag",pkg:"",typ:$funcType([$Int],[$Bool],false)},{prop:"Precision",name:"Precision",pkg:"",typ:$funcType([],[$Int,$Bool],false)},{prop:"Width",name:"Width",pkg:"",typ:$funcType([],[$Int,$Bool],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([BO],[$Int,$error],false)}]);M.init([{prop:"Format",name:"Format",pkg:"",typ:$funcType([L,$Int32],[],false)}]);N.init([{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}]);O.init([{prop:"GoString",name:"GoString",pkg:"",typ:$funcType([],[$String],false)}]);P.init($Uint8);Q.init("fmt",[{prop:"buf",name:"buf",embedded:false,exported:false,typ:P,tag:""},{prop:"arg",name:"arg",embedded:false,exported:false,typ:$emptyInterface,tag:""},{prop:"value",name:"value",embedded:false,exported:false,typ:G.Value,tag:""},{prop:"fmt",name:"fmt",embedded:false,exported:false,typ:K,tag:""},{prop:"reordered",name:"reordered",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"goodArgNum",name:"goodArgNum",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"panicking",name:"panicking",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"erroring",name:"erroring",embedded:false,exported:false,typ:$Bool,tag:""}]);AU.init("fmt",[{prop:"err",name:"err",embedded:false,exported:false,typ:$error,tag:""}]);AV.init("fmt",[{prop:"rs",name:"rs",embedded:false,exported:false,typ:E.RuneScanner,tag:""},{prop:"buf",name:"buf",embedded:false,exported:false,typ:P,tag:""},{prop:"count",name:"count",embedded:false,exported:false,typ:$Int,tag:""},{prop:"atEOF",name:"atEOF",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"ssave",name:"ssave",embedded:true,exported:false,typ:AW,tag:""}]);AW.init("fmt",[{prop:"validSave",name:"validSave",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"nlIsEnd",name:"nlIsEnd",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"nlIsSpace",name:"nlIsSpace",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"argLimit",name:"argLimit",embedded:false,exported:false,typ:$Int,tag:""},{prop:"limit",name:"limit",embedded:false,exported:false,typ:$Int,tag:""},{prop:"maxWid",name:"maxWid",embedded:false,exported:false,typ:$Int,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=C.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=I.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=G.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=H.$init();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}R=new H.Pool.ptr(0,0,BI.nil,(function(){return new Q.ptr(P.nil,$ifaceNil,new G.Value.ptr(BJ.nil,0,0),new K.ptr(BK.nil,new J.ptr(false,false,false,false,false,false,false,false,false),0,0,BL.zero()),false,false,false,false);}));AX=new BN([$toNativeArray($kindUint16,[9,13]),$toNativeArray($kindUint16,[32,32]),$toNativeArray($kindUint16,[133,133]),$toNativeArray($kindUint16,[160,160]),$toNativeArray($kindUint16,[5760,5760]),$toNativeArray($kindUint16,[8192,8202]),$toNativeArray($kindUint16,[8232,8233]),$toNativeArray($kindUint16,[8239,8239]),$toNativeArray($kindUint16,[8287,8287]),$toNativeArray($kindUint16,[12288,12288])]);BB=new H.Pool.ptr(0,0,BI.nil,(function(){return new AV.ptr($ifaceNil,P.nil,0,false,new AW.ptr(false,false,false,0,0,0));}));BD=C.New("syntax error scanning complex number");BE=C.New("syntax error scanning boolean");}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["bytes"]=(function(){var $pkg={},$init,A,D,B,E,C,BS,BV,CD,K,F,G,AJ,AM,BP,BQ,BR,BT;A=$packages["errors"];D=$packages["internal/bytealg"];B=$packages["io"];E=$packages["unicode"];C=$packages["unicode/utf8"];BS=$pkg.Reader=$newType(0,$kindStruct,"bytes.Reader",true,"bytes",true,function(s_,i_,prevRune_){this.$val=this;if(arguments.length===0){this.s=BV.nil;this.i=new $Int64(0,0);this.prevRune=0;return;}this.s=s_;this.i=i_;this.prevRune=prevRune_;});BV=$sliceType($Uint8);CD=$ptrType(BS);F=function(d,e){var d,e,f,g,h,i;f=d;g=0;while(true){if(!(g=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+g]);if(i===e){return h;}g++;}return-1;};$pkg.IndexByte=F;G=function(d,e){var d,e,f,g,h,i;if(!((d.$length===e.$length))){return false;}f=d;g=0;while(true){if(!(g=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+g]);if(!((i===((h<0||h>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+h])))){return false;}g++;}return true;};$pkg.Equal=G;AJ=function(d,e){var d,e;return d.$length>=e.$length&&G($subslice(d,0,e.$length),e);};$pkg.HasPrefix=AJ;AM=function(d,e){var d,e,f,g,h;if(e<0){$panic(new $String("bytes: negative Repeat count"));}else if(e>0&&!(((f=($imul(d.$length,e))/e,(f===f&&f!==1/0&&f!==-1/0)?f>>0:$throwRuntimeError("integer divide by zero"))===d.$length))){$panic(new $String("bytes: Repeat count causes overflow"));}g=$makeSlice(BV,($imul(d.$length,e)));h=$copySlice(g,d);while(true){if(!(h=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]));}else if((f===d.$length)){if(G(e,d)){return 0;}return-1;}else if(f>d.$length){return-1;}else if(f<=D.MaxLen){if(d.$length<=0){return D.Index(d,e);}g=(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]);h=(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1]);i=0;j=(d.$length-f>>0)+1>>0;k=0;while(true){if(!(i=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+i])===g))){l=F($subslice(d,i,j),g);if(l<0){return-1;}i=i+(l)>>0;}if(((m=i+1>>0,((m<0||m>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+m]))===h)&&G($subslice(d,i,(i+f>>0)),e)){return i;}k=k+(1)>>0;i=i+(1)>>0;if(k>D.Cutover(i)){n=D.Index($subslice(d,i),e);if(n>=0){return n+i>>0;}return-1;}}return-1;}o=(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]);p=(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1]);q=0;r=0;s=(d.$length-f>>0)+1>>0;while(true){if(!(q=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+q])===o))){t=F($subslice(d,q,s),o);if(t<0){break;}q=q+(t)>>0;}if(((u=q+1>>0,((u<0||u>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+u]))===p)&&G($subslice(d,q,(q+f>>0)),e)){return q;}q=q+(1)>>0;r=r+(1)>>0;if(r>=(4+(q>>4>>0)>>0)&&q>0;}}return-1;};$pkg.Index=BP;BQ=function(d,e){var d,e,f,g,h,i,j,k,l,m;f=BR(e);g=f[0];h=f[1];i=e.$length;j=0;k=0;while(true){if(!(k>>0)+((((k<0||k>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+k])>>>0))>>>0;k=k+(1)>>0;}if((j===g)&&G($subslice(d,0,i),e)){return 0;}l=i;while(true){if(!(l>>0;j=j+(((((l<0||l>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+l])>>>0)))>>>0;j=j-(($imul(h,(((m=l-i>>0,((m<0||m>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+m]))>>>0)))>>>0))>>>0;l=l+(1)>>0;if((j===g)&&G($subslice(d,(l-i>>0),l),e)){return l-i>>0;}}return-1;};BR=function(d){var d,e,f,g,h,i,j,k;e=0;f=0;while(true){if(!(f>>0)+((((f<0||f>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+f])>>>0))>>>0;f=f+(1)>>0;}g=1;h=16777619;i=g;j=h;k=d.$length;while(true){if(!(k>0)){break;}if(!(((k&1)===0))){i=$imul(i,(j))>>>0;}j=$imul(j,(j))>>>0;k=(k>>$min((1),31))>>0;}return[e,i];};BS.ptr.prototype.Len=function(){var d,e,f,g,h,i;d=this;if((e=d.i,f=(new $Int64(0,d.s.$length)),(e.$high>f.$high||(e.$high===f.$high&&e.$low>=f.$low)))){return 0;}return(((g=(h=(new $Int64(0,d.s.$length)),i=d.i,new $Int64(h.$high-i.$high,h.$low-i.$low)),g.$low+((g.$high>>31)*4294967296))>>0));};BS.prototype.Len=function(){return this.$val.Len();};BS.ptr.prototype.Size=function(){var d;d=this;return(new $Int64(0,d.s.$length));};BS.prototype.Size=function(){return this.$val.Size();};BS.ptr.prototype.Read=function(d){var d,e,f,g,h,i,j,k,l,m;e=0;f=$ifaceNil;g=this;if((h=g.i,i=(new $Int64(0,g.s.$length)),(h.$high>i.$high||(h.$high===i.$high&&h.$low>=i.$low)))){j=0;k=B.EOF;e=j;f=k;return[e,f];}g.prevRune=-1;e=$copySlice(d,$subslice(g.s,$flatten64(g.i)));g.i=(l=g.i,m=(new $Int64(0,e)),new $Int64(l.$high+m.$high,l.$low+m.$low));return[e,f];};BS.prototype.Read=function(d){return this.$val.Read(d);};BS.ptr.prototype.ReadAt=function(d,e){var d,e,f,g,h,i,j,k,l,m;f=0;g=$ifaceNil;h=this;if((e.$high<0||(e.$high===0&&e.$low<0))){i=0;j=A.New("bytes.Reader.ReadAt: negative offset");f=i;g=j;return[f,g];}if((k=(new $Int64(0,h.s.$length)),(e.$high>k.$high||(e.$high===k.$high&&e.$low>=k.$low)))){l=0;m=B.EOF;f=l;g=m;return[f,g];}f=$copySlice(d,$subslice(h.s,$flatten64(e)));if(ff.$high||(e.$high===f.$high&&e.$low>=f.$low)))){return[0,B.EOF];}i=(g=d.s,h=d.i,(($flatten64(h)<0||$flatten64(h)>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+$flatten64(h)]));d.i=(j=d.i,k=new $Int64(0,1),new $Int64(j.$high+k.$high,j.$low+k.$low));return[i,$ifaceNil];};BS.prototype.ReadByte=function(){return this.$val.ReadByte();};BS.ptr.prototype.UnreadByte=function(){var d,e,f,g;d=this;if((e=d.i,(e.$high<0||(e.$high===0&&e.$low<=0)))){return A.New("bytes.Reader.UnreadByte: at beginning of slice");}d.prevRune=-1;d.i=(f=d.i,g=new $Int64(0,1),new $Int64(f.$high-g.$high,f.$low-g.$low));return $ifaceNil;};BS.prototype.UnreadByte=function(){return this.$val.UnreadByte();};BS.ptr.prototype.ReadRune=function(){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x;d=0;e=0;f=$ifaceNil;g=this;if((h=g.i,i=(new $Int64(0,g.s.$length)),(h.$high>i.$high||(h.$high===i.$high&&h.$low>=i.$low)))){g.prevRune=-1;j=0;k=0;l=B.EOF;d=j;e=k;f=l;return[d,e,f];}g.prevRune=(((m=g.i,m.$low+((m.$high>>31)*4294967296))>>0));p=(n=g.s,o=g.i,(($flatten64(o)<0||$flatten64(o)>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+$flatten64(o)]));if(p<128){g.i=(q=g.i,r=new $Int64(0,1),new $Int64(q.$high+r.$high,q.$low+r.$low));s=((p>>0));t=1;u=$ifaceNil;d=s;e=t;f=u;return[d,e,f];}v=C.DecodeRune($subslice(g.s,$flatten64(g.i)));d=v[0];e=v[1];g.i=(w=g.i,x=(new $Int64(0,e)),new $Int64(w.$high+x.$high,w.$low+x.$low));return[d,e,f];};BS.prototype.ReadRune=function(){return this.$val.ReadRune();};BS.ptr.prototype.UnreadRune=function(){var d,e;d=this;if((e=d.i,(e.$high<0||(e.$high===0&&e.$low<=0)))){return A.New("bytes.Reader.UnreadRune: at beginning of slice");}if(d.prevRune<0){return A.New("bytes.Reader.UnreadRune: previous operation was not ReadRune");}d.i=(new $Int64(0,d.prevRune));d.prevRune=-1;return $ifaceNil;};BS.prototype.UnreadRune=function(){return this.$val.UnreadRune();};BS.ptr.prototype.Seek=function(d,e){var d,e,f,g,h,i,j;f=this;f.prevRune=-1;g=new $Int64(0,0);h=e;if(h===(0)){g=d;}else if(h===(1)){g=(i=f.i,new $Int64(i.$high+d.$high,i.$low+d.$low));}else if(h===(2)){g=(j=(new $Int64(0,f.s.$length)),new $Int64(j.$high+d.$high,j.$low+d.$low));}else{return[new $Int64(0,0),A.New("bytes.Reader.Seek: invalid whence")];}if((g.$high<0||(g.$high===0&&g.$low<0))){return[new $Int64(0,0),A.New("bytes.Reader.Seek: negative position")];}f.i=g;return[g,$ifaceNil];};BS.prototype.Seek=function(d,e){return this.$val.Seek(d,e);};BS.ptr.prototype.WriteTo=function(d){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=new $Int64(0,0);f=$ifaceNil;g=this;g.prevRune=-1;if((h=g.i,i=(new $Int64(0,g.s.$length)),(h.$high>i.$high||(h.$high===i.$high&&h.$low>=i.$low)))){j=new $Int64(0,0);k=$ifaceNil;e=j;f=k;$s=-1;return[e,f];}l=$subslice(g.s,$flatten64(g.i));n=d.Write(l);$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m[0];f=m[1];if(o>l.$length){$panic(new $String("bytes.Reader.WriteTo: invalid Write count"));}g.i=(p=g.i,q=(new $Int64(0,o)),new $Int64(p.$high+q.$high,p.$low+q.$low));e=(new $Int64(0,o));if(!((o===l.$length))&&$interfaceIsEqual(f,$ifaceNil)){f=B.ErrShortWrite;}$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:BS.ptr.prototype.WriteTo};}$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.$s=$s;$f.$r=$r;return $f;};BS.prototype.WriteTo=function(d){return this.$val.WriteTo(d);};BS.ptr.prototype.Reset=function(d){var d,e;e=this;BS.copy(e,new BS.ptr(d,new $Int64(0,0),-1));};BS.prototype.Reset=function(d){return this.$val.Reset(d);};BT=function(d){var d;return new BS.ptr(d,new $Int64(0,0),-1);};$pkg.NewReader=BT;CD.methods=[{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Size",name:"Size",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([BV],[$Int,$error],false)},{prop:"ReadAt",name:"ReadAt",pkg:"",typ:$funcType([BV,$Int64],[$Int,$error],false)},{prop:"ReadByte",name:"ReadByte",pkg:"",typ:$funcType([],[$Uint8,$error],false)},{prop:"UnreadByte",name:"UnreadByte",pkg:"",typ:$funcType([],[$error],false)},{prop:"ReadRune",name:"ReadRune",pkg:"",typ:$funcType([],[$Int32,$Int,$error],false)},{prop:"UnreadRune",name:"UnreadRune",pkg:"",typ:$funcType([],[$error],false)},{prop:"Seek",name:"Seek",pkg:"",typ:$funcType([$Int64,$Int],[$Int64,$error],false)},{prop:"WriteTo",name:"WriteTo",pkg:"",typ:$funcType([B.Writer],[$Int64,$error],false)},{prop:"Reset",name:"Reset",pkg:"",typ:$funcType([BV],[],false)}];BS.init("bytes",[{prop:"s",name:"s",embedded:false,exported:false,typ:BV,tag:""},{prop:"i",name:"i",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"prevRune",name:"prevRune",embedded:false,exported:false,typ:$Int,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$pkg.ErrTooLarge=A.New("bytes.Buffer: too large");K=A.New("bytes.Buffer: reader returned negative count from Read");}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["encoding/binary"]=(function(){var $pkg={},$init,A,B,C,D,E,F,G,N,O,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,U,H,I,J,K,L,P;A=$packages["errors"];B=$packages["io"];C=$packages["math"];D=$packages["reflect"];E=$pkg.ByteOrder=$newType(8,$kindInterface,"binary.ByteOrder",true,"encoding/binary",true,null);F=$pkg.littleEndian=$newType(0,$kindStruct,"binary.littleEndian",true,"encoding/binary",false,function(){this.$val=this;if(arguments.length===0){return;}});G=$pkg.bigEndian=$newType(0,$kindStruct,"binary.bigEndian",true,"encoding/binary",false,function(){this.$val=this;if(arguments.length===0){return;}});N=$pkg.decoder=$newType(0,$kindStruct,"binary.decoder",true,"encoding/binary",false,function(order_,buf_,offset_){this.$val=this;if(arguments.length===0){this.order=$ifaceNil;this.buf=X.nil;this.offset=0;return;}this.order=order_;this.buf=buf_;this.offset=offset_;});O=$pkg.encoder=$newType(0,$kindStruct,"binary.encoder",true,"encoding/binary",false,function(order_,buf_,offset_){this.$val=this;if(arguments.length===0){this.order=$ifaceNil;this.buf=X.nil;this.offset=0;return;}this.order=order_;this.buf=buf_;this.offset=offset_;});X=$sliceType($Uint8);Y=$ptrType($Bool);Z=$ptrType($Int8);AA=$ptrType($Uint8);AB=$ptrType($Int16);AC=$ptrType($Uint16);AD=$ptrType($Int32);AE=$ptrType($Uint32);AF=$ptrType($Int64);AG=$ptrType($Uint64);AH=$sliceType($Bool);AI=$sliceType($Int8);AJ=$sliceType($Int16);AK=$sliceType($Uint16);AL=$sliceType($Int32);AM=$sliceType($Uint32);AN=$sliceType($Int64);AO=$sliceType($Uint64);AP=$ptrType(N);AQ=$ptrType(O);F.ptr.prototype.Uint16=function(a){var a;$unused((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]));return((((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0])<<16>>>16))|((((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1])<<16>>>16))<<8<<16>>>16))>>>0;};F.prototype.Uint16=function(a){return this.$val.Uint16(a);};F.ptr.prototype.PutUint16=function(a,b){var a,b;$unused((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=((b<<24>>>24)));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=(((b>>>8<<16>>>16)<<24>>>24)));};F.prototype.PutUint16=function(a,b){return this.$val.PutUint16(a,b);};F.ptr.prototype.Uint32=function(a){var a;$unused((3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]));return((((((((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0])>>>0))|((((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1])>>>0))<<8>>>0))>>>0)|((((2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2])>>>0))<<16>>>0))>>>0)|((((3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3])>>>0))<<24>>>0))>>>0;};F.prototype.Uint32=function(a){return this.$val.Uint32(a);};F.ptr.prototype.PutUint32=function(a,b){var a,b;$unused((3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=((b<<24>>>24)));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=(((b>>>8>>>0)<<24>>>24)));(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]=(((b>>>16>>>0)<<24>>>24)));(3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]=(((b>>>24>>>0)<<24>>>24)));};F.prototype.PutUint32=function(a,b){return this.$val.PutUint32(a,b);};F.ptr.prototype.Uint64=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;$unused((7>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+7]));return(b=(c=(d=(e=(f=(g=(h=(new $Uint64(0,(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]))),i=$shiftLeft64((new $Uint64(0,(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]))),8),new $Uint64(h.$high|i.$high,(h.$low|i.$low)>>>0)),j=$shiftLeft64((new $Uint64(0,(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]))),16),new $Uint64(g.$high|j.$high,(g.$low|j.$low)>>>0)),k=$shiftLeft64((new $Uint64(0,(3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]))),24),new $Uint64(f.$high|k.$high,(f.$low|k.$low)>>>0)),l=$shiftLeft64((new $Uint64(0,(4>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+4]))),32),new $Uint64(e.$high|l.$high,(e.$low|l.$low)>>>0)),m=$shiftLeft64((new $Uint64(0,(5>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+5]))),40),new $Uint64(d.$high|m.$high,(d.$low|m.$low)>>>0)),n=$shiftLeft64((new $Uint64(0,(6>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+6]))),48),new $Uint64(c.$high|n.$high,(c.$low|n.$low)>>>0)),o=$shiftLeft64((new $Uint64(0,(7>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+7]))),56),new $Uint64(b.$high|o.$high,(b.$low|o.$low)>>>0));};F.prototype.Uint64=function(a){return this.$val.Uint64(a);};F.ptr.prototype.PutUint64=function(a,b){var a,b;$unused((7>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+7]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=((b.$low<<24>>>24)));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=(($shiftRightUint64(b,8).$low<<24>>>24)));(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]=(($shiftRightUint64(b,16).$low<<24>>>24)));(3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]=(($shiftRightUint64(b,24).$low<<24>>>24)));(4>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+4]=(($shiftRightUint64(b,32).$low<<24>>>24)));(5>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+5]=(($shiftRightUint64(b,40).$low<<24>>>24)));(6>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+6]=(($shiftRightUint64(b,48).$low<<24>>>24)));(7>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+7]=(($shiftRightUint64(b,56).$low<<24>>>24)));};F.prototype.PutUint64=function(a,b){return this.$val.PutUint64(a,b);};F.ptr.prototype.String=function(){return"LittleEndian";};F.prototype.String=function(){return this.$val.String();};F.ptr.prototype.GoString=function(){return"binary.LittleEndian";};F.prototype.GoString=function(){return this.$val.GoString();};G.ptr.prototype.Uint16=function(a){var a;$unused((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]));return((((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1])<<16>>>16))|((((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0])<<16>>>16))<<8<<16>>>16))>>>0;};G.prototype.Uint16=function(a){return this.$val.Uint16(a);};G.ptr.prototype.PutUint16=function(a,b){var a,b;$unused((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=(((b>>>8<<16>>>16)<<24>>>24)));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=((b<<24>>>24)));};G.prototype.PutUint16=function(a,b){return this.$val.PutUint16(a,b);};G.ptr.prototype.Uint32=function(a){var a;$unused((3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]));return((((((((3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3])>>>0))|((((2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2])>>>0))<<8>>>0))>>>0)|((((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1])>>>0))<<16>>>0))>>>0)|((((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0])>>>0))<<24>>>0))>>>0;};G.prototype.Uint32=function(a){return this.$val.Uint32(a);};G.ptr.prototype.PutUint32=function(a,b){var a,b;$unused((3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=(((b>>>24>>>0)<<24>>>24)));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=(((b>>>16>>>0)<<24>>>24)));(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]=(((b>>>8>>>0)<<24>>>24)));(3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]=((b<<24>>>24)));};G.prototype.PutUint32=function(a,b){return this.$val.PutUint32(a,b);};G.ptr.prototype.Uint64=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;$unused((7>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+7]));return(b=(c=(d=(e=(f=(g=(h=(new $Uint64(0,(7>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+7]))),i=$shiftLeft64((new $Uint64(0,(6>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+6]))),8),new $Uint64(h.$high|i.$high,(h.$low|i.$low)>>>0)),j=$shiftLeft64((new $Uint64(0,(5>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+5]))),16),new $Uint64(g.$high|j.$high,(g.$low|j.$low)>>>0)),k=$shiftLeft64((new $Uint64(0,(4>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+4]))),24),new $Uint64(f.$high|k.$high,(f.$low|k.$low)>>>0)),l=$shiftLeft64((new $Uint64(0,(3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]))),32),new $Uint64(e.$high|l.$high,(e.$low|l.$low)>>>0)),m=$shiftLeft64((new $Uint64(0,(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]))),40),new $Uint64(d.$high|m.$high,(d.$low|m.$low)>>>0)),n=$shiftLeft64((new $Uint64(0,(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]))),48),new $Uint64(c.$high|n.$high,(c.$low|n.$low)>>>0)),o=$shiftLeft64((new $Uint64(0,(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]))),56),new $Uint64(b.$high|o.$high,(b.$low|o.$low)>>>0));};G.prototype.Uint64=function(a){return this.$val.Uint64(a);};G.ptr.prototype.PutUint64=function(a,b){var a,b;$unused((7>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+7]));(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]=(($shiftRightUint64(b,56).$low<<24>>>24)));(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1]=(($shiftRightUint64(b,48).$low<<24>>>24)));(2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2]=(($shiftRightUint64(b,40).$low<<24>>>24)));(3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3]=(($shiftRightUint64(b,32).$low<<24>>>24)));(4>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+4]=(($shiftRightUint64(b,24).$low<<24>>>24)));(5>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+5]=(($shiftRightUint64(b,16).$low<<24>>>24)));(6>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+6]=(($shiftRightUint64(b,8).$low<<24>>>24)));(7>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+7]=((b.$low<<24>>>24)));};G.prototype.PutUint64=function(a,b){return this.$val.PutUint64(a,b);};G.ptr.prototype.String=function(){return"BigEndian";};G.prototype.String=function(){return this.$val.String();};G.ptr.prototype.GoString=function(){return"binary.BigEndian";};G.prototype.GoString=function(){return this.$val.GoString();};H=function(a,b,c){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,bx,by,bz,c,ca,cb,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;b=$f.b;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;bj=$f.bj;bk=$f.bk;bl=$f.bl;bm=$f.bm;bn=$f.bn;bo=$f.bo;bp=$f.bp;bq=$f.bq;br=$f.br;bs=$f.bs;bt=$f.bt;bu=$f.bu;bv=$f.bv;bw=$f.bw;bx=$f.bx;by=$f.by;bz=$f.bz;c=$f.c;ca=$f.ca;cb=$f.cb;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=P(c);if(!((d===0))){$s=1;continue;}$s=2;continue;case 1:e=$makeSlice(X,d);g=B.ReadFull(a,e);$s=3;case 3:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}f=g;h=f[1];if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return h;}i=c;if($assertType(i,Y,true)[1]){$s=4;continue;}if($assertType(i,Z,true)[1]){$s=5;continue;}if($assertType(i,AA,true)[1]){$s=6;continue;}if($assertType(i,AB,true)[1]){$s=7;continue;}if($assertType(i,AC,true)[1]){$s=8;continue;}if($assertType(i,AD,true)[1]){$s=9;continue;}if($assertType(i,AE,true)[1]){$s=10;continue;}if($assertType(i,AF,true)[1]){$s=11;continue;}if($assertType(i,AG,true)[1]){$s=12;continue;}if($assertType(i,AH,true)[1]){$s=13;continue;}if($assertType(i,AI,true)[1]){$s=14;continue;}if($assertType(i,X,true)[1]){$s=15;continue;}if($assertType(i,AJ,true)[1]){$s=16;continue;}if($assertType(i,AK,true)[1]){$s=17;continue;}if($assertType(i,AL,true)[1]){$s=18;continue;}if($assertType(i,AM,true)[1]){$s=19;continue;}if($assertType(i,AN,true)[1]){$s=20;continue;}if($assertType(i,AO,true)[1]){$s=21;continue;}$s=22;continue;case 4:j=i.$val;j.$set(!(((0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0])===0)));$s=22;continue;case 5:k=i.$val;k.$set((((0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0])<<24>>24)));$s=22;continue;case 6:l=i.$val;l.$set((0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]));$s=22;continue;case 7:m=i.$val;ab=b.Uint16(e);$s=23;case 23:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}m.$set(((ab<<16>>16)));$s=22;continue;case 8:n=i.$val;ac=b.Uint16(e);$s=24;case 24:if($c){$c=false;ac=ac.$blk();}if(ac&&ac.$blk!==undefined){break s;}n.$set(ac);$s=22;continue;case 9:o=i.$val;ad=b.Uint32(e);$s=25;case 25:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}o.$set(((ad>>0)));$s=22;continue;case 10:p=i.$val;ae=b.Uint32(e);$s=26;case 26:if($c){$c=false;ae=ae.$blk();}if(ae&&ae.$blk!==undefined){break s;}p.$set(ae);$s=22;continue;case 11:q=i.$val;ag=b.Uint64(e);$s=27;case 27:if($c){$c=false;ag=ag.$blk();}if(ag&&ag.$blk!==undefined){break s;}q.$set(((af=ag,new $Int64(af.$high,af.$low))));$s=22;continue;case 12:r=i.$val;ah=b.Uint64(e);$s=28;case 28:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}r.$set(ah);$s=22;continue;case 13:s=i.$val;ai=e;aj=0;while(true){if(!(aj=ai.$length)?($throwRuntimeError("index out of range"),undefined):ai.$array[ai.$offset+aj]);((ak<0||ak>=s.$length)?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+ak]=!((al===0)));aj++;}$s=22;continue;case 14:t=i.$val;am=e;an=0;while(true){if(!(an=am.$length)?($throwRuntimeError("index out of range"),undefined):am.$array[am.$offset+an]);((ao<0||ao>=t.$length)?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+ao]=((ap<<24>>24)));an++;}$s=22;continue;case 15:u=i.$val;$copySlice(u,e);$s=22;continue;case 16:v=i.$val;aq=v;ar=0;case 29:if(!(ar=v.$length)?($throwRuntimeError("index out of range"),undefined):v.$array[v.$offset+as]=((at<<16>>16)));ar++;$s=29;continue;case 30:$s=22;continue;case 17:w=i.$val;au=w;av=0;case 32:if(!(av=w.$length)?($throwRuntimeError("index out of range"),undefined):w.$array[w.$offset+aw]=ax);av++;$s=32;continue;case 33:$s=22;continue;case 18:x=i.$val;ay=x;az=0;case 35:if(!(az=x.$length)?($throwRuntimeError("index out of range"),undefined):x.$array[x.$offset+ba]=((bb>>0)));az++;$s=35;continue;case 36:$s=22;continue;case 19:y=i.$val;bc=y;bd=0;case 38:if(!(bd=y.$length)?($throwRuntimeError("index out of range"),undefined):y.$array[y.$offset+be]=bf);bd++;$s=38;continue;case 39:$s=22;continue;case 20:z=i.$val;bg=z;bh=0;case 41:if(!(bh=z.$length)?($throwRuntimeError("index out of range"),undefined):z.$array[z.$offset+bi]=((bj=bk,new $Int64(bj.$high,bj.$low))));bh++;$s=41;continue;case 42:$s=22;continue;case 21:aa=i.$val;bl=aa;bm=0;case 44:if(!(bm=aa.$length)?($throwRuntimeError("index out of range"),undefined):aa.$array[aa.$offset+bn]=bo);bm++;$s=44;continue;case 45:case 22:$s=-1;return $ifaceNil;case 2:bp=D.ValueOf(c);$s=47;case 47:if($c){$c=false;bp=bp.$blk();}if(bp&&bp.$blk!==undefined){break s;}bq=bp;br=-1;bs=$clone(bq,D.Value).Kind();if(bs===(22)){$s=49;continue;}if(bs===(23)){$s=50;continue;}$s=51;continue;case 49:bt=$clone(bq,D.Value).Elem();$s=52;case 52:if($c){$c=false;bt=bt.$blk();}if(bt&&bt.$blk!==undefined){break s;}bq=bt;bu=K($clone(bq,D.Value));$s=53;case 53:if($c){$c=false;bu=bu.$blk();}if(bu&&bu.$blk!==undefined){break s;}br=bu;$s=51;continue;case 50:bv=K($clone(bq,D.Value));$s=54;case 54:if($c){$c=false;bv=bv.$blk();}if(bv&&bv.$blk!==undefined){break s;}br=bv;case 51:case 48:if(br<0){$s=55;continue;}$s=56;continue;case 55:bw=D.TypeOf(c).String();$s=57;case 57:if($c){$c=false;bw=bw.$blk();}if(bw&&bw.$blk!==undefined){break s;}bx=A.New("binary.Read: invalid type "+bw);$s=58;case 58:if($c){$c=false;bx=bx.$blk();}if(bx&&bx.$blk!==undefined){break s;}$s=-1;return bx;case 56:by=new N.ptr(b,$makeSlice(X,br),0);ca=B.ReadFull(a,by.buf);$s=59;case 59:if($c){$c=false;ca=ca.$blk();}if(ca&&ca.$blk!==undefined){break s;}bz=ca;cb=bz[1];if(!($interfaceIsEqual(cb,$ifaceNil))){$s=-1;return cb;}$r=by.value($clone(bq,D.Value));$s=60;case 60:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:H};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.b=b;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.bj=bj;$f.bk=bk;$f.bl=bl;$f.bm=bm;$f.bn=bn;$f.bo=bo;$f.bp=bp;$f.bq=bq;$f.br=br;$f.bs=bs;$f.bt=bt;$f.bu=bu;$f.bv=bv;$f.bw=bw;$f.bx=bx;$f.by=by;$f.bz=bz;$f.c=c;$f.ca=ca;$f.cb=cb;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Read=H;I=function(a,b,c){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,bq,br,bs,bt,bu,bv,bw,bx,by,bz,c,ca,cb,cc,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;b=$f.b;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;bj=$f.bj;bk=$f.bk;bl=$f.bl;bm=$f.bm;bn=$f.bn;bo=$f.bo;bp=$f.bp;bq=$f.bq;br=$f.br;bs=$f.bs;bt=$f.bt;bu=$f.bu;bv=$f.bv;bw=$f.bw;bx=$f.bx;by=$f.by;bz=$f.bz;c=$f.c;ca=$f.ca;cb=$f.cb;cc=$f.cc;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=P(c);if(!((d===0))){$s=1;continue;}$s=2;continue;case 1:e=$makeSlice(X,d);f=c;if($assertType(f,Y,true)[1]){$s=3;continue;}if($assertType(f,$Bool,true)[1]){$s=4;continue;}if($assertType(f,AH,true)[1]){$s=5;continue;}if($assertType(f,Z,true)[1]){$s=6;continue;}if($assertType(f,$Int8,true)[1]){$s=7;continue;}if($assertType(f,AI,true)[1]){$s=8;continue;}if($assertType(f,AA,true)[1]){$s=9;continue;}if($assertType(f,$Uint8,true)[1]){$s=10;continue;}if($assertType(f,X,true)[1]){$s=11;continue;}if($assertType(f,AB,true)[1]){$s=12;continue;}if($assertType(f,$Int16,true)[1]){$s=13;continue;}if($assertType(f,AJ,true)[1]){$s=14;continue;}if($assertType(f,AC,true)[1]){$s=15;continue;}if($assertType(f,$Uint16,true)[1]){$s=16;continue;}if($assertType(f,AK,true)[1]){$s=17;continue;}if($assertType(f,AD,true)[1]){$s=18;continue;}if($assertType(f,$Int32,true)[1]){$s=19;continue;}if($assertType(f,AL,true)[1]){$s=20;continue;}if($assertType(f,AE,true)[1]){$s=21;continue;}if($assertType(f,$Uint32,true)[1]){$s=22;continue;}if($assertType(f,AM,true)[1]){$s=23;continue;}if($assertType(f,AF,true)[1]){$s=24;continue;}if($assertType(f,$Int64,true)[1]){$s=25;continue;}if($assertType(f,AN,true)[1]){$s=26;continue;}if($assertType(f,AG,true)[1]){$s=27;continue;}if($assertType(f,$Uint64,true)[1]){$s=28;continue;}if($assertType(f,AO,true)[1]){$s=29;continue;}$s=30;continue;case 3:g=f.$val;if(g.$get()){(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=1);}else{(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=0);}$s=30;continue;case 4:h=f.$val;if(h){(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=1);}else{(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=0);}$s=30;continue;case 5:i=f.$val;ah=i;ai=0;while(true){if(!(ai=ah.$length)?($throwRuntimeError("index out of range"),undefined):ah.$array[ah.$offset+ai]);if(ak){((aj<0||aj>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+aj]=1);}else{((aj<0||aj>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+aj]=0);}ai++;}$s=30;continue;case 6:j=f.$val;(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=((j.$get()<<24>>>24)));$s=30;continue;case 7:k=f.$val;(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=((k<<24>>>24)));$s=30;continue;case 8:l=f.$val;al=l;am=0;while(true){if(!(am=al.$length)?($throwRuntimeError("index out of range"),undefined):al.$array[al.$offset+am]);((an<0||an>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+an]=((ao<<24>>>24)));am++;}$s=30;continue;case 9:m=f.$val;(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=m.$get());$s=30;continue;case 10:n=f.$val;(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=n);$s=30;continue;case 11:o=f.$val;e=o;$s=30;continue;case 12:p=f.$val;$r=b.PutUint16(e,((p.$get()<<16>>>16)));$s=31;case 31:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 13:q=f.$val;$r=b.PutUint16(e,((q<<16>>>16)));$s=32;case 32:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 14:r=f.$val;ap=r;aq=0;case 33:if(!(aq=ap.$length)?($throwRuntimeError("index out of range"),undefined):ap.$array[ap.$offset+aq]);$r=b.PutUint16($subslice(e,($imul(2,ar))),((as<<16>>>16)));$s=35;case 35:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}aq++;$s=33;continue;case 34:$s=30;continue;case 15:s=f.$val;$r=b.PutUint16(e,s.$get());$s=36;case 36:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 16:t=f.$val;$r=b.PutUint16(e,t);$s=37;case 37:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 17:u=f.$val;at=u;au=0;case 38:if(!(au=at.$length)?($throwRuntimeError("index out of range"),undefined):at.$array[at.$offset+au]);$r=b.PutUint16($subslice(e,($imul(2,av))),aw);$s=40;case 40:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}au++;$s=38;continue;case 39:$s=30;continue;case 18:v=f.$val;$r=b.PutUint32(e,((v.$get()>>>0)));$s=41;case 41:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 19:w=f.$val;$r=b.PutUint32(e,((w>>>0)));$s=42;case 42:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 20:x=f.$val;ax=x;ay=0;case 43:if(!(ay=ax.$length)?($throwRuntimeError("index out of range"),undefined):ax.$array[ax.$offset+ay]);$r=b.PutUint32($subslice(e,($imul(4,az))),((ba>>>0)));$s=45;case 45:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}ay++;$s=43;continue;case 44:$s=30;continue;case 21:y=f.$val;$r=b.PutUint32(e,y.$get());$s=46;case 46:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 22:z=f.$val;$r=b.PutUint32(e,z);$s=47;case 47:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 23:aa=f.$val;bb=aa;bc=0;case 48:if(!(bc=bb.$length)?($throwRuntimeError("index out of range"),undefined):bb.$array[bb.$offset+bc]);$r=b.PutUint32($subslice(e,($imul(4,bd))),be);$s=50;case 50:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}bc++;$s=48;continue;case 49:$s=30;continue;case 24:ab=f.$val;$r=b.PutUint64(e,((bf=ab.$get(),new $Uint64(bf.$high,bf.$low))));$s=51;case 51:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 25:ac=f.$val;$r=b.PutUint64(e,(new $Uint64(ac.$high,ac.$low)));$s=52;case 52:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 26:ad=f.$val;bg=ad;bh=0;case 53:if(!(bh=bg.$length)?($throwRuntimeError("index out of range"),undefined):bg.$array[bg.$offset+bh]);$r=b.PutUint64($subslice(e,($imul(8,bi))),(new $Uint64(bj.$high,bj.$low)));$s=55;case 55:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}bh++;$s=53;continue;case 54:$s=30;continue;case 27:ae=f.$val;$r=b.PutUint64(e,ae.$get());$s=56;case 56:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 28:af=f.$val;$r=b.PutUint64(e,af);$s=57;case 57:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=30;continue;case 29:ag=f.$val;bk=ag;bl=0;case 58:if(!(bl=bk.$length)?($throwRuntimeError("index out of range"),undefined):bk.$array[bk.$offset+bl]);$r=b.PutUint64($subslice(e,($imul(8,bm))),bn);$s=60;case 60:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}bl++;$s=58;continue;case 59:case 30:bp=a.Write(e);$s=61;case 61:if($c){$c=false;bp=bp.$blk();}if(bp&&bp.$blk!==undefined){break s;}bo=bp;bq=bo[1];$s=-1;return bq;case 2:br=D.ValueOf(c);$s=62;case 62:if($c){$c=false;br=br.$blk();}if(br&&br.$blk!==undefined){break s;}bs=D.Indirect($clone(br,D.Value));$s=63;case 63:if($c){$c=false;bs=bs.$blk();}if(bs&&bs.$blk!==undefined){break s;}bt=bs;bu=K($clone(bt,D.Value));$s=64;case 64:if($c){$c=false;bu=bu.$blk();}if(bu&&bu.$blk!==undefined){break s;}bv=bu;if(bv<0){$s=65;continue;}$s=66;continue;case 65:bw=D.TypeOf(c).String();$s=67;case 67:if($c){$c=false;bw=bw.$blk();}if(bw&&bw.$blk!==undefined){break s;}bx=A.New("binary.Write: invalid type "+bw);$s=68;case 68:if($c){$c=false;bx=bx.$blk();}if(bx&&bx.$blk!==undefined){break s;}$s=-1;return bx;case 66:by=$makeSlice(X,bv);bz=new O.ptr(b,by,0);$r=bz.value($clone(bt,D.Value));$s=69;case 69:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}cb=a.Write(by);$s=70;case 70:if($c){$c=false;cb=cb.$blk();}if(cb&&cb.$blk!==undefined){break s;}ca=cb;cc=ca[1];$s=-1;return cc;}return;}if($f===undefined){$f={$blk:I};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.b=b;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.bj=bj;$f.bk=bk;$f.bl=bl;$f.bm=bm;$f.bn=bn;$f.bo=bo;$f.bp=bp;$f.bq=bq;$f.br=br;$f.bs=bs;$f.bt=bt;$f.bu=bu;$f.bv=bv;$f.bw=bw;$f.bx=bx;$f.by=by;$f.bz=bz;$f.c=c;$f.ca=ca;$f.cb=cb;$f.cc=cc;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Write=I;J=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=D.ValueOf(a);$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=D.Indirect($clone(b,D.Value));$s=2;case 2:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=K($clone(c,D.Value));$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:J};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Size=J;K=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if($clone(a,D.Value).Kind()===23){$s=1;continue;}$s=2;continue;case 1:b=$clone(a,D.Value).Type().Elem();$s=3;case 3:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=L(b);$s=4;case 4:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;if(d>=0){$s=-1;return $imul(d,$clone(a,D.Value).Len());}$s=-1;return-1;case 2:e=L($clone(a,D.Value).Type());$s=5;case 5:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;}return;}if($f===undefined){$f={$blk:K};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};L=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=a.Kind();$s=2;case 2:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;if(c===(17)){$s=3;continue;}if(c===(25)){$s=4;continue;}if((c===(1))||(c===(8))||(c===(9))||(c===(10))||(c===(11))||(c===(3))||(c===(4))||(c===(5))||(c===(6))||(c===(13))||(c===(14))||(c===(15))||(c===(16))){$s=5;continue;}$s=6;continue;case 3:d=a.Elem();$s=7;case 7:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=L(d);$s=8;case 8:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(f>=0){$s=9;continue;}$s=10;continue;case 9:g=a.Len();$s=11;case 11:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}$s=-1;return $imul(f,g);case 10:$s=6;continue;case 4:h=0;i=0;k=a.NumField();$s=12;case 12:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;l=i;m=j;case 13:if(!(l>0;l=l+(1)>>0;$s=13;continue;case 14:$s=-1;return h;case 5:q=a.Size();$s=17;case 17:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}$s=-1;return((q>>0));case 6:case 1:$s=-1;return-1;}return;}if($f===undefined){$f={$blk:L};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.$s=$s;$f.$r=$r;return $f;};N.ptr.prototype.bool=function(){var a,b,c,d;a=this;d=(b=a.buf,c=a.offset,((c<0||c>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+c]));a.offset=a.offset+(1)>>0;return!((d===0));};N.prototype.bool=function(){return this.$val.bool();};O.ptr.prototype.bool=function(a){var a,b,c,d,e,f;b=this;if(a){(c=b.buf,d=b.offset,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]=1));}else{(e=b.buf,f=b.offset,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]=0));}b.offset=b.offset+(1)>>0;};O.prototype.bool=function(a){return this.$val.bool(a);};N.ptr.prototype.uint8=function(){var a,b,c,d;a=this;d=(b=a.buf,c=a.offset,((c<0||c>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+c]));a.offset=a.offset+(1)>>0;return d;};N.prototype.uint8=function(){return this.$val.uint8();};O.ptr.prototype.uint8=function(a){var a,b,c,d;b=this;(c=b.buf,d=b.offset,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]=a));b.offset=b.offset+(1)>>0;};O.prototype.uint8=function(a){return this.$val.uint8(a);};N.ptr.prototype.uint16=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.order.Uint16($subslice(a.buf,a.offset,(a.offset+2>>0)));$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;a.offset=a.offset+(2)>>0;$s=-1;return c;}return;}if($f===undefined){$f={$blk:N.ptr.prototype.uint16};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.uint16=function(){return this.$val.uint16();};O.ptr.prototype.uint16=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;$r=b.order.PutUint16($subslice(b.buf,b.offset,(b.offset+2>>0)),a);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}b.offset=b.offset+(2)>>0;$s=-1;return;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.uint16};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.uint16=function(a){return this.$val.uint16(a);};N.ptr.prototype.uint32=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.order.Uint32($subslice(a.buf,a.offset,(a.offset+4>>0)));$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;a.offset=a.offset+(4)>>0;$s=-1;return c;}return;}if($f===undefined){$f={$blk:N.ptr.prototype.uint32};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.uint32=function(){return this.$val.uint32();};O.ptr.prototype.uint32=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;$r=b.order.PutUint32($subslice(b.buf,b.offset,(b.offset+4>>0)),a);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}b.offset=b.offset+(4)>>0;$s=-1;return;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.uint32};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.uint32=function(a){return this.$val.uint32(a);};N.ptr.prototype.uint64=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.order.Uint64($subslice(a.buf,a.offset,(a.offset+8>>0)));$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;a.offset=a.offset+(8)>>0;$s=-1;return c;}return;}if($f===undefined){$f={$blk:N.ptr.prototype.uint64};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.uint64=function(){return this.$val.uint64();};O.ptr.prototype.uint64=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;$r=b.order.PutUint64($subslice(b.buf,b.offset,(b.offset+8>>0)),a);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}b.offset=b.offset+(8)>>0;$s=-1;return;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.uint64};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.uint64=function(a){return this.$val.uint64(a);};N.ptr.prototype.int8=function(){var a;a=this;return((a.uint8()<<24>>24));};N.prototype.int8=function(){return this.$val.int8();};O.ptr.prototype.int8=function(a){var a,b;b=this;b.uint8(((a<<24>>>24)));};O.prototype.int8=function(a){return this.$val.int8(a);};N.ptr.prototype.int16=function(){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.uint16();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return((b<<16>>16));}return;}if($f===undefined){$f={$blk:N.ptr.prototype.int16};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.int16=function(){return this.$val.int16();};O.ptr.prototype.int16=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;$r=b.uint16(((a<<16>>>16)));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.int16};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.int16=function(a){return this.$val.int16(a);};N.ptr.prototype.int32=function(){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.uint32();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return((b>>0));}return;}if($f===undefined){$f={$blk:N.ptr.prototype.int32};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.int32=function(){return this.$val.int32();};O.ptr.prototype.int32=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;$r=b.uint32(((a>>>0)));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.int32};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.int32=function(a){return this.$val.int32(a);};N.ptr.prototype.int64=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;c=a.uint64();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return((b=c,new $Int64(b.$high,b.$low)));}return;}if($f===undefined){$f={$blk:N.ptr.prototype.int64};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.int64=function(){return this.$val.int64();};O.ptr.prototype.int64=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;$r=b.uint64((new $Uint64(a.$high,a.$low)));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.int64};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.int64=function(a){return this.$val.int64(a);};N.ptr.prototype.value=function(a){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=$clone(a,D.Value).Kind();if(c===(17)){$s=2;continue;}if(c===(25)){$s=3;continue;}if(c===(23)){$s=4;continue;}if(c===(1)){$s=5;continue;}if(c===(3)){$s=6;continue;}if(c===(4)){$s=7;continue;}if(c===(5)){$s=8;continue;}if(c===(6)){$s=9;continue;}if(c===(8)){$s=10;continue;}if(c===(9)){$s=11;continue;}if(c===(10)){$s=12;continue;}if(c===(11)){$s=13;continue;}if(c===(13)){$s=14;continue;}if(c===(14)){$s=15;continue;}if(c===(15)){$s=16;continue;}if(c===(16)){$s=17;continue;}$s=18;continue;case 2:d=$clone(a,D.Value).Len();e=0;case 19:if(!(e>0;$s=19;continue;case 20:$s=18;continue;case 3:g=$clone(a,D.Value).Type();h=$clone(a,D.Value).NumField();i=0;case 23:if(!(i>0;$s=23;continue;case 24:$s=18;continue;case 4:n=$clone(a,D.Value).Len();o=0;case 33:if(!(o>0;$s=33;continue;case 34:$s=18;continue;case 5:$clone(a,D.Value).SetBool(b.bool());$s=18;continue;case 6:$clone(a,D.Value).SetInt((new $Int64(0,b.int8())));$s=18;continue;case 7:q=b.int16();$s=37;case 37:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}$r=$clone(a,D.Value).SetInt((new $Int64(0,q)));$s=38;case 38:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 8:r=b.int32();$s=39;case 39:if($c){$c=false;r=r.$blk();}if(r&&r.$blk!==undefined){break s;}$r=$clone(a,D.Value).SetInt((new $Int64(0,r)));$s=40;case 40:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 9:s=b.int64();$s=41;case 41:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}$r=$clone(a,D.Value).SetInt(s);$s=42;case 42:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 10:$clone(a,D.Value).SetUint((new $Uint64(0,b.uint8())));$s=18;continue;case 11:t=b.uint16();$s=43;case 43:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}$r=$clone(a,D.Value).SetUint((new $Uint64(0,t)));$s=44;case 44:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 12:u=b.uint32();$s=45;case 45:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}$r=$clone(a,D.Value).SetUint((new $Uint64(0,u)));$s=46;case 46:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 13:v=b.uint64();$s=47;case 47:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}$r=$clone(a,D.Value).SetUint(v);$s=48;case 48:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 14:w=b.uint32();$s=49;case 49:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}x=C.Float32frombits(w);$s=50;case 50:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}$r=$clone(a,D.Value).SetFloat((x));$s=51;case 51:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 15:y=b.uint64();$s=52;case 52:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}z=C.Float64frombits(y);$s=53;case 53:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}$r=$clone(a,D.Value).SetFloat(z);$s=54;case 54:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 16:aa=b.uint32();$s=55;case 55:if($c){$c=false;aa=aa.$blk();}if(aa&&aa.$blk!==undefined){break s;}ab=C.Float32frombits(aa);$s=56;case 56:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}ac=(ab);ad=b.uint32();$s=57;case 57:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}ae=C.Float32frombits(ad);$s=58;case 58:if($c){$c=false;ae=ae.$blk();}if(ae&&ae.$blk!==undefined){break s;}af=(ae);$r=$clone(a,D.Value).SetComplex(new $Complex128(ac,af));$s=59;case 59:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=18;continue;case 17:ag=b.uint64();$s=60;case 60:if($c){$c=false;ag=ag.$blk();}if(ag&&ag.$blk!==undefined){break s;}ah=C.Float64frombits(ag);$s=61;case 61:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}ai=ah;aj=b.uint64();$s=62;case 62:if($c){$c=false;aj=aj.$blk();}if(aj&&aj.$blk!==undefined){break s;}ak=C.Float64frombits(aj);$s=63;case 63:if($c){$c=false;ak=ak.$blk();}if(ak&&ak.$blk!==undefined){break s;}al=ak;$r=$clone(a,D.Value).SetComplex(new $Complex128(ai,al));$s=64;case 64:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 18:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:N.ptr.prototype.value};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.value=function(a){return this.$val.value(a);};O.ptr.prototype.value=function(a){var a,aa,ab,ac,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=$clone(a,D.Value).Kind();if(c===(17)){$s=2;continue;}if(c===(25)){$s=3;continue;}if(c===(23)){$s=4;continue;}if(c===(1)){$s=5;continue;}if((c===(2))||(c===(3))||(c===(4))||(c===(5))||(c===(6))){$s=6;continue;}if((c===(7))||(c===(8))||(c===(9))||(c===(10))||(c===(11))||(c===(12))){$s=7;continue;}if((c===(13))||(c===(14))){$s=8;continue;}if((c===(15))||(c===(16))){$s=9;continue;}$s=10;continue;case 2:d=$clone(a,D.Value).Len();e=0;case 11:if(!(e>0;$s=11;continue;case 12:$s=10;continue;case 3:g=$clone(a,D.Value).Type();h=$clone(a,D.Value).NumField();i=0;case 15:if(!(i>0;$s=15;continue;case 16:$s=10;continue;case 4:n=$clone(a,D.Value).Len();o=0;case 25:if(!(o>0;$s=25;continue;case 26:$s=10;continue;case 5:b.bool($clone(a,D.Value).Bool());$s=10;continue;case 6:q=$clone(a,D.Value).Type().Kind();$s=30;case 30:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}r=q;if(r===(3)){$s=31;continue;}if(r===(4)){$s=32;continue;}if(r===(5)){$s=33;continue;}if(r===(6)){$s=34;continue;}$s=35;continue;case 31:b.int8((((s=$clone(a,D.Value).Int(),s.$low+((s.$high>>31)*4294967296))<<24>>24)));$s=35;continue;case 32:$r=b.int16((((t=$clone(a,D.Value).Int(),t.$low+((t.$high>>31)*4294967296))<<16>>16)));$s=36;case 36:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=35;continue;case 33:$r=b.int32((((u=$clone(a,D.Value).Int(),u.$low+((u.$high>>31)*4294967296))>>0)));$s=37;case 37:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=35;continue;case 34:$r=b.int64($clone(a,D.Value).Int());$s=38;case 38:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 35:case 29:$s=10;continue;case 7:v=$clone(a,D.Value).Type().Kind();$s=40;case 40:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}w=v;if(w===(8)){$s=41;continue;}if(w===(9)){$s=42;continue;}if(w===(10)){$s=43;continue;}if(w===(11)){$s=44;continue;}$s=45;continue;case 41:b.uint8((($clone(a,D.Value).Uint().$low<<24>>>24)));$s=45;continue;case 42:$r=b.uint16((($clone(a,D.Value).Uint().$low<<16>>>16)));$s=46;case 46:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=45;continue;case 43:$r=b.uint32((($clone(a,D.Value).Uint().$low>>>0)));$s=47;case 47:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=45;continue;case 44:$r=b.uint64($clone(a,D.Value).Uint());$s=48;case 48:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 45:case 39:$s=10;continue;case 8:x=$clone(a,D.Value).Type().Kind();$s=50;case 50:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}y=x;if(y===(13)){$s=51;continue;}if(y===(14)){$s=52;continue;}$s=53;continue;case 51:$r=b.uint32(C.Float32bits(($fround($clone(a,D.Value).Float()))));$s=54;case 54:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=53;continue;case 52:$r=b.uint64(C.Float64bits($clone(a,D.Value).Float()));$s=55;case 55:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 53:case 49:$s=10;continue;case 9:z=$clone(a,D.Value).Type().Kind();$s=57;case 57:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}aa=z;if(aa===(15)){$s=58;continue;}if(aa===(16)){$s=59;continue;}$s=60;continue;case 58:ab=$clone(a,D.Value).Complex();$r=b.uint32(C.Float32bits(($fround(ab.$real))));$s=61;case 61:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=b.uint32(C.Float32bits(($fround(ab.$imag))));$s=62;case 62:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=60;continue;case 59:ac=$clone(a,D.Value).Complex();$r=b.uint64(C.Float64bits(ac.$real));$s=63;case 63:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=b.uint64(C.Float64bits(ac.$imag));$s=64;case 64:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 60:case 56:case 10:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.value};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.value=function(a){return this.$val.value(a);};N.ptr.prototype.skip=function(a){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=K($clone(a,D.Value));$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}b.offset=b.offset+(c)>>0;$s=-1;return;}return;}if($f===undefined){$f={$blk:N.ptr.prototype.skip};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.skip=function(a){return this.$val.skip(a);};O.ptr.prototype.skip=function(a){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=K($clone(a,D.Value));$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;e=$subslice(b.buf,b.offset,(b.offset+d>>0));f=e;g=0;while(true){if(!(g=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+h]=0);g++;}b.offset=b.offset+(d)>>0;$s=-1;return;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.skip};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.skip=function(a){return this.$val.skip(a);};P=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;b=a;if($assertType(b,$Bool,true)[1]||$assertType(b,$Int8,true)[1]||$assertType(b,$Uint8,true)[1]||$assertType(b,Y,true)[1]||$assertType(b,Z,true)[1]||$assertType(b,AA,true)[1]){c=b;return 1;}else if($assertType(b,AH,true)[1]){d=b.$val;return d.$length;}else if($assertType(b,AI,true)[1]){e=b.$val;return e.$length;}else if($assertType(b,X,true)[1]){f=b.$val;return f.$length;}else if($assertType(b,$Int16,true)[1]||$assertType(b,$Uint16,true)[1]||$assertType(b,AB,true)[1]||$assertType(b,AC,true)[1]){g=b;return 2;}else if($assertType(b,AJ,true)[1]){h=b.$val;return $imul(2,h.$length);}else if($assertType(b,AK,true)[1]){i=b.$val;return $imul(2,i.$length);}else if($assertType(b,$Int32,true)[1]||$assertType(b,$Uint32,true)[1]||$assertType(b,AD,true)[1]||$assertType(b,AE,true)[1]){j=b;return 4;}else if($assertType(b,AL,true)[1]){k=b.$val;return $imul(4,k.$length);}else if($assertType(b,AM,true)[1]){l=b.$val;return $imul(4,l.$length);}else if($assertType(b,$Int64,true)[1]||$assertType(b,$Uint64,true)[1]||$assertType(b,AF,true)[1]||$assertType(b,AG,true)[1]){m=b;return 8;}else if($assertType(b,AN,true)[1]){n=b.$val;return $imul(8,n.$length);}else if($assertType(b,AO,true)[1]){o=b.$val;return $imul(8,o.$length);}return 0;};F.methods=[{prop:"Uint16",name:"Uint16",pkg:"",typ:$funcType([X],[$Uint16],false)},{prop:"PutUint16",name:"PutUint16",pkg:"",typ:$funcType([X,$Uint16],[],false)},{prop:"Uint32",name:"Uint32",pkg:"",typ:$funcType([X],[$Uint32],false)},{prop:"PutUint32",name:"PutUint32",pkg:"",typ:$funcType([X,$Uint32],[],false)},{prop:"Uint64",name:"Uint64",pkg:"",typ:$funcType([X],[$Uint64],false)},{prop:"PutUint64",name:"PutUint64",pkg:"",typ:$funcType([X,$Uint64],[],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"GoString",name:"GoString",pkg:"",typ:$funcType([],[$String],false)}];G.methods=[{prop:"Uint16",name:"Uint16",pkg:"",typ:$funcType([X],[$Uint16],false)},{prop:"PutUint16",name:"PutUint16",pkg:"",typ:$funcType([X,$Uint16],[],false)},{prop:"Uint32",name:"Uint32",pkg:"",typ:$funcType([X],[$Uint32],false)},{prop:"PutUint32",name:"PutUint32",pkg:"",typ:$funcType([X,$Uint32],[],false)},{prop:"Uint64",name:"Uint64",pkg:"",typ:$funcType([X],[$Uint64],false)},{prop:"PutUint64",name:"PutUint64",pkg:"",typ:$funcType([X,$Uint64],[],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"GoString",name:"GoString",pkg:"",typ:$funcType([],[$String],false)}];AP.methods=[{prop:"bool",name:"bool",pkg:"encoding/binary",typ:$funcType([],[$Bool],false)},{prop:"uint8",name:"uint8",pkg:"encoding/binary",typ:$funcType([],[$Uint8],false)},{prop:"uint16",name:"uint16",pkg:"encoding/binary",typ:$funcType([],[$Uint16],false)},{prop:"uint32",name:"uint32",pkg:"encoding/binary",typ:$funcType([],[$Uint32],false)},{prop:"uint64",name:"uint64",pkg:"encoding/binary",typ:$funcType([],[$Uint64],false)},{prop:"int8",name:"int8",pkg:"encoding/binary",typ:$funcType([],[$Int8],false)},{prop:"int16",name:"int16",pkg:"encoding/binary",typ:$funcType([],[$Int16],false)},{prop:"int32",name:"int32",pkg:"encoding/binary",typ:$funcType([],[$Int32],false)},{prop:"int64",name:"int64",pkg:"encoding/binary",typ:$funcType([],[$Int64],false)},{prop:"value",name:"value",pkg:"encoding/binary",typ:$funcType([D.Value],[],false)},{prop:"skip",name:"skip",pkg:"encoding/binary",typ:$funcType([D.Value],[],false)}];AQ.methods=[{prop:"bool",name:"bool",pkg:"encoding/binary",typ:$funcType([$Bool],[],false)},{prop:"uint8",name:"uint8",pkg:"encoding/binary",typ:$funcType([$Uint8],[],false)},{prop:"uint16",name:"uint16",pkg:"encoding/binary",typ:$funcType([$Uint16],[],false)},{prop:"uint32",name:"uint32",pkg:"encoding/binary",typ:$funcType([$Uint32],[],false)},{prop:"uint64",name:"uint64",pkg:"encoding/binary",typ:$funcType([$Uint64],[],false)},{prop:"int8",name:"int8",pkg:"encoding/binary",typ:$funcType([$Int8],[],false)},{prop:"int16",name:"int16",pkg:"encoding/binary",typ:$funcType([$Int16],[],false)},{prop:"int32",name:"int32",pkg:"encoding/binary",typ:$funcType([$Int32],[],false)},{prop:"int64",name:"int64",pkg:"encoding/binary",typ:$funcType([$Int64],[],false)},{prop:"value",name:"value",pkg:"encoding/binary",typ:$funcType([D.Value],[],false)},{prop:"skip",name:"skip",pkg:"encoding/binary",typ:$funcType([D.Value],[],false)}];E.init([{prop:"PutUint16",name:"PutUint16",pkg:"",typ:$funcType([X,$Uint16],[],false)},{prop:"PutUint32",name:"PutUint32",pkg:"",typ:$funcType([X,$Uint32],[],false)},{prop:"PutUint64",name:"PutUint64",pkg:"",typ:$funcType([X,$Uint64],[],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"Uint16",name:"Uint16",pkg:"",typ:$funcType([X],[$Uint16],false)},{prop:"Uint32",name:"Uint32",pkg:"",typ:$funcType([X],[$Uint32],false)},{prop:"Uint64",name:"Uint64",pkg:"",typ:$funcType([X],[$Uint64],false)}]);F.init("",[]);G.init("",[]);N.init("encoding/binary",[{prop:"order",name:"order",embedded:false,exported:false,typ:E,tag:""},{prop:"buf",name:"buf",embedded:false,exported:false,typ:X,tag:""},{prop:"offset",name:"offset",embedded:false,exported:false,typ:$Int,tag:""}]);O.init("encoding/binary",[{prop:"order",name:"order",embedded:false,exported:false,typ:E,tag:""},{prop:"buf",name:"buf",embedded:false,exported:false,typ:X,tag:""},{prop:"offset",name:"offset",embedded:false,exported:false,typ:$Int,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$pkg.LittleEndian=new F.ptr();$pkg.BigEndian=new G.ptr();U=A.New("binary: varint overflows a 64-bit integer");}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["strings"]=(function(){var $pkg={},$init,C,B,F,D,E,G,A,L,BT,CG,CH,CI,CS,CV,H,I,J,K,AH,AK,AM,AN,AO,AQ,AR,AS,AU,AZ,BA,BB,BC,BF,BU,CA;C=$packages["errors"];B=$packages["github.com/gopherjs/gopherjs/js"];F=$packages["internal/bytealg"];D=$packages["io"];E=$packages["sync"];G=$packages["unicode"];A=$packages["unicode/utf8"];L=$pkg.Builder=$newType(0,$kindStruct,"strings.Builder",true,"strings",true,function(addr_,buf_){this.$val=this;if(arguments.length===0){this.addr=CG.nil;this.buf=CH.nil;return;}this.addr=addr_;this.buf=buf_;});BT=$pkg.asciiSet=$newType(32,$kindArray,"strings.asciiSet",true,"strings",false,null);CG=$ptrType(L);CH=$sliceType($Uint8);CI=$sliceType($String);CS=$ptrType(BT);CV=$arrayType($Uint32,8);H=function(e,f){var e,f;return $parseInt(e.indexOf($global.String.fromCharCode(f)))>>0;};$pkg.IndexByte=H;I=function(e,f){var e,f;return $parseInt(e.indexOf(f))>>0;};$pkg.Index=I;J=function(e,f){var e,f;return $parseInt(e.lastIndexOf(f))>>0;};$pkg.LastIndex=J;K=function(e,f){var e,f,g,h;g=0;if((f.length===0)){return A.RuneCountInString(e)+1>>0;}else if(f.length>e.length){return 0;}else if((f.length===e.length)){if(f===e){return 1;}return 0;}while(true){h=I(e,f);if(h===-1){break;}g=g+(1)>>0;e=$substring(e,(h+f.length>>0));}return g;};$pkg.Count=K;L.ptr.prototype.String=function(){var e;e=this;return($bytesToString(e.buf));};L.prototype.String=function(){return this.$val.String();};L.ptr.prototype.copyCheck=function(){var e;e=this;if(e.addr===CG.nil){e.addr=e;}else if(!(e.addr===e)){$panic(new $String("strings: illegal use of non-zero Builder copied by value"));}};L.prototype.copyCheck=function(){return this.$val.copyCheck();};L.ptr.prototype.Len=function(){var e;e=this;return e.buf.$length;};L.prototype.Len=function(){return this.$val.Len();};L.ptr.prototype.Cap=function(){var e;e=this;return e.buf.$capacity;};L.prototype.Cap=function(){return this.$val.Cap();};L.ptr.prototype.Reset=function(){var e;e=this;e.addr=CG.nil;e.buf=CH.nil;};L.prototype.Reset=function(){return this.$val.Reset();};L.ptr.prototype.grow=function(e){var e,f,g;f=this;g=$makeSlice(CH,f.buf.$length,(($imul(2,f.buf.$capacity))+e>>0));$copySlice(g,f.buf);f.buf=g;};L.prototype.grow=function(e){return this.$val.grow(e);};L.ptr.prototype.Grow=function(e){var e,f;f=this;f.copyCheck();if(e<0){$panic(new $String("strings.Builder.Grow: negative count"));}if((f.buf.$capacity-f.buf.$length>>0)>>24)));return[1,$ifaceNil];}g=f.buf.$length;if((f.buf.$capacity-g>>0)<4){f.grow(4);}h=A.EncodeRune($subslice(f.buf,g,(g+4>>0)),e);f.buf=$subslice(f.buf,0,(g+h>>0));return[h,$ifaceNil];};L.prototype.WriteRune=function(e){return this.$val.WriteRune(e);};L.ptr.prototype.WriteString=function(e){var e,f;f=this;f.copyCheck();f.buf=$appendSlice(f.buf,e);return[e.length,$ifaceNil];};L.prototype.WriteString=function(e){return this.$val.WriteString(e);};AH=function(e,f){var e,f,g,h,i,j,k,l,m;g=A.RuneCountInString(e);if(f<0||f>g){f=g;}h=$makeSlice(CI,f);i=0;while(true){if(!(i<(f-1>>0))){break;}j=A.DecodeRuneInString(e);k=j[0];l=j[1];((i<0||i>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]=$substring(e,0,l));e=$substring(e,l);if(k===65533){((i<0||i>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]="\xEF\xBF\xBD");}i=i+(1)>>0;}if(f>0){(m=f-1>>0,((m<0||m>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+m]=e));}return h;};AK=function(e,f){var e,f;return I(e,f)>=0;};$pkg.Contains=AK;AM=function(e,f){var e,f;return AN(e,f)>=0;};$pkg.ContainsRune=AM;AN=function(e,f){var e,f,g,h,i,j,k;if(0<=f&&f<128){return H(e,((f<<24>>>24)));}else if((f===65533)){g=e;h=0;while(true){if(!(h8){g=BU(f);h=$clone(g[0],BT);i=g[1];if(i){j=0;while(true){if(!(j>0;}return-1;}}k=e;l=0;while(true){if(!(l>0;while(true){if(!(g>=0)){break;}if(e.charCodeAt(g)===f){return g;}g=g-(1)>>0;}return-1;};$pkg.LastIndexByte=AQ;AR=function(e,f,g,h){var e,f,g,h,i,j,k;if(h===0){return CI.nil;}if(f===""){return AH(e,h);}if(h<0){h=K(e,f)+1>>0;}i=$makeSlice(CI,h);h=h-(1)>>0;j=0;while(true){if(!(j=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]=$substring(e,0,(k+g>>0)));e=$substring(e,(k+f.length>>0));j=j+(1)>>0;}((j<0||j>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]=e);return $subslice(i,0,(j+1>>0));};AS=function(e,f,g){var e,f,g;return AR(e,f,0,g);};$pkg.SplitN=AS;AU=function(e,f){var e,f;return AR(e,f,0,-1);};$pkg.Split=AU;AZ=function(e,f){var e,f,g,h,i,j,k,l,m;g=e.$length;if(g===(0)){return"";}else if(g===(1)){return(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]);}h=$imul(f.length,((e.$length-1>>0)));i=0;while(true){if(!(i=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+i]).length)>>0;i=i+(1)>>0;}j=new L.ptr(CG.nil,CH.nil);j.Grow(h);j.WriteString((0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]));k=$subslice(e,1);l=0;while(true){if(!(l=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+l]);j.WriteString(f);j.WriteString(m);l++;}return j.String();};$pkg.Join=AZ;BA=function(e,f){var e,f;return e.length>=f.length&&$substring(e,0,f.length)===f;};$pkg.HasPrefix=BA;BB=function(e,f){var e,f;return e.length>=f.length&&$substring(e,(e.length-f.length>>0))===f;};$pkg.HasSuffix=BB;BC=function(e,f){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:g=new L.ptr(CG.nil,CH.nil);h=f;i=0;case 1:if(!(i>0);g.WriteString($substring(f,0,k));if(n>=0){g.WriteRune(n);}f=$substring(f,(k+o>>0));$s=2;continue;$s=1;continue;case 2:if(g.Cap()===0){$s=-1;return f;}q=f;r=0;case 4:if(!(r=0){if(v<128){g.WriteByte(((v<<24>>>24)));}else{g.WriteRune(v);}}r+=s[1];$s=4;continue;case 5:$s=-1;return g.String();}return;}if($f===undefined){$f={$blk:BC};}$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Map=BC;BF=function(e){var e,f,g,h,i,j,k,l,m,n,o,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:f=true;g=false;h=f;i=g;j=0;while(true){if(!(j=128){h=false;break;}i=i||(k>=65&&k<=90);j=j+(1)>>0;}if(h){if(!i){$s=-1;return e;}l=new L.ptr(CG.nil,CH.nil);l.Grow(e.length);m=0;while(true){if(!(m=65&&n<=90){n=n+(32)<<24>>>24;}l.WriteByte(n);m=m+(1)>>0;}$s=-1;return l.String();}o=BC(G.ToLower,e);$s=1;case 1:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}$s=-1;return o;}return;}if($f===undefined){$f={$blk:BF};}$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.$s=$s;$f.$r=$r;return $f;};$pkg.ToLower=BF;BU=function(e){var e,f,g,h,i,j,k,l,m,n,o;f=CV.zero();g=false;h=0;while(true){if(!(h=128){j=$clone(f,BT);k=false;BT.copy(f,j);g=k;return[f,g];}l=i>>>5<<24>>>24;((l<0||l>=f.length)?($throwRuntimeError("index out of range"),undefined):f[l]=((((l<0||l>=f.length)?($throwRuntimeError("index out of range"),undefined):f[l])|(((m=((((i&31)>>>0)>>>0)),m<32?(1<>>0)))>>>0));h=h+(1)>>0;}n=$clone(f,BT);o=true;BT.copy(f,n);g=o;return[f,g];};BT.prototype.contains=function(e){var e,f,g,h;f=this.$val;return!((((((g=e>>>5<<24>>>24,(f.nilCheck,((g<0||g>=f.length)?($throwRuntimeError("index out of range"),undefined):f[g])))&(((h=((((e&31)>>>0)>>>0)),h<32?(1<>>0)))>>>0))===0));};$ptrType(BT).prototype.contains=function(e){return(new BT(this.$get())).contains(e);};CA=function(e,f){var e,f;if(BA(e,f)){return $substring(e,f.length);}return e;};$pkg.TrimPrefix=CA;CG.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"copyCheck",name:"copyCheck",pkg:"strings",typ:$funcType([],[],false)},{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Cap",name:"Cap",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Reset",name:"Reset",pkg:"",typ:$funcType([],[],false)},{prop:"grow",name:"grow",pkg:"strings",typ:$funcType([$Int],[],false)},{prop:"Grow",name:"Grow",pkg:"",typ:$funcType([$Int],[],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([CH],[$Int,$error],false)},{prop:"WriteByte",name:"WriteByte",pkg:"",typ:$funcType([$Uint8],[$error],false)},{prop:"WriteRune",name:"WriteRune",pkg:"",typ:$funcType([$Int32],[$Int,$error],false)},{prop:"WriteString",name:"WriteString",pkg:"",typ:$funcType([$String],[$Int,$error],false)}];CS.methods=[{prop:"contains",name:"contains",pkg:"strings",typ:$funcType([$Uint8],[$Bool],false)}];L.init("strings",[{prop:"addr",name:"addr",embedded:false,exported:false,typ:CG,tag:""},{prop:"buf",name:"buf",embedded:false,exported:false,typ:CH,tag:""}]);BT.init($Uint32,8);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=C.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=G.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["regexp/syntax"]=(function(){var $pkg={},$init,C,B,D,A,E,F,G,H,M,N,O,P,Z,AM,BK,BL,BN,BQ,BV,BW,CA,CB,CC,CD,CE,CF,CG,CH,CI,CJ,CK,CL,CM,CN,CO,CP,CQ,CR,J,K,L,AA,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BM,I,Q,R,S,T,U,V,W,X,Y,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AN,AO,AP,AQ,BP,BR,BS,BT,BU,BX,BY,BZ;C=$packages["sort"];B=$packages["strconv"];D=$packages["strings"];A=$packages["unicode"];E=$packages["unicode/utf8"];F=$pkg.patchList=$newType(4,$kindUint32,"syntax.patchList",true,"regexp/syntax",false,null);G=$pkg.frag=$newType(0,$kindStruct,"syntax.frag",true,"regexp/syntax",false,function(i_,out_){this.$val=this;if(arguments.length===0){this.i=0;this.out=0;return;}this.i=i_;this.out=out_;});H=$pkg.compiler=$newType(0,$kindStruct,"syntax.compiler",true,"regexp/syntax",false,function(p_){this.$val=this;if(arguments.length===0){this.p=CE.nil;return;}this.p=p_;});M=$pkg.Error=$newType(0,$kindStruct,"syntax.Error",true,"regexp/syntax",true,function(Code_,Expr_){this.$val=this;if(arguments.length===0){this.Code="";this.Expr="";return;}this.Code=Code_;this.Expr=Expr_;});N=$pkg.ErrorCode=$newType(8,$kindString,"syntax.ErrorCode",true,"regexp/syntax",true,null);O=$pkg.Flags=$newType(2,$kindUint16,"syntax.Flags",true,"regexp/syntax",true,null);P=$pkg.parser=$newType(0,$kindStruct,"syntax.parser",true,"regexp/syntax",false,function(flags_,stack_,free_,numCap_,wholeRegexp_,tmpClass_){this.$val=this;if(arguments.length===0){this.flags=0;this.stack=CH.nil;this.free=CG.nil;this.numCap=0;this.wholeRegexp="";this.tmpClass=CA.nil;return;}this.flags=flags_;this.stack=stack_;this.free=free_;this.numCap=numCap_;this.wholeRegexp=wholeRegexp_;this.tmpClass=tmpClass_;});Z=$pkg.charGroup=$newType(0,$kindStruct,"syntax.charGroup",true,"regexp/syntax",false,function(sign_,class$1_){this.$val=this;if(arguments.length===0){this.sign=0;this.class$1=CA.nil;return;}this.sign=sign_;this.class$1=class$1_;});AM=$pkg.ranges=$newType(0,$kindStruct,"syntax.ranges",true,"regexp/syntax",false,function(p_){this.$val=this;if(arguments.length===0){this.p=CK.nil;return;}this.p=p_;});BK=$pkg.Prog=$newType(0,$kindStruct,"syntax.Prog",true,"regexp/syntax",true,function(Inst_,Start_,NumCap_){this.$val=this;if(arguments.length===0){this.Inst=CF.nil;this.Start=0;this.NumCap=0;return;}this.Inst=Inst_;this.Start=Start_;this.NumCap=NumCap_;});BL=$pkg.InstOp=$newType(1,$kindUint8,"syntax.InstOp",true,"regexp/syntax",true,null);BN=$pkg.EmptyOp=$newType(1,$kindUint8,"syntax.EmptyOp",true,"regexp/syntax",true,null);BQ=$pkg.Inst=$newType(0,$kindStruct,"syntax.Inst",true,"regexp/syntax",true,function(Op_,Out_,Arg_,Rune_){this.$val=this;if(arguments.length===0){this.Op=0;this.Out=0;this.Arg=0;this.Rune=CA.nil;return;}this.Op=Op_;this.Out=Out_;this.Arg=Arg_;this.Rune=Rune_;});BV=$pkg.Regexp=$newType(0,$kindStruct,"syntax.Regexp",true,"regexp/syntax",true,function(Op_,Flags_,Sub_,Sub0_,Rune_,Rune0_,Min_,Max_,Cap_,Name_){this.$val=this;if(arguments.length===0){this.Op=0;this.Flags=0;this.Sub=CH.nil;this.Sub0=CI.zero();this.Rune=CA.nil;this.Rune0=CJ.zero();this.Min=0;this.Max=0;this.Cap=0;this.Name="";return;}this.Op=Op_;this.Flags=Flags_;this.Sub=Sub_;this.Sub0=Sub0_;this.Rune=Rune_;this.Rune0=Rune0_;this.Min=Min_;this.Max=Max_;this.Cap=Cap_;this.Name=Name_;});BW=$pkg.Op=$newType(1,$kindUint8,"syntax.Op",true,"regexp/syntax",true,null);CA=$sliceType($Int32);CB=$sliceType(A.Range16);CC=$sliceType(A.Range32);CD=$sliceType($String);CE=$ptrType(BK);CF=$sliceType(BQ);CG=$ptrType(BV);CH=$sliceType(CG);CI=$arrayType(CG,1);CJ=$arrayType($Int32,2);CK=$ptrType(CA);CL=$ptrType(A.RangeTable);CM=$ptrType(D.Builder);CN=$sliceType($Uint8);CO=$ptrType(H);CP=$ptrType(M);CQ=$ptrType(P);CR=$ptrType(BQ);F.prototype.next=function(a){var a,b,c,d,e;b=this.$val;e=(c=a.Inst,d=b>>>1>>>0,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]));if(((b&1)>>>0)===0){return((e.Out>>>0));}return((e.Arg>>>0));};$ptrType(F).prototype.next=function(a){return new F(this.$get()).next(a);};F.prototype.patch=function(a,b){var a,b,c,d,e,f;c=this.$val;while(true){if(!(!((c===0)))){break;}f=(d=a.Inst,e=c>>>1>>>0,((e<0||e>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]));if(((c&1)>>>0)===0){c=((f.Out>>>0));f.Out=b;}else{c=((f.Arg>>>0));f.Arg=b;}}};$ptrType(F).prototype.patch=function(a,b){return new F(this.$get()).patch(a,b);};F.prototype.append=function(a,b){var a,b,c,d,e,f,g,h;c=this.$val;if(c===0){return b;}if(b===0){return c;}d=c;while(true){e=new F(d).next(a);if(e===0){break;}d=e;}h=(f=a.Inst,g=d>>>1>>>0,((g<0||g>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+g]));if(((d&1)>>>0)===0){h.Out=((b>>>0));}else{h.Arg=((b>>>0));}return c;};$ptrType(F).prototype.append=function(a,b){return new F(this.$get()).append(a,b);};I=function(a){var a,b,c;b=new H.ptr(CE.nil);b.init();c=$clone(b.compile(a),G);new F(c.out).patch(b.p,b.inst(4).i);b.p.Start=((c.i>>0));return[b.p,$ifaceNil];};$pkg.Compile=I;H.ptr.prototype.init=function(){var a;a=this;a.p=new BK.ptr(CF.nil,0,0);a.p.NumCap=2;a.inst(5);};H.prototype.init=function(){return this.$val.init();};H.ptr.prototype.compile=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x;b=this;c=a.Op;if(c===(1)){return b.fail();}else if(c===(2)){return b.nop();}else if(c===(3)){if(a.Rune.$length===0){return b.nop();}d=new G.ptr(0,0);e=a.Rune;f=0;while(true){if(!(f>0)),a.Flags),G);if(g===0){G.copy(d,h);}else{G.copy(d,b.cat($clone(d,G),$clone(h,G)));}f++;}return d;}else if(c===(4)){return b.rune(a.Rune,a.Flags);}else if(c===(5)){return b.rune(J,0);}else if(c===(6)){return b.rune(K,0);}else if(c===(7)){return b.empty(1);}else if(c===(8)){return b.empty(2);}else if(c===(9)){return b.empty(4);}else if(c===(10)){return b.empty(8);}else if(c===(11)){return b.empty(16);}else if(c===(12)){return b.empty(32);}else if(c===(13)){i=$clone(b.cap((((a.Cap<<1>>0)>>>0))),G);k=$clone(b.compile((j=a.Sub,(0>=j.$length?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+0]))),G);l=$clone(b.cap(((((a.Cap<<1>>0)|1)>>>0))),G);return b.cat($clone(b.cat($clone(i,G),$clone(k,G)),G),$clone(l,G));}else if(c===(14)){return b.star($clone(b.compile((m=a.Sub,(0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0]))),G),!((((a.Flags&32)>>>0)===0)));}else if(c===(15)){return b.plus($clone(b.compile((n=a.Sub,(0>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+0]))),G),!((((a.Flags&32)>>>0)===0)));}else if(c===(16)){return b.quest($clone(b.compile((o=a.Sub,(0>=o.$length?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+0]))),G),!((((a.Flags&32)>>>0)===0)));}else if(c===(18)){if(a.Sub.$length===0){return b.nop();}p=new G.ptr(0,0);q=a.Sub;r=0;while(true){if(!(r=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+r]);if(s===0){G.copy(p,b.compile(t));}else{G.copy(p,b.cat($clone(p,G),$clone(b.compile(t),G)));}r++;}return p;}else if(c===(19)){u=new G.ptr(0,0);v=a.Sub;w=0;while(true){if(!(w=v.$length)?($throwRuntimeError("index out of range"),undefined):v.$array[v.$offset+w]);G.copy(u,b.alt($clone(u,G),$clone(b.compile(x),G)));w++;}return u;}$panic(new $String("regexp: unhandled case in compile"));};H.prototype.compile=function(a){return this.$val.compile(a);};H.ptr.prototype.inst=function(a){var a,b,c;b=this;c=new G.ptr(((b.p.Inst.$length>>>0)),0);b.p.Inst=$append(b.p.Inst,new BQ.ptr(a,0,0,CA.nil));return c;};H.prototype.inst=function(a){return this.$val.inst(a);};H.ptr.prototype.nop=function(){var a,b;a=this;b=$clone(a.inst(6),G);b.out=(((b.i<<1>>>0)>>>0));return b;};H.prototype.nop=function(){return this.$val.nop();};H.ptr.prototype.fail=function(){var a;a=this;return new G.ptr(0,0);};H.prototype.fail=function(){return this.$val.fail();};H.ptr.prototype.cap=function(a){var a,b,c,d,e;b=this;c=$clone(b.inst(2),G);c.out=(((c.i<<1>>>0)>>>0));(d=b.p.Inst,e=c.i,((e<0||e>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e])).Arg=a;if(b.p.NumCap<(((a>>0))+1>>0)){b.p.NumCap=((a>>0))+1>>0;}return c;};H.prototype.cap=function(a){return this.$val.cap(a);};H.ptr.prototype.cat=function(a,b){var a,b,c;c=this;if((a.i===0)||(b.i===0)){return new G.ptr(0,0);}new F(a.out).patch(c.p,b.i);return new G.ptr(a.i,b.out);};H.prototype.cat=function(a,b){return this.$val.cat(a,b);};H.ptr.prototype.alt=function(a,b){var a,b,c,d,e,f,g;c=this;if(a.i===0){return b;}if(b.i===0){return a;}d=$clone(c.inst(0),G);g=(e=c.p.Inst,f=d.i,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]));g.Out=a.i;g.Arg=b.i;d.out=new F(a.out).append(c.p,b.out);return d;};H.prototype.alt=function(a,b){return this.$val.alt(a,b);};H.ptr.prototype.quest=function(a,b){var a,b,c,d,e,f,g;c=this;d=$clone(c.inst(0),G);g=(e=c.p.Inst,f=d.i,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]));if(b){g.Arg=a.i;d.out=(((d.i<<1>>>0)>>>0));}else{g.Out=a.i;d.out=(((((d.i<<1>>>0)|1)>>>0)>>>0));}d.out=new F(d.out).append(c.p,a.out);return d;};H.prototype.quest=function(a,b){return this.$val.quest(a,b);};H.ptr.prototype.star=function(a,b){var a,b,c,d,e,f,g;c=this;d=$clone(c.inst(0),G);g=(e=c.p.Inst,f=d.i,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]));if(b){g.Arg=a.i;d.out=(((d.i<<1>>>0)>>>0));}else{g.Out=a.i;d.out=(((((d.i<<1>>>0)|1)>>>0)>>>0));}new F(a.out).patch(c.p,d.i);return d;};H.prototype.star=function(a,b){return this.$val.star(a,b);};H.ptr.prototype.plus=function(a,b){var a,b,c;c=this;return new G.ptr(a.i,c.star($clone(a,G),b).out);};H.prototype.plus=function(a,b){return this.$val.plus(a,b);};H.ptr.prototype.empty=function(a){var a,b,c,d,e;b=this;c=$clone(b.inst(3),G);(d=b.p.Inst,e=c.i,((e<0||e>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e])).Arg=((a>>>0));c.out=(((c.i<<1>>>0)>>>0));return c;};H.prototype.empty=function(a){return this.$val.empty(a);};H.ptr.prototype.rune=function(a,b){var a,b,c,d,e,f,g;c=this;d=$clone(c.inst(7),G);g=(e=c.p.Inst,f=d.i,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]));g.Rune=a;b=(b&(1))>>>0;if(!((a.$length===1))||(A.SimpleFold((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]))===(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]))){b=(b&~(1))<<16>>>16;}g.Arg=((b>>>0));d.out=(((d.i<<1>>>0)>>>0));if((((b&1)>>>0)===0)&&((a.$length===1)||(a.$length===2)&&((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0])===(1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1])))){g.Op=8;}else if((a.$length===2)&&((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0])===0)&&((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1])===1114111)){g.Op=9;}else if((a.$length===4)&&((0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0])===0)&&((1>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+1])===9)&&((2>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+2])===11)&&((3>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+3])===1114111)){g.Op=10;}return d;};H.prototype.rune=function(a,b){return this.$val.rune(a,b);};BW.prototype.String=function(){var a,b;a=this.$val;if(1<=a&&a<=19){a=a-(1)<<24>>>24;return $substring("NoMatchEmptyMatchLiteralCharClassAnyCharNotNLAnyCharBeginLineEndLineBeginTextEndTextWordBoundaryNoWordBoundaryCaptureStarPlusQuestRepeatConcatAlternate",((a<0||a>=L.length)?($throwRuntimeError("index out of range"),undefined):L[a]),(b=a+1<<24>>>24,((b<0||b>=L.length)?($throwRuntimeError("index out of range"),undefined):L[b])));}else if((a===128)){return"opPseudo";}else{return"Op("+B.FormatInt((new $Int64(0,a)),10)+")";}};$ptrType(BW).prototype.String=function(){return new BW(this.$get()).String();};M.ptr.prototype.Error=function(){var a;a=this;return"error parsing regexp: "+new N(a.Code).String()+": `"+a.Expr+"`";};M.prototype.Error=function(){return this.$val.Error();};N.prototype.String=function(){var a;a=this.$val;return(a);};$ptrType(N).prototype.String=function(){return new N(this.$get()).String();};P.ptr.prototype.newRegexp=function(a){var a,b,c;b=this;c=b.free;if(!(c===CG.nil)){b.free=c.Sub0[0];BV.copy(c,new BV.ptr(0,0,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,""));}else{c=new BV.ptr(0,0,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,"");}c.Op=a;return c;};P.prototype.newRegexp=function(a){return this.$val.newRegexp(a);};P.ptr.prototype.reuse=function(a){var a,b;b=this;a.Sub0[0]=b.free;b.free=a;};P.prototype.reuse=function(a){return this.$val.reuse(a);};P.ptr.prototype.push=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t;b=this;if((a.Op===4)&&(a.Rune.$length===2)&&((c=a.Rune,(0>=c.$length?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+0]))===(d=a.Rune,(1>=d.$length?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+1])))){if(b.maybeConcat((s=a.Rune,(0>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+0])),(b.flags&~1)<<16>>>16)){return CG.nil;}a.Op=3;a.Rune=$subslice(a.Rune,0,1);a.Flags=(b.flags&~1)<<16>>>16;}else if((a.Op===4)&&(a.Rune.$length===4)&&((e=a.Rune,(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]))===(f=a.Rune,(1>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+1])))&&((g=a.Rune,(2>=g.$length?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+2]))===(h=a.Rune,(3>=h.$length?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+3])))&&(A.SimpleFold((i=a.Rune,(0>=i.$length?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+0])))===(j=a.Rune,(2>=j.$length?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+2])))&&(A.SimpleFold((k=a.Rune,(2>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+2])))===(l=a.Rune,(0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])))||(a.Op===4)&&(a.Rune.$length===2)&&(((m=a.Rune,(0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0]))+1>>0)===(n=a.Rune,(1>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+1])))&&(A.SimpleFold((o=a.Rune,(0>=o.$length?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+0])))===(p=a.Rune,(1>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+1])))&&(A.SimpleFold((q=a.Rune,(1>=q.$length?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+1])))===(r=a.Rune,(0>=r.$length?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+0])))){if(b.maybeConcat((t=a.Rune,(0>=t.$length?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+0])),(b.flags|1)>>>0)){return CG.nil;}a.Op=3;a.Rune=$subslice(a.Rune,0,1);a.Flags=(b.flags|1)>>>0;}else{b.maybeConcat(-1,0);}b.stack=$append(b.stack,a);return a;};P.prototype.push=function(a){return this.$val.push(a);};P.ptr.prototype.maybeConcat=function(a,b){var a,b,c,d,e,f,g,h,i,j,k;c=this;d=c.stack.$length;if(d<2){return false;}g=(e=c.stack,f=d-1>>0,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]));j=(h=c.stack,i=d-2>>0,((i<0||i>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]));if(!((g.Op===3))||!((j.Op===3))||!((((g.Flags&1)>>>0)===((j.Flags&1)>>>0)))){return false;}j.Rune=$appendSlice(j.Rune,g.Rune);if(a>=0){g.Rune=$subslice(new CA(g.Rune0),0,1);(k=g.Rune,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]=a));g.Flags=b;return true;}c.stack=$subslice(c.stack,0,(d-1>>0));c.reuse(g);return false;};P.prototype.maybeConcat=function(a,b){return this.$val.maybeConcat(a,b);};P.ptr.prototype.newLiteral=function(a,b){var a,b,c,d;c=this;d=c.newRegexp(3);d.Flags=b;if(!((((b&1)>>>0)===0))){a=Q(a);}d.Rune0[0]=a;d.Rune=$subslice(new CA(d.Rune0),0,1);return d;};P.prototype.newLiteral=function(a,b){return this.$val.newLiteral(a,b);};Q=function(a){var a,b,c;if(a<65||a>125251){return a;}b=a;c=a;a=A.SimpleFold(a);while(true){if(!(!((a===c)))){break;}if(b>a){b=a;}a=A.SimpleFold(a);}return b;};P.ptr.prototype.literal=function(a){var a,b;b=this;b.push(b.newLiteral(a,b.flags));};P.prototype.literal=function(a){return this.$val.literal(a);};P.ptr.prototype.op=function(a){var a,b,c;b=this;c=b.newRegexp(a);c.Flags=b.flags;return b.push(c);};P.prototype.op=function(a){return this.$val.op(a);};P.ptr.prototype.repeat=function(a,b,c,d,e,f){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;g=this;h=g.flags;if(!((((g.flags&64)>>>0)===0))){if(e.length>0&&(e.charCodeAt(0)===63)){e=$substring(e,1);h=(h^(32))<<16>>>16;}if(!(f==="")){return["",new M.ptr("invalid nested repetition operator",$substring(f,0,(f.length-e.length>>0)))];}}i=g.stack.$length;if(i===0){return["",new M.ptr("missing argument to repetition operator",$substring(d,0,(d.length-e.length>>0)))];}l=(j=g.stack,k=i-1>>0,((k<0||k>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+k]));if(l.Op>=128){return["",new M.ptr("missing argument to repetition operator",$substring(d,0,(d.length-e.length>>0)))];}m=g.newRegexp(a);m.Min=b;m.Max=c;m.Flags=h;m.Sub=$subslice(new CH(m.Sub0),0,1);(n=m.Sub,(0>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+0]=l));(o=g.stack,p=i-1>>0,((p<0||p>=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]=m));if((a===17)&&(b>=2||c>=2)&&!R(m,1000)){return["",new M.ptr("invalid repeat count",$substring(d,0,(d.length-e.length>>0)))];}return[e,$ifaceNil];};P.prototype.repeat=function(a,b,c,d,e,f){return this.$val.repeat(a,b,c,d,e,f);};R=function(a,b){var a,b,c,d,e,f,g;if(a.Op===17){c=a.Max;if(c===0){return true;}if(c<0){c=a.Min;}if(c>b){return false;}if(c>0){b=(d=b/(c),(d===d&&d!==1/0&&d!==-1/0)?d>>0:$throwRuntimeError("integer divide by zero"));}}e=a.Sub;f=0;while(true){if(!(f=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]);if(!R(g,b)){return false;}f++;}return true;};P.ptr.prototype.concat=function(){var a,b,c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;a.maybeConcat(-1,0);b=a.stack.$length;while(true){if(!(b>0&&(c=a.stack,d=b-1>>0,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d])).Op<128)){break;}b=b-(1)>>0;}e=$subslice(a.stack,b);a.stack=$subslice(a.stack,0,b);if(e.$length===0){$s=-1;return a.push(a.newRegexp(2));}f=a.collapse(e,18);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=a.push(f);$s=2;case 2:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}$s=-1;return g;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.concat};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.concat=function(){return this.$val.concat();};P.ptr.prototype.alternate=function(){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.stack.$length;while(true){if(!(b>0&&(c=a.stack,d=b-1>>0,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d])).Op<128)){break;}b=b-(1)>>0;}e=$subslice(a.stack,b);a.stack=$subslice(a.stack,0,b);if(e.$length>0){$s=1;continue;}$s=2;continue;case 1:$r=S((f=e.$length-1>>0,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f])));$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 2:if(e.$length===0){$s=-1;return a.push(a.newRegexp(1));}g=a.collapse(e,19);$s=4;case 4:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=a.push(g);$s=5;case 5:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}$s=-1;return h;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.alternate};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.alternate=function(){return this.$val.alternate();};S=function(a){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=a.Op;if(b===(4)){$s=2;continue;}$s=3;continue;case 2:c=AC((a.$ptr_Rune||(a.$ptr_Rune=new CK(function(){return this.$target.Rune;},function($v){this.$target.Rune=$v;},a))));$s=4;case 4:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}a.Rune=c;if((a.Rune.$length===2)&&((d=a.Rune,(0>=d.$length?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+0]))===0)&&((e=a.Rune,(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1]))===1114111)){a.Rune=CA.nil;a.Op=6;$s=-1;return;}if((a.Rune.$length===4)&&((f=a.Rune,(0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0]))===0)&&((g=a.Rune,(1>=g.$length?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+1]))===9)&&((h=a.Rune,(2>=h.$length?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+2]))===11)&&((i=a.Rune,(3>=i.$length?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+3]))===1114111)){a.Rune=CA.nil;a.Op=5;$s=-1;return;}if((a.Rune.$capacity-a.Rune.$length>>0)>100){a.Rune=$appendSlice($subslice(new CA(a.Rune0),0,0),a.Rune);}case 3:case 1:$s=-1;return;}return;}if($f===undefined){$f={$blk:S};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};P.ptr.prototype.collapse=function(a,b){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(a.$length===1){$s=-1;return(0>=a.$length?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+0]);}d=c.newRegexp(b);d.Sub=$subslice(new CH(d.Sub0),0,0);e=a;f=0;while(true){if(!(f=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]);if(g.Op===b){d.Sub=$appendSlice(d.Sub,g.Sub);c.reuse(g);}else{d.Sub=$append(d.Sub,g);}f++;}if(b===19){$s=1;continue;}$s=2;continue;case 1:h=c.factor(d.Sub);$s=3;case 3:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}d.Sub=h;if(d.Sub.$length===1){i=d;d=(j=d.Sub,(0>=j.$length?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+0]));c.reuse(i);}case 2:$s=-1;return d;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.collapse};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.collapse=function(a,b){return this.$val.collapse(a,b);};P.ptr.prototype.factor=function(a){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;if(a.$length<2){$s=-1;return a;}c=CA.nil;d=0;e=0;f=$subslice(a,0,0);g=0;case 1:if(!(g<=a.$length)){$s=2;continue;}h=CA.nil;i=0;if(g=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+g]));h=j[0];i=j[1];if(i===d){k=0;while(true){if(!(k=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+k])===((k<0||k>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+k])))){break;}k=k+(1)>>0;}if(k>0){c=$subslice(c,0,k);g=g+(1)>>0;$s=1;continue;}}case 4:if(g===e){$s=5;continue;}if(g===(e+1>>0)){$s=6;continue;}$s=7;continue;case 5:$s=8;continue;case 6:f=$append(f,((e<0||e>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]));$s=8;continue;case 7:l=b.newRegexp(3);l.Flags=d;l.Rune=$appendSlice($subslice(l.Rune,0,0),c);m=e;while(true){if(!(m=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+m]=b.removeLeadingString(((m<0||m>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+m]),c.$length));m=m+(1)>>0;}n=b.collapse($subslice(a,e,g),19);$s=9;case 9:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}o=n;p=b.newRegexp(18);p.Sub=$append($subslice(p.Sub,0,0),l,o);f=$append(f,p);case 8:e=g;c=h;d=i;g=g+(1)>>0;$s=1;continue;case 2:a=f;e=0;f=$subslice(a,0,0);q=CG.nil;r=0;case 10:if(!(r<=a.$length)){$s=11;continue;}s=CG.nil;if(r=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+r]));if(!(q===CG.nil)&&q.Equal(s)&&(W(q)||((q.Op===17)&&(q.Min===q.Max)&&W((t=q.Sub,(0>=t.$length?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+0])))))){r=r+(1)>>0;$s=10;continue;}case 13:if(r===e){$s=14;continue;}if(r===(e+1>>0)){$s=15;continue;}$s=16;continue;case 14:$s=17;continue;case 15:f=$append(f,((e<0||e>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]));$s=17;continue;case 16:u=q;v=e;while(true){if(!(v=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+v]=b.removeLeadingRegexp(((v<0||v>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+v]),w));v=v+(1)>>0;}x=b.collapse($subslice(a,e,r),19);$s=18;case 18:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}y=x;z=b.newRegexp(18);z.Sub=$append($subslice(z.Sub,0,0),u,y);f=$append(f,z);case 17:e=r;q=s;r=r+(1)>>0;$s=10;continue;case 11:a=f;e=0;f=$subslice(a,0,0);aa=0;case 19:if(!(aa<=a.$length)){$s=20;continue;}if(aa=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+aa]))){$s=21;continue;}$s=22;continue;case 21:aa=aa+(1)>>0;$s=19;continue;case 22:if(aa===e){$s=23;continue;}if(aa===(e+1>>0)){$s=24;continue;}$s=25;continue;case 23:$s=26;continue;case 24:f=$append(f,((e<0||e>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]));$s=26;continue;case 25:ab=e;ac=e+1>>0;while(true){if(!(ac=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ab]).Op<((ac<0||ac>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ac]).Op||(((ab<0||ab>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ab]).Op===((ac<0||ac>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ac]).Op)&&((ab<0||ab>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ab]).Rune.$length<((ac<0||ac>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ac]).Rune.$length){ab=ac;}ac=ac+(1)>>0;}ad=((ab<0||ab>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ab]);ae=((e<0||e>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]);((e<0||e>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]=ad);((ab<0||ab>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ab]=ae);af=e+1>>0;while(true){if(!(af=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]),((af<0||af>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+af]));b.reuse(((af<0||af>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+af]));af=af+(1)>>0;}$r=S(((e<0||e>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]));$s=27;case 27:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}f=$append(f,((e<0||e>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]));case 26:if(aa=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+aa]));}e=aa+1>>0;aa=aa+(1)>>0;$s=19;continue;case 20:a=f;e=0;f=$subslice(a,0,0);ag=a;ah=0;while(true){if(!(ah>0)=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ai]).Op===2)&&((aj=ai+1>>0,((aj<0||aj>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+aj])).Op===2)){ah++;continue;}f=$append(f,((ai<0||ai>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+ai]));ah++;}a=f;$s=-1;return a;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.factor};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.factor=function(a){return this.$val.factor(a);};P.ptr.prototype.leadingString=function(a){var a,b,c;b=this;if((a.Op===18)&&a.Sub.$length>0){a=(c=a.Sub,(0>=c.$length?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+0]));}if(!((a.Op===3))){return[CA.nil,0];}return[a.Rune,(a.Flags&1)>>>0];};P.prototype.leadingString=function(a){return this.$val.leadingString(a);};P.ptr.prototype.removeLeadingString=function(a,b){var a,b,c,d,e,f,g,h,i;c=this;if((a.Op===18)&&a.Sub.$length>0){e=(d=a.Sub,(0>=d.$length?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+0]));e=c.removeLeadingString(e,b);(f=a.Sub,(0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0]=e));if(e.Op===2){c.reuse(e);g=a.Sub.$length;if((g===(0))||(g===(1))){a.Op=2;a.Sub=CH.nil;}else if(g===(2)){h=a;a=(i=a.Sub,(1>=i.$length?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+1]));c.reuse(h);}else{$copySlice(a.Sub,$subslice(a.Sub,1));a.Sub=$subslice(a.Sub,0,(a.Sub.$length-1>>0));}}return a;}if(a.Op===3){a.Rune=$subslice(a.Rune,0,$copySlice(a.Rune,$subslice(a.Rune,b)));if(a.Rune.$length===0){a.Op=2;}}return a;};P.prototype.removeLeadingString=function(a,b){return this.$val.removeLeadingString(a,b);};P.ptr.prototype.leadingRegexp=function(a){var a,b,c,d;b=this;if(a.Op===2){return CG.nil;}if((a.Op===18)&&a.Sub.$length>0){d=(c=a.Sub,(0>=c.$length?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+0]));if(d.Op===2){return CG.nil;}return d;}return a;};P.prototype.leadingRegexp=function(a){return this.$val.leadingRegexp(a);};P.ptr.prototype.removeLeadingRegexp=function(a,b){var a,b,c,d,e,f,g;c=this;if((a.Op===18)&&a.Sub.$length>0){if(b){c.reuse((d=a.Sub,(0>=d.$length?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+0])));}a.Sub=$subslice(a.Sub,0,$copySlice(a.Sub,$subslice(a.Sub,1)));e=a.Sub.$length;if(e===(0)){a.Op=2;a.Sub=CH.nil;}else if(e===(1)){f=a;a=(g=a.Sub,(0>=g.$length?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+0]));c.reuse(f);}return a;}if(b){c.reuse(a);}return c.newRegexp(2);};P.prototype.removeLeadingRegexp=function(a,b){return this.$val.removeLeadingRegexp(a,b);};T=function(a,b){var a,b,c,d,e,f,g;c=new BV.ptr(3,0,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,"");c.Flags=b;c.Rune=$subslice(new CA(c.Rune0),0,0);d=a;e=0;while(true){if(!(e=c.Rune.$capacity){c.Rune=(new CA($stringToRunes(a)));break;}c.Rune=$append(c.Rune,g);e+=f[1];}return c;};U=function(a,b){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(!((((b&2)>>>0)===0))){c=AN(a);if(!($interfaceIsEqual(c,$ifaceNil))){$s=-1;return[CG.nil,c];}$s=-1;return[T(a,b),$ifaceNil];}d=new P.ptr(0,CH.nil,CG.nil,0,"",CA.nil);e=$ifaceNil;f=0;g=0;h="";d.flags=b;d.wholeRegexp=a;i=a;case 1:if(!(!(i===""))){$s=2;continue;}j="";k=i.charCodeAt(0);if(k===(40)){$s=4;continue;}if(k===(124)){$s=5;continue;}if(k===(41)){$s=6;continue;}if(k===(94)){$s=7;continue;}if(k===(36)){$s=8;continue;}if(k===(46)){$s=9;continue;}if(k===(91)){$s=10;continue;}if((k===(42))||(k===(43))||(k===(63))){$s=11;continue;}if(k===(123)){$s=12;continue;}if(k===(92)){$s=13;continue;}$s=14;continue;case 4:if(!((((d.flags&64)>>>0)===0))&&i.length>=2&&(i.charCodeAt(1)===63)){l=d.parsePerlFlags(i);i=l[0];e=l[1];if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[CG.nil,e];}$s=3;continue;}d.numCap=d.numCap+(1)>>0;d.op(128).Cap=d.numCap;i=$substring(i,1);$s=15;continue;case 5:m=d.parseVerticalBar();$s=16;case 16:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}e=m;if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[CG.nil,e];}i=$substring(i,1);$s=15;continue;case 6:n=d.parseRightParen();$s=17;case 17:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}e=n;if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[CG.nil,e];}i=$substring(i,1);$s=15;continue;case 7:if(!((((d.flags&16)>>>0)===0))){d.op(9);}else{d.op(7);}i=$substring(i,1);$s=15;continue;case 8:if(!((((d.flags&16)>>>0)===0))){o=d.op(10);o.Flags=(o.Flags|(256))>>>0;}else{d.op(8);}i=$substring(i,1);$s=15;continue;case 9:if(!((((d.flags&8)>>>0)===0))){d.op(6);}else{d.op(5);}i=$substring(i,1);$s=15;continue;case 10:q=d.parseClass(i);$s=18;case 18:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}p=q;i=p[0];e=p[1];if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[CG.nil,e];}$s=15;continue;case 11:r=i;s=i.charCodeAt(0);if(s===(42)){g=14;}else if(s===(43)){g=15;}else if(s===(63)){g=16;}t=$substring(i,1);u=d.repeat(g,0,0,r,t,h);t=u[0];e=u[1];if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[CG.nil,e];}j=r;i=t;$s=15;continue;case 12:g=17;v=i;w=d.parseRepeat(i);x=w[0];y=w[1];z=w[2];aa=w[3];if(!aa){d.literal(123);i=$substring(i,1);$s=3;continue;}if(x<0||x>1000||y>1000||y>=0&&x>y){$s=-1;return[CG.nil,new M.ptr("invalid repeat count",$substring(v,0,(v.length-z.length>>0)))];}ab=d.repeat(g,x,y,v,z,h);z=ab[0];e=ab[1];if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[CG.nil,e];}j=v;i=z;$s=15;continue;case 13:if(!((((d.flags&64)>>>0)===0))&&i.length>=2){ac=i.charCodeAt(1);if(ac===(65)){d.op(9);i=$substring(i,2);$s=3;continue s;}else if(ac===(98)){d.op(11);i=$substring(i,2);$s=3;continue s;}else if(ac===(66)){d.op(12);i=$substring(i,2);$s=3;continue s;}else if(ac===(67)){$s=-1;return[CG.nil,new M.ptr("invalid escape sequence",$substring(i,0,2))];}else if(ac===(81)){ad="";ae=D.Index(i,"\\E");if(ae<0){ad=$substring(i,2);i="";}else{ad=$substring(i,2,ae);i=$substring(i,(ae+2>>0));}while(true){if(!(!(ad===""))){break;}af=AO(ad);ag=af[0];ah=af[1];ai=af[2];if(!($interfaceIsEqual(ai,$ifaceNil))){$s=-1;return[CG.nil,ai];}d.literal(ag);ad=ah;}$s=3;continue s;}else if(ac===(122)){d.op(10);i=$substring(i,2);$s=3;continue s;}}aj=d.newRegexp(4);aj.Flags=d.flags;if(i.length>=2&&((i.charCodeAt(1)===112)||(i.charCodeAt(1)===80))){$s=19;continue;}$s=20;continue;case 19:al=d.parseUnicodeClass(i,$subslice(new CA(aj.Rune0),0,0));$s=21;case 21:if($c){$c=false;al=al.$blk();}if(al&&al.$blk!==undefined){break s;}ak=al;am=ak[0];an=ak[1];ao=ak[2];if(!($interfaceIsEqual(ao,$ifaceNil))){$s=-1;return[CG.nil,ao];}if(!(am===CA.nil)){aj.Rune=am;i=an;d.push(aj);$s=3;continue s;}case 20:aq=d.parsePerlClassEscape(i,$subslice(new CA(aj.Rune0),0,0));$s=22;case 22:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ap=aq;ar=ap[0];as=ap[1];if(!(ar===CA.nil)){aj.Rune=ar;i=as;d.push(aj);$s=3;continue s;}d.reuse(aj);at=d.parseEscape(i);f=at[0];i=at[1];e=at[2];if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[CG.nil,e];}d.literal(f);$s=15;continue;case 14:au=AO(i);f=au[0];i=au[1];e=au[2];if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[CG.nil,e];}d.literal(f);case 15:case 3:h=j;$s=1;continue;case 2:av=d.concat();$s=23;case 23:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}av;aw=d.swapVerticalBar();$s=26;case 26:if($c){$c=false;aw=aw.$blk();}if(aw&&aw.$blk!==undefined){break s;}if(aw){$s=24;continue;}$s=25;continue;case 24:d.stack=$subslice(d.stack,0,(d.stack.$length-1>>0));case 25:ax=d.alternate();$s=27;case 27:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}ax;ay=d.stack.$length;if(!((ay===1))){$s=-1;return[CG.nil,new M.ptr("missing closing )",a)];}$s=-1;return[(az=d.stack,(0>=az.$length?($throwRuntimeError("index out of range"),undefined):az.$array[az.$offset+0])),$ifaceNil];}return;}if($f===undefined){$f={$blk:U};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Parse=U;P.ptr.prototype.parseRepeat=function(a){var a,b,c,d,e,f,g,h,i;b=0;c=0;d="";e=false;f=this;if(a===""||!((a.charCodeAt(0)===123))){return[b,c,d,e];}a=$substring(a,1);g=false;h=f.parseInt(a);b=h[0];a=h[1];g=h[2];if(!g){return[b,c,d,e];}if(a===""){return[b,c,d,e];}if(!((a.charCodeAt(0)===44))){c=b;}else{a=$substring(a,1);if(a===""){return[b,c,d,e];}if(a.charCodeAt(0)===125){c=-1;}else{i=f.parseInt(a);c=i[0];a=i[1];g=i[2];if(!g){return[b,c,d,e];}else if(c<0){b=-1;}}}if(a===""||!((a.charCodeAt(0)===125))){return[b,c,d,e];}d=$substring(a,1);e=true;return[b,c,d,e];};P.prototype.parseRepeat=function(a){return this.$val.parseRepeat(a);};P.ptr.prototype.parsePerlFlags=function(a){var a,aa,ab,ac,ad,ae,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;b="";c=$ifaceNil;d=this;e=a;if(e.length>4&&(e.charCodeAt(2)===80)&&(e.charCodeAt(3)===60)){f=D.IndexRune(e,62);if(f<0){c=AN(e);if(!($interfaceIsEqual(c,$ifaceNil))){g="";h=c;b=g;c=h;return[b,c];}i="";j=new M.ptr("invalid named capture",a);b=i;c=j;return[b,c];}k=$substring(e,0,(f+1>>0));l=$substring(e,4,f);c=AN(l);if(!($interfaceIsEqual(c,$ifaceNil))){m="";n=c;b=m;c=n;return[b,c];}if(!V(l)){o="";p=new M.ptr("invalid named capture",k);b=o;c=p;return[b,c];}d.numCap=d.numCap+(1)>>0;q=d.op(128);q.Cap=d.numCap;q.Name=l;r=$substring(e,(f+1>>0));s=$ifaceNil;b=r;c=s;return[b,c];}t=0;e=$substring(e,2);u=d.flags;v=1;w=false;Loop:while(true){if(!(!(e===""))){break;}x=AO(e);t=x[0];e=x[1];c=x[2];if(!($interfaceIsEqual(c,$ifaceNil))){y="";z=c;b=y;c=z;return[b,c];}aa=t;if(aa===(105)){u=(u|(1))>>>0;w=true;}else if(aa===(109)){u=(u&~(16))<<16>>>16;w=true;}else if(aa===(115)){u=(u|(8))>>>0;w=true;}else if(aa===(85)){u=(u|(32))>>>0;w=true;}else if(aa===(45)){if(v<0){break Loop;}v=-1;u=~u<<16>>>16;w=false;}else if((aa===(58))||(aa===(41))){if(v<0){if(!w){break Loop;}u=~u<<16>>>16;}if(t===58){d.op(128);}d.flags=u;ab=e;ac=$ifaceNil;b=ab;c=ac;return[b,c];}else{break Loop;}}ad="";ae=new M.ptr("invalid or unsupported Perl syntax",$substring(a,0,(a.length-e.length>>0)));b=ad;c=ae;return[b,c];};P.prototype.parsePerlFlags=function(a){return this.$val.parsePerlFlags(a);};V=function(a){var a,b,c,d,e;if(a===""){return false;}b=a;c=0;while(true){if(!(c=2&&(a.charCodeAt(0)===48)&&48<=a.charCodeAt(1)&&a.charCodeAt(1)<=57){return[b,c,d];}f=a;while(true){if(!(!(a==="")&&48<=a.charCodeAt(0)&&a.charCodeAt(0)<=57)){break;}a=$substring(a,1);}c=a;d=true;f=$substring(f,0,(f.length-a.length>>0));g=0;while(true){if(!(g=100000000){b=-1;break;}b=(($imul(b,10))+((f.charCodeAt(g)>>0))>>0)-48>>0;g=g+(1)>>0;}return[b,c,d];};P.prototype.parseInt=function(a){return this.$val.parseInt(a);};W=function(a){var a;return(a.Op===3)&&(a.Rune.$length===1)||(a.Op===4)||(a.Op===5)||(a.Op===6);};X=function(a,b){var a,b,c,d,e,f,g,h;c=a.Op;if(c===(3)){return(a.Rune.$length===1)&&((d=a.Rune,(0>=d.$length?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+0]))===b);}else if(c===(4)){e=0;while(true){if(!(e=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+e]))<=b&&b<=(g=a.Rune,h=e+1>>0,((h<0||h>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+h]))){return true;}e=e+(2)>>0;}return false;}else if(c===(5)){return!((b===10));}else if(c===(6)){return true;}return false;};P.ptr.prototype.parseVerticalBar=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.concat();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}b;c=a.swapVerticalBar();$s=4;case 4:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}if(!c){$s=2;continue;}$s=3;continue;case 2:a.op(129);case 3:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.parseVerticalBar};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.parseVerticalBar=function(){return this.$val.parseVerticalBar();};Y=function(a,b){var a,b,c,d,e,f,g,h;switch(0){default:c=a.Op;if(c===(6)){}else if(c===(5)){if(X(b,10)){a.Op=6;}}else if(c===(4)){if(b.Op===3){a.Rune=AD(a.Rune,(d=b.Rune,(0>=d.$length?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+0])),b.Flags);}else{a.Rune=AG(a.Rune,b.Rune);}}else if(c===(3)){if(((e=b.Rune,(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]))===(f=a.Rune,(0>=f.$length?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+0])))&&(b.Flags===a.Flags)){break;}a.Op=4;a.Rune=AD($subslice(a.Rune,0,0),(g=a.Rune,(0>=g.$length?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+0])),a.Flags);a.Rune=AD(a.Rune,(h=b.Rune,(0>=h.$length?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+0])),b.Flags);}}};P.ptr.prototype.swapVerticalBar=function(){var a,aa,ab,ac,ad,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.stack.$length;if(b>=3&&((c=a.stack,d=b-2>>0,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d])).Op===129)&&W((e=a.stack,f=b-1>>0,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f])))&&W((g=a.stack,h=b-3>>0,((h<0||h>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+h])))){k=(i=a.stack,j=b-1>>0,((j<0||j>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]));n=(l=a.stack,m=b-3>>0,((m<0||m>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+m]));if(k.Op>n.Op){o=n;p=k;k=o;n=p;(q=a.stack,r=b-3>>0,((r<0||r>=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+r]=n));}Y(n,k);a.reuse(k);a.stack=$subslice(a.stack,0,(b-1>>0));$s=-1;return true;}if(b>=2){$s=1;continue;}$s=2;continue;case 1:u=(s=a.stack,t=b-1>>0,((t<0||t>=s.$length)?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+t]));x=(v=a.stack,w=b-2>>0,((w<0||w>=v.$length)?($throwRuntimeError("index out of range"),undefined):v.$array[v.$offset+w]));if(x.Op===129){$s=3;continue;}$s=4;continue;case 3:if(b>=3){$s=5;continue;}$s=6;continue;case 5:$r=S((y=a.stack,z=b-3>>0,((z<0||z>=y.$length)?($throwRuntimeError("index out of range"),undefined):y.$array[y.$offset+z])));$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 6:(aa=a.stack,ab=b-2>>0,((ab<0||ab>=aa.$length)?($throwRuntimeError("index out of range"),undefined):aa.$array[aa.$offset+ab]=u));(ac=a.stack,ad=b-1>>0,((ad<0||ad>=ac.$length)?($throwRuntimeError("index out of range"),undefined):ac.$array[ac.$offset+ad]=x));$s=-1;return true;case 4:case 2:$s=-1;return false;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.swapVerticalBar};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.swapVerticalBar=function(){return this.$val.swapVerticalBar();};P.ptr.prototype.parseRightParen=function(){var a,b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.concat();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}b;c=a.swapVerticalBar();$s=4;case 4:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}if(c){$s=2;continue;}$s=3;continue;case 2:a.stack=$subslice(a.stack,0,(a.stack.$length-1>>0));case 3:d=a.alternate();$s=5;case 5:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}d;e=a.stack.$length;if(e<2){$s=-1;return new M.ptr("unexpected )",a.wholeRegexp);}h=(f=a.stack,g=e-1>>0,((g<0||g>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+g]));k=(i=a.stack,j=e-2>>0,((j<0||j>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]));a.stack=$subslice(a.stack,0,(e-2>>0));if(!((k.Op===128))){$s=-1;return new M.ptr("unexpected )",a.wholeRegexp);}a.flags=k.Flags;if(k.Cap===0){a.push(h);}else{k.Op=13;k.Sub=$subslice(new CH(k.Sub0),0,1);(l=k.Sub,(0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0]=h));a.push(k);}$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.parseRightParen};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.parseRightParen=function(){return this.$val.parseRightParen();};P.ptr.prototype.parseEscape=function(a){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,bo,bp,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;b=0;c="";d=$ifaceNil;e=this;f=$substring(a,1);if(f===""){g=0;h="";i=new M.ptr("trailing backslash at end of expression","");b=g;c=h;d=i;return[b,c,d];}j=AO(f);k=j[0];f=j[1];d=j[2];if(!($interfaceIsEqual(d,$ifaceNil))){l=0;m="";n=d;b=l;c=m;d=n;return[b,c,d];}Switch:switch(0){default:o=k;if((o===(49))||(o===(50))||(o===(51))||(o===(52))||(o===(53))||(o===(54))||(o===(55))){if(f===""||f.charCodeAt(0)<48||f.charCodeAt(0)>55){break;}b=k-48>>0;p=1;while(true){if(!(p<3)){break;}if(f===""||f.charCodeAt(0)<48||f.charCodeAt(0)>55){break;}b=(($imul(b,8))+((f.charCodeAt(0)>>0))>>0)-48>>0;f=$substring(f,1);p=p+(1)>>0;}q=b;r=f;s=$ifaceNil;b=q;c=r;d=s;return[b,c,d];}else if(o===(48)){b=k-48>>0;p=1;while(true){if(!(p<3)){break;}if(f===""||f.charCodeAt(0)<48||f.charCodeAt(0)>55){break;}b=(($imul(b,8))+((f.charCodeAt(0)>>0))>>0)-48>>0;f=$substring(f,1);p=p+(1)>>0;}t=b;u=f;v=$ifaceNil;b=t;c=u;d=v;return[b,c,d];}else if(o===(120)){if(f===""){break;}w=AO(f);k=w[0];f=w[1];d=w[2];if(!($interfaceIsEqual(d,$ifaceNil))){x=0;y="";z=d;b=x;c=y;d=z;return[b,c,d];}if(k===123){aa=0;b=0;while(true){if(f===""){break Switch;}ab=AO(f);k=ab[0];f=ab[1];d=ab[2];if(!($interfaceIsEqual(d,$ifaceNil))){ac=0;ad="";ae=d;b=ac;c=ad;d=ae;return[b,c,d];}if(k===125){break;}af=AQ(k);if(af<0){break Switch;}b=($imul(b,16))+af>>0;if(b>1114111){break Switch;}aa=aa+(1)>>0;}if(aa===0){break Switch;}ag=b;ah=f;ai=$ifaceNil;b=ag;c=ah;d=ai;return[b,c,d];}aj=AQ(k);ak=AO(f);k=ak[0];f=ak[1];d=ak[2];if(!($interfaceIsEqual(d,$ifaceNil))){al=0;am="";an=d;b=al;c=am;d=an;return[b,c,d];}ao=AQ(k);if(aj<0||ao<0){break;}ap=($imul(aj,16))+ao>>0;aq=f;ar=$ifaceNil;b=ap;c=aq;d=ar;return[b,c,d];}else if(o===(97)){as=7;at=f;au=d;b=as;c=at;d=au;return[b,c,d];}else if(o===(102)){av=12;aw=f;ax=d;b=av;c=aw;d=ax;return[b,c,d];}else if(o===(110)){ay=10;az=f;ba=d;b=ay;c=az;d=ba;return[b,c,d];}else if(o===(114)){bb=13;bc=f;bd=d;b=bb;c=bc;d=bd;return[b,c,d];}else if(o===(116)){be=9;bf=f;bg=d;b=be;c=bf;d=bg;return[b,c,d];}else if(o===(118)){bh=11;bi=f;bj=d;b=bh;c=bi;d=bj;return[b,c,d];}else if(k<128&&!AP(k)){bk=k;bl=f;bm=$ifaceNil;b=bk;c=bl;d=bm;return[b,c,d];}}bn=0;bo="";bp=new M.ptr("invalid escape sequence",$substring(a,0,(a.length-f.length>>0)));b=bn;c=bo;d=bp;return[b,c,d];};P.prototype.parseEscape=function(a){return this.$val.parseEscape(a);};P.ptr.prototype.parseClassChar=function(a,b){var a,b,c,d,e,f,g,h,i,j,k;c=0;d="";e=$ifaceNil;f=this;if(a===""){g=0;h="";i=new M.ptr("missing closing ]",b);c=g;d=h;e=i;return[c,d,e];}if(a.charCodeAt(0)===92){j=f.parseEscape(a);c=j[0];d=j[1];e=j[2];return[c,d,e];}k=AO(a);c=k[0];d=k[1];e=k[2];return[c,d,e];};P.prototype.parseClassChar=function(a,b){return this.$val.parseClassChar(a,b);};P.ptr.prototype.parsePerlClassEscape=function(a,b){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=CA.nil;d="";e=this;if((((e.flags&64)>>>0)===0)||a.length<2||!((a.charCodeAt(0)===92))){$s=-1;return[c,d];}g=$clone((f=AU[$String.keyFor($substring(a,0,2))],f!==undefined?f.v:new Z.ptr(0,CA.nil)),Z);if(g.sign===0){$s=-1;return[c,d];}i=e.appendGroup(b,$clone(g,Z));$s=1;case 1:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;j=$substring(a,2);c=h;d=j;$s=-1;return[c,d];}return;}if($f===undefined){$f={$blk:P.ptr.prototype.parsePerlClassEscape};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.parsePerlClassEscape=function(a,b){return this.$val.parsePerlClassEscape(a,b);};P.ptr.prototype.parseNamedClass=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=CA.nil;d="";e=$ifaceNil;f=this;if(a.length<2||!((a.charCodeAt(0)===91))||!((a.charCodeAt(1)===58))){$s=-1;return[c,d,e];}g=D.Index($substring(a,2),":]");if(g<0){$s=-1;return[c,d,e];}g=g+(2)>>0;h=$substring(a,0,(g+2>>0));i=$substring(a,(g+2>>0));j=h;a=i;l=$clone((k=BJ[$String.keyFor(j)],k!==undefined?k.v:new Z.ptr(0,CA.nil)),Z);if(l.sign===0){m=CA.nil;n="";o=new M.ptr("invalid character class range",j);c=m;d=n;e=o;$s=-1;return[c,d,e];}q=f.appendGroup(b,$clone(l,Z));$s=1;case 1:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}p=q;r=a;s=$ifaceNil;c=p;d=r;e=s;$s=-1;return[c,d,e];}return;}if($f===undefined){$f={$blk:P.ptr.prototype.parseNamedClass};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.parseNamedClass=function(a,b){return this.$val.parseNamedClass(a,b);};P.ptr.prototype.appendGroup=function(a,b){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(((c.flags&1)>>>0)===0){$s=1;continue;}$s=2;continue;case 1:if(b.sign<0){a=AI(a,b.class$1);}else{a=AG(a,b.class$1);}$s=3;continue;case 2:d=$subslice(c.tmpClass,0,0);d=AH(d,b.class$1);c.tmpClass=d;e=AC((c.$ptr_tmpClass||(c.$ptr_tmpClass=new CK(function(){return this.$target.tmpClass;},function($v){this.$target.tmpClass=$v;},c))));$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;if(b.sign<0){a=AI(a,d);}else{a=AG(a,d);}case 3:$s=-1;return a;}return;}if($f===undefined){$f={$blk:P.ptr.prototype.appendGroup};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.appendGroup=function(a,b){return this.$val.appendGroup(a,b);};AB=function(a){var a,b,c,d,e,f,g;if(a==="Any"){return[AA,AA];}c=(b=A.Categories[$String.keyFor(a)],b!==undefined?b.v:CL.nil);if(!(c===CL.nil)){return[c,(d=A.FoldCategory[$String.keyFor(a)],d!==undefined?d.v:CL.nil)];}f=(e=A.Scripts[$String.keyFor(a)],e!==undefined?e.v:CL.nil);if(!(f===CL.nil)){return[f,(g=A.FoldScript[$String.keyFor(a)],g!==undefined?g.v:CL.nil)];}return[CL.nil,CL.nil];};P.ptr.prototype.parseUnicodeClass=function(a,b){var a,aa,ab,ac,ad,ae,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=CA.nil;d="";e=$ifaceNil;f=this;if((((f.flags&128)>>>0)===0)||a.length<2||!((a.charCodeAt(0)===92))||!((a.charCodeAt(1)===112))&&!((a.charCodeAt(1)===80))){$s=-1;return[c,d,e];}g=1;if(a.charCodeAt(1)===80){g=-1;}h=$substring(a,2);i=AO(h);j=i[0];h=i[1];e=i[2];if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[c,d,e];}k="";l="";m=k;n=l;if(!((j===123))){m=$substring(a,0,(a.length-h.length>>0));n=$substring(m,2);}else{o=D.IndexRune(a,125);if(o<0){e=AN(a);if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[c,d,e];}p=CA.nil;q="";r=new M.ptr("invalid character class range",a);c=p;d=q;e=r;$s=-1;return[c,d,e];}s=$substring(a,0,(o+1>>0));t=$substring(a,(o+1>>0));m=s;h=t;n=$substring(a,3,o);e=AN(n);if(!($interfaceIsEqual(e,$ifaceNil))){$s=-1;return[c,d,e];}}if(!(n==="")&&(n.charCodeAt(0)===94)){g=-g;n=$substring(n,1);}u=AB(n);v=u[0];w=u[1];if(v===CL.nil){x=CA.nil;y="";z=new M.ptr("invalid character class range",m);c=x;d=y;e=z;$s=-1;return[c,d,e];}if((((f.flags&1)>>>0)===0)||w===CL.nil){$s=1;continue;}$s=2;continue;case 1:if(g>0){b=AJ(b,v);}else{b=AK(b,v);}$s=3;continue;case 2:aa=$subslice(f.tmpClass,0,0);aa=AJ(aa,v);aa=AJ(aa,w);f.tmpClass=aa;ab=AC((f.$ptr_tmpClass||(f.$ptr_tmpClass=new CK(function(){return this.$target.tmpClass;},function($v){this.$target.tmpClass=$v;},f))));$s=4;case 4:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}aa=ab;if(g>0){b=AG(b,aa);}else{b=AI(b,aa);}case 3:ac=b;ad=h;ae=$ifaceNil;c=ac;d=ad;e=ae;$s=-1;return[c,d,e];}return;}if($f===undefined){$f={$blk:P.ptr.prototype.parseUnicodeClass};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.parseUnicodeClass=function(a,b){return this.$val.parseUnicodeClass(a,b);};P.ptr.prototype.parseClass=function(a){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,ba,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;b=$f.b;ba=$f.ba;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b="";c=$ifaceNil;d=this;e=$substring(a,1);f=d.newRegexp(4);f.Flags=d.flags;f.Rune=$subslice(new CA(f.Rune0),0,0);g=1;if(!(e==="")&&(e.charCodeAt(0)===94)){g=-1;e=$substring(e,1);if(((d.flags&4)>>>0)===0){f.Rune=$append(f.Rune,10,10);}}h=f.Rune;i=true;case 1:if(!(e===""||!((e.charCodeAt(0)===93))||i)){$s=2;continue;}if(!(e==="")&&(e.charCodeAt(0)===45)&&(((d.flags&64)>>>0)===0)&&!i&&((e.length===1)||!((e.charCodeAt(1)===93)))){j=E.DecodeRuneInString($substring(e,1));k=j[1];l="";m=new M.ptr("invalid character class range",$substring(e,0,(1+k>>0)));b=l;c=m;$s=-1;return[b,c];}i=false;if(e.length>2&&(e.charCodeAt(0)===91)&&(e.charCodeAt(1)===58)){$s=3;continue;}$s=4;continue;case 3:o=d.parseNamedClass(e,h);$s=5;case 5:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;p=n[0];q=n[1];r=n[2];if(!($interfaceIsEqual(r,$ifaceNil))){s="";t=r;b=s;c=t;$s=-1;return[b,c];}if(!(p===CA.nil)){u=p;v=q;h=u;e=v;$s=1;continue;}case 4:x=d.parseUnicodeClass(e,h);$s=6;case 6:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}w=x;y=w[0];z=w[1];aa=w[2];if(!($interfaceIsEqual(aa,$ifaceNil))){ab="";ac=aa;b=ab;c=ac;$s=-1;return[b,c];}if(!(y===CA.nil)){$s=7;continue;}$s=8;continue;case 7:ad=y;ae=z;h=ad;e=ae;$s=1;continue;case 8:ag=d.parsePerlClassEscape(e,h);$s=9;case 9:if($c){$c=false;ag=ag.$blk();}if(ag&&ag.$blk!==undefined){break s;}af=ag;ah=af[0];ai=af[1];if(!(ah===CA.nil)){aj=ah;ak=ai;h=aj;e=ak;$s=1;continue;}al=e;am=0;an=0;ao=am;ap=an;aq=d.parseClassChar(e,a);ao=aq[0];e=aq[1];aa=aq[2];if(!($interfaceIsEqual(aa,$ifaceNil))){ar="";as=aa;b=ar;c=as;$s=-1;return[b,c];}ap=ao;if(e.length>=2&&(e.charCodeAt(0)===45)&&!((e.charCodeAt(1)===93))){e=$substring(e,1);at=d.parseClassChar(e,a);ap=at[0];e=at[1];aa=at[2];if(!($interfaceIsEqual(aa,$ifaceNil))){au="";av=aa;b=au;c=av;$s=-1;return[b,c];}if(ap>0));aw="";ax=new M.ptr("invalid character class range",al);b=aw;c=ax;$s=-1;return[b,c];}}if(((d.flags&1)>>>0)===0){h=AE(h,ao,ap);}else{h=AF(h,ao,ap);}$s=1;continue;case 2:e=$substring(e,1);f.Rune=h;ay=AC((f.$ptr_Rune||(f.$ptr_Rune=new CK(function(){return this.$target.Rune;},function($v){this.$target.Rune=$v;},f))));$s=10;case 10:if($c){$c=false;ay=ay.$blk();}if(ay&&ay.$blk!==undefined){break s;}h=ay;if(g<0){h=AL(h);}f.Rune=h;d.push(f);az=e;ba=$ifaceNil;b=az;c=ba;$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:P.ptr.prototype.parseClass};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.b=b;$f.ba=ba;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};P.prototype.parseClass=function(a){return this.$val.parseClass(a);};AC=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=C.Sort((b=new AM.ptr(a),new b.constructor.elem(b)));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}c=a.$get();if(c.$length<2){$s=-1;return c;}d=2;e=2;while(true){if(!(e=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+e]);g=(h=e+1>>0,((h<0||h>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+h]));i=f;j=g;if(i<=((k=d-1>>0,((k<0||k>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+k]))+1>>0)){if(j>(l=d-1>>0,((l<0||l>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+l]))){(m=d-1>>0,((m<0||m>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+m]=j));}e=e+(2)>>0;continue;}((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]=i);(n=d+1>>0,((n<0||n>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+n]=j));d=d+(2)>>0;e=e+(2)>>0;}$s=-1;return $subslice(c,0,d);}return;}if($f===undefined){$f={$blk:AC};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};AD=function(a,b,c){var a,b,c;if(!((((c&1)>>>0)===0))){return AF(a,b,b);}return AE(a,b,b);};AE=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,k,l,m;d=a.$length;e=2;while(true){if(!(e<=4)){break;}if(d>=e){f=(g=d-e>>0,((g<0||g>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+g]));h=(i=(d-e>>0)+1>>0,((i<0||i>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+i]));j=f;k=h;if(b<=(k+1>>0)&&j<=(c+1>>0)){if(b>0,((l<0||l>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+l]=b));}if(c>k){(m=(d-e>>0)+1>>0,((m<0||m>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+m]=c));}return a;}}e=e+(2)>>0;}return $append(a,b,c);};AF=function(a,b,c){var a,b,c,d,e;if(b<=65&&c>=125251){return AE(a,b,c);}if(c<65||b>125251){return AE(a,b,c);}if(b<65){a=AE(a,b,64);b=65;}if(c>125251){a=AE(a,125252,c);c=125251;}d=b;while(true){if(!(d<=c)){break;}a=AE(a,d,d);e=A.SimpleFold(d);while(true){if(!(!((e===d)))){break;}a=AE(a,e,e);e=A.SimpleFold(e);}d=d+(1)>>0;}return a;};AG=function(a,b){var a,b,c,d;c=0;while(true){if(!(c=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+c]),(d=c+1>>0,((d<0||d>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+d])));c=c+(2)>>0;}return a;};AH=function(a,b){var a,b,c,d;c=0;while(true){if(!(c=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+c]),(d=c+1>>0,((d<0||d>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+d])));c=c+(2)>>0;}return a;};AI=function(a,b){var a,b,c,d,e,f,g,h,i;c=0;d=0;while(true){if(!(d=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+d]);f=(g=d+1>>0,((g<0||g>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+g]));h=e;i=f;if(c<=(h-1>>0)){a=AE(a,c,h-1>>0);}c=i+1>>0;d=d+(2)>>0;}if(c<=1114111){a=AE(a,c,1114111);}return a;};AJ=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v;c=b.R16;d=0;while(true){if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]),A.Range16);f=((e.Lo>>0));g=((e.Hi>>0));h=((e.Stride>>0));i=f;j=g;k=h;if(k===1){a=AE(a,i,j);d++;continue;}l=i;while(true){if(!(l<=j)){break;}a=AE(a,l,l);l=l+(k)>>0;}d++;}m=b.R32;n=0;while(true){if(!(n=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+n]),A.Range32);p=((o.Lo>>0));q=((o.Hi>>0));r=((o.Stride>>0));s=p;t=q;u=r;if(u===1){a=AE(a,s,t);n++;continue;}v=s;while(true){if(!(v<=t)){break;}a=AE(a,v,v);v=v+(u)>>0;}n++;}return a;};AK=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w;c=0;d=b.R16;e=0;while(true){if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]),A.Range16);g=((f.Lo>>0));h=((f.Hi>>0));i=((f.Stride>>0));j=g;k=h;l=i;if(l===1){if(c<=(j-1>>0)){a=AE(a,c,j-1>>0);}c=k+1>>0;e++;continue;}m=j;while(true){if(!(m<=k)){break;}if(c<=(m-1>>0)){a=AE(a,c,m-1>>0);}c=m+1>>0;m=m+(l)>>0;}e++;}n=b.R32;o=0;while(true){if(!(o=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]),A.Range32);q=((p.Lo>>0));r=((p.Hi>>0));s=((p.Stride>>0));t=q;u=r;v=s;if(v===1){if(c<=(t-1>>0)){a=AE(a,c,t-1>>0);}c=u+1>>0;o++;continue;}w=t;while(true){if(!(w<=u)){break;}if(c<=(w-1>>0)){a=AE(a,c,w-1>>0);}c=w+1>>0;w=w+(v)>>0;}o++;}if(c<=1114111){a=AE(a,c,1114111);}return a;};AL=function(a){var a,b,c,d,e,f,g,h,i,j;b=0;c=0;d=0;while(true){if(!(d=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+d]);f=(g=d+1>>0,((g<0||g>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+g]));h=e;i=f;if(b<=(h-1>>0)){((c<0||c>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+c]=b);(j=c+1>>0,((j<0||j>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+j]=(h-1>>0)));c=c+(2)>>0;}b=i+1>>0;d=d+(2)>>0;}a=$subslice(a,0,c);if(b<=1114111){a=$append(a,b,1114111);}return a;};AM.ptr.prototype.Less=function(a,b){var a,b,c,d,e,f;c=this;d=c.p.$get();a=$imul(a,(2));b=$imul(b,(2));return((a<0||a>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+a])<((b<0||b>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+b])||(((a<0||a>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+a])===((b<0||b>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+b]))&&(e=a+1>>0,((e<0||e>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]))>(f=b+1>>0,((f<0||f>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+f]));};AM.prototype.Less=function(a,b){return this.$val.Less(a,b);};AM.ptr.prototype.Len=function(){var a,b;a=this;return(b=a.p.$get().$length/2,(b===b&&b!==1/0&&b!==-1/0)?b>>0:$throwRuntimeError("integer divide by zero"));};AM.prototype.Len=function(){return this.$val.Len();};AM.ptr.prototype.Swap=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l;c=this;d=c.p.$get();a=$imul(a,(2));b=$imul(b,(2));e=((b<0||b>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+b]);f=(g=b+1>>0,((g<0||g>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+g]));h=((a<0||a>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+a]);i=(j=a+1>>0,((j<0||j>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+j]));((a<0||a>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+a]=e);(k=a+1>>0,((k<0||k>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+k]=f));((b<0||b>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+b]=h);(l=b+1>>0,((l<0||l>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+l]=i));};AM.prototype.Swap=function(a,b){return this.$val.Swap(a,b);};AN=function(a){var a,b,c,d;while(true){if(!(!(a===""))){break;}b=E.DecodeRuneInString(a);c=b[0];d=b[1];if((c===65533)&&(d===1)){return new M.ptr("invalid UTF-8",a);}a=$substring(a,d);}return $ifaceNil;};AO=function(a){var a,b,c,d,e,f,g,h,i,j,k,l;b=0;c="";d=$ifaceNil;e=E.DecodeRuneInString(a);b=e[0];f=e[1];if((b===65533)&&(f===1)){g=0;h="";i=new M.ptr("invalid UTF-8",a);b=g;c=h;d=i;return[b,c,d];}j=b;k=$substring(a,f);l=$ifaceNil;b=j;c=k;d=l;return[b,c,d];};AP=function(a){var a;return 48<=a&&a<=57||65<=a&&a<=90||97<=a&&a<=122;};AQ=function(a){var a;if(48<=a&&a<=57){return a-48>>0;}if(97<=a&&a<=102){return(a-97>>0)+10>>0;}if(65<=a&&a<=70){return(a-65>>0)+10>>0;}return-1;};BL.prototype.String=function(){var a;a=this.$val;if(((a>>>0))>=((BM.$length>>>0))){return"";}return((a<0||a>=BM.$length)?($throwRuntimeError("index out of range"),undefined):BM.$array[BM.$offset+a]);};$ptrType(BL).prototype.String=function(){return new BL(this.$get()).String();};BP=function(a){var a;return 65<=a&&a<=90||97<=a&&a<=122||48<=a&&a<=57||(a===95);};$pkg.IsWordChar=BP;BK.ptr.prototype.String=function(){var a,b;a=this;b=new D.Builder.ptr(CM.nil,CN.nil);BS(b,a);return b.String();};BK.prototype.String=function(){return this.$val.String();};BK.ptr.prototype.skipNop=function(a){var a,b,c,d,e,f;b=this;d=(c=b.Inst,((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]));while(true){if(!((d.Op===6)||(d.Op===2))){break;}d=(e=b.Inst,f=d.Out,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]));}return d;};BK.prototype.skipNop=function(a){return this.$val.skipNop(a);};BQ.ptr.prototype.op=function(){var a,b,c;a=this;b=a.Op;c=b;if((c===(8))||(c===(9))||(c===(10))){b=7;}return b;};BQ.prototype.op=function(){return this.$val.op();};BK.ptr.prototype.Prefix=function(){var a,b,c,d,e,f,g,h,i,j;a="";b=false;c=this;d=c.skipNop(((c.Start>>>0)));if(!((d.op()===7))||!((d.Rune.$length===1))){e="";f=d.Op===4;a=e;b=f;return[a,b];}g=new D.Builder.ptr(CM.nil,CN.nil);while(true){if(!((d.op()===7)&&(d.Rune.$length===1)&&(((((d.Arg<<16>>>16))&1)>>>0)===0))){break;}g.WriteRune((h=d.Rune,(0>=h.$length?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+0])));d=c.skipNop(d.Out);}i=g.String();j=d.Op===4;a=i;b=j;return[a,b];};BK.prototype.Prefix=function(){return this.$val.Prefix();};BK.ptr.prototype.StartCond=function(){var a,b,c,d,e,f,g;a=this;b=0;c=((a.Start>>>0));e=(d=a.Inst,((c<0||c>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+c]));Loop:while(true){f=e.Op;if(f===(3)){b=(b|(((e.Arg<<24>>>24))))>>>0;}else if(f===(5)){return 255;}else if((f===(2))||(f===(6))){}else{break Loop;}c=e.Out;e=(g=a.Inst,((c<0||c>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+c]));}return b;};BK.prototype.StartCond=function(){return this.$val.StartCond();};BQ.ptr.prototype.MatchRune=function(a){var a,b;b=this;return!((b.MatchRunePos(a)===-1));};BQ.prototype.MatchRune=function(a){return this.$val.MatchRune(a);};BQ.ptr.prototype.MatchRunePos=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;b=this;c=b.Rune;d=c.$length;if(d===(0)){return-1;}else if(d===(1)){e=(0>=c.$length?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+0]);if(a===e){return 0;}if(!((((((b.Arg<<16>>>16))&1)>>>0)===0))){f=A.SimpleFold(e);while(true){if(!(!((f===e)))){break;}if(a===f){return 0;}f=A.SimpleFold(f);}}return-1;}else if(d===(2)){if(a>=(0>=c.$length?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+0])&&a<=(1>=c.$length?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+1])){return 0;}return-1;}else if((d===(4))||(d===(6))||(d===(8))){g=0;while(true){if(!(g=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+g])){return-1;}if(a<=(h=g+1>>0,((h<0||h>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+h]))){return(i=g/2,(i===i&&i!==1/0&&i!==-1/0)?i>>0:$throwRuntimeError("integer divide by zero"));}g=g+(2)>>0;}return-1;}j=0;l=(k=c.$length/2,(k===k&&k!==1/0&&k!==-1/0)?k>>0:$throwRuntimeError("integer divide by zero"));while(true){if(!(j>0))/2,(m===m&&m!==1/0&&m!==-1/0)?m>>0:$throwRuntimeError("integer divide by zero"))>>0;p=(o=$imul(2,n),((o<0||o>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+o]));if(p<=a){if(a<=(q=($imul(2,n))+1>>0,((q<0||q>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+q]))){return n;}j=n+1>>0;}else{l=n;}}return-1;};BQ.prototype.MatchRunePos=function(a){return this.$val.MatchRunePos(a);};BQ.ptr.prototype.MatchEmptyWidth=function(a,b){var a,b,c,d;c=this;d=((c.Arg<<24>>>24));if(d===(1)){return(a===10)||(a===-1);}else if(d===(2)){return(b===10)||(b===-1);}else if(d===(4)){return a===-1;}else if(d===(8)){return b===-1;}else if(d===(16)){return!(BP(a)===BP(b));}else if(d===(32)){return BP(a)===BP(b);}$panic(new $String("unknown empty width arg"));};BQ.prototype.MatchEmptyWidth=function(a,b){return this.$val.MatchEmptyWidth(a,b);};BQ.ptr.prototype.String=function(){var a,b;a=this;b=new D.Builder.ptr(CM.nil,CN.nil);BU(b,a);return b.String();};BQ.prototype.String=function(){return this.$val.String();};BR=function(a,b){var a,b,c,d,e;c=b;d=0;while(true){if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]);a.WriteString(e);d++;}};BS=function(a,b){var a,b,c,d,e,f,g,h;c=b.Inst;d=0;while(true){if(!(d=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+e]));h=B.Itoa(e);if(h.length<3){a.WriteString($substring(" ",h.length));}if(e===b.Start){h=h+("*");}BR(a,new CD([h,"\t"]));BU(a,g);BR(a,new CD(["\n"]));d++;}};BT=function(a){var a;return B.FormatUint((new $Uint64(0,a)),10);};BU=function(a,b){var a,b,c;c=b.Op;if(c===(0)){BR(a,new CD(["alt -> ",BT(b.Out),", ",BT(b.Arg)]));}else if(c===(1)){BR(a,new CD(["altmatch -> ",BT(b.Out),", ",BT(b.Arg)]));}else if(c===(2)){BR(a,new CD(["cap ",BT(b.Arg)," -> ",BT(b.Out)]));}else if(c===(3)){BR(a,new CD(["empty ",BT(b.Arg)," -> ",BT(b.Out)]));}else if(c===(4)){BR(a,new CD(["match"]));}else if(c===(5)){BR(a,new CD(["fail"]));}else if(c===(6)){BR(a,new CD(["nop -> ",BT(b.Out)]));}else if(c===(7)){if(b.Rune===CA.nil){BR(a,new CD(["rune "]));}BR(a,new CD(["rune ",B.QuoteToASCII(($runesToString(b.Rune)))]));if(!((((((b.Arg<<16>>>16))&1)>>>0)===0))){BR(a,new CD(["/i"]));}BR(a,new CD([" -> ",BT(b.Out)]));}else if(c===(8)){BR(a,new CD(["rune1 ",B.QuoteToASCII(($runesToString(b.Rune)))," -> ",BT(b.Out)]));}else if(c===(9)){BR(a,new CD(["any -> ",BT(b.Out)]));}else if(c===(10)){BR(a,new CD(["anynotnl -> ",BT(b.Out)]));}};BV.ptr.prototype.Equal=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;b=this;if(b===CG.nil||a===CG.nil){return b===a;}if(!((b.Op===a.Op))){return false;}c=b.Op;if(c===(10)){if(!((((b.Flags&256)>>>0)===((a.Flags&256)>>>0)))){return false;}}else if((c===(3))||(c===(4))){if(!((b.Rune.$length===a.Rune.$length))){return false;}d=b.Rune;e=0;while(true){if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);if(!((g===(h=a.Rune,((f<0||f>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+f]))))){return false;}e++;}}else if((c===(19))||(c===(18))){if(!((b.Sub.$length===a.Sub.$length))){return false;}i=b.Sub;j=0;while(true){if(!(j=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]);if(!l.Equal((m=a.Sub,((k<0||k>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+k])))){return false;}j++;}}else if((c===(14))||(c===(15))||(c===(16))){if(!((((b.Flags&32)>>>0)===((a.Flags&32)>>>0)))||!(n=b.Sub,(0>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+0])).Equal((o=a.Sub,(0>=o.$length?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+0])))){return false;}}else if(c===(17)){if(!((((b.Flags&32)>>>0)===((a.Flags&32)>>>0)))||!((b.Min===a.Min))||!((b.Max===a.Max))||!(p=b.Sub,(0>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+0])).Equal((q=a.Sub,(0>=q.$length?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+0])))){return false;}}else if(c===(13)){if(!((b.Cap===a.Cap))||!(b.Name===a.Name)||!(r=b.Sub,(0>=r.$length?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+0])).Equal((s=a.Sub,(0>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+0])))){return false;}}return true;};BV.prototype.Equal=function(a){return this.$val.Equal(a);};BX=function(a,b){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;switch(0){default:c=b.Op;if(c===(1)){a.WriteString("[^\\x00-\\x{10FFFF}]");}else if(c===(2)){a.WriteString("(?:)");}else if(c===(3)){if(!((((b.Flags&1)>>>0)===0))){a.WriteString("(?i:");}d=b.Rune;e=0;while(true){if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);BY(a,f,false);e++;}if(!((((b.Flags&1)>>>0)===0))){a.WriteString(")");}}else if(c===(4)){if(!(((g=b.Rune.$length%2,g===g?g:$throwRuntimeError("integer divide by zero"))===0))){a.WriteString("[invalid char class]");break;}a.WriteRune(91);if(b.Rune.$length===0){a.WriteString("^\\x00-\\x{10FFFF}");}else if(((h=b.Rune,(0>=h.$length?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+0]))===0)&&((i=b.Rune,j=b.Rune.$length-1>>0,((j<0||j>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]))===1114111)){a.WriteRune(94);k=1;while(true){if(!(k<(b.Rune.$length-1>>0))){break;}l=(m=b.Rune,((k<0||k>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+k]))+1>>0;n=(o=b.Rune,p=k+1>>0,((p<0||p>=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]))-1>>0;q=l;r=n;BY(a,q,q===45);if(!((q===r))){a.WriteRune(45);BY(a,r,r===45);}k=k+(2)>>0;}}else{s=0;while(true){if(!(s=u.$length)?($throwRuntimeError("index out of range"),undefined):u.$array[u.$offset+s]));v=(w=b.Rune,x=s+1>>0,((x<0||x>=w.$length)?($throwRuntimeError("index out of range"),undefined):w.$array[w.$offset+x]));y=t;z=v;BY(a,y,y===45);if(!((y===z))){a.WriteRune(45);BY(a,z,z===45);}s=s+(2)>>0;}}a.WriteRune(93);}else if(c===(5)){a.WriteString("(?-s:.)");}else if(c===(6)){a.WriteString("(?s:.)");}else if(c===(7)){a.WriteString("(?m:^)");}else if(c===(8)){a.WriteString("(?m:$)");}else if(c===(9)){a.WriteString("\\A");}else if(c===(10)){if(!((((b.Flags&256)>>>0)===0))){a.WriteString("(?-m:$)");}else{a.WriteString("\\z");}}else if(c===(11)){a.WriteString("\\b");}else if(c===(12)){a.WriteString("\\B");}else if(c===(13)){if(!(b.Name==="")){a.WriteString("(?P<");a.WriteString(b.Name);a.WriteRune(62);}else{a.WriteRune(40);}if(!(((aa=b.Sub,(0>=aa.$length?($throwRuntimeError("index out of range"),undefined):aa.$array[aa.$offset+0])).Op===2))){BX(a,(ab=b.Sub,(0>=ab.$length?($throwRuntimeError("index out of range"),undefined):ab.$array[ab.$offset+0])));}a.WriteRune(41);}else if((c===(14))||(c===(15))||(c===(16))||(c===(17))){ad=(ac=b.Sub,(0>=ac.$length?($throwRuntimeError("index out of range"),undefined):ac.$array[ac.$offset+0]));if(ad.Op>13||(ad.Op===3)&&ad.Rune.$length>1){a.WriteString("(?:");BX(a,ad);a.WriteString(")");}else{BX(a,ad);}ae=b.Op;if(ae===(14)){a.WriteRune(42);}else if(ae===(15)){a.WriteRune(43);}else if(ae===(16)){a.WriteRune(63);}else if(ae===(17)){a.WriteRune(123);a.WriteString(B.Itoa(b.Min));if(!((b.Max===b.Min))){a.WriteRune(44);if(b.Max>=0){a.WriteString(B.Itoa(b.Max));}}a.WriteRune(125);}if(!((((b.Flags&32)>>>0)===0))){a.WriteRune(63);}}else if(c===(18)){af=b.Sub;ag=0;while(true){if(!(ag=af.$length)?($throwRuntimeError("index out of range"),undefined):af.$array[af.$offset+ag]);if(ah.Op===19){a.WriteString("(?:");BX(a,ah);a.WriteString(")");}else{BX(a,ah);}ag++;}}else if(c===(19)){ai=b.Sub;aj=0;while(true){if(!(aj=ai.$length)?($throwRuntimeError("index out of range"),undefined):ai.$array[ai.$offset+aj]);if(ak>0){a.WriteRune(124);}BX(a,al);aj++;}}else{a.WriteString(">0)))+">");}}};BV.ptr.prototype.String=function(){var a,b;a=this;b=new D.Builder.ptr(CM.nil,CN.nil);BX(b,a);return b.String();};BV.prototype.String=function(){return this.$val.String();};BY=function(a,b,c){var a,b,c,d,e;if(A.IsPrint(b)){if(D.ContainsRune("\\.+*?()|[]{}^$",b)||c){a.WriteRune(92);}a.WriteRune(b);return;}switch(0){default:d=b;if(d===(7)){a.WriteString("\\a");}else if(d===(12)){a.WriteString("\\f");}else if(d===(10)){a.WriteString("\\n");}else if(d===(13)){a.WriteString("\\r");}else if(d===(9)){a.WriteString("\\t");}else if(d===(11)){a.WriteString("\\v");}else{if(b<256){a.WriteString("\\x");e=B.FormatInt((new $Int64(0,b)),16);if(e.length===1){a.WriteRune(48);}a.WriteString(e);break;}a.WriteString("\\x{");a.WriteString(B.FormatInt((new $Int64(0,b)),16));a.WriteString("}");}}};BV.ptr.prototype.MaxCap=function(){var a,b,c,d,e,f;a=this;b=0;if(a.Op===13){b=a.Cap;}c=a.Sub;d=0;while(true){if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]);f=e.MaxCap();if(b>0));a.capNames(b);return b;};BV.prototype.CapNames=function(){return this.$val.CapNames();};BV.ptr.prototype.capNames=function(a){var a,b,c,d,e,f;b=this;if(b.Op===13){(c=b.Cap,((c<0||c>=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+c]=b.Name));}d=b.Sub;e=0;while(true){if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);f.capNames(a);e++;}};BV.prototype.capNames=function(a){return this.$val.capNames(a);};BV.ptr.prototype.Simplify=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;a=this;if(a===CG.nil){return CG.nil;}b=a.Op;if((b===(13))||(b===(18))||(b===(19))){c=a;d=a.Sub;e=0;while(true){if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);h=g.Simplify();if(c===a&&!(h===g)){c=new BV.ptr(0,0,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,"");BV.copy(c,a);c.Rune=CA.nil;c.Sub=$appendSlice($subslice(new CH(c.Sub0),0,0),$subslice(a.Sub,0,f));}if(!(c===a)){c.Sub=$append(c.Sub,h);}e++;}return c;}else if((b===(14))||(b===(15))||(b===(16))){j=(i=a.Sub,(0>=i.$length?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+0])).Simplify();return BZ(a.Op,a.Flags,j,a);}else if(b===(17)){if((a.Min===0)&&(a.Max===0)){return new BV.ptr(2,0,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,"");}l=(k=a.Sub,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0])).Simplify();if(a.Max===-1){if(a.Min===0){return BZ(14,a.Flags,l,CG.nil);}if(a.Min===1){return BZ(15,a.Flags,l,CG.nil);}m=new BV.ptr(18,0,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,"");m.Sub=$subslice(new CH(m.Sub0),0,0);n=0;while(true){if(!(n<(a.Min-1>>0))){break;}m.Sub=$append(m.Sub,l);n=n+(1)>>0;}m.Sub=$append(m.Sub,BZ(15,a.Flags,l,CG.nil));return m;}if((a.Min===1)&&(a.Max===1)){return l;}o=CG.nil;if(a.Min>0){o=new BV.ptr(18,0,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,"");o.Sub=$subslice(new CH(o.Sub0),0,0);p=0;while(true){if(!(p>0;}}if(a.Max>a.Min){q=BZ(16,a.Flags,l,CG.nil);r=a.Min+1>>0;while(true){if(!(r>0;}if(o===CG.nil){return q;}o.Sub=$append(o.Sub,q);}if(!(o===CG.nil)){return o;}return new BV.ptr(1,0,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,"");}return a;};BV.prototype.Simplify=function(){return this.$val.Simplify();};BZ=function(a,b,c,d){var a,b,c,d,e;if(c.Op===2){return c;}if((a===c.Op)&&(((b&32)>>>0)===((c.Flags&32)>>>0))){return c;}if(!(d===CG.nil)&&(d.Op===a)&&(((d.Flags&32)>>>0)===((b&32)>>>0))&&c===(e=d.Sub,(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]))){return d;}d=new BV.ptr(a,b,CH.nil,CI.zero(),CA.nil,CJ.zero(),0,0,0,"");d.Sub=$append($subslice(new CH(d.Sub0),0,0),c);return d;};F.methods=[{prop:"next",name:"next",pkg:"regexp/syntax",typ:$funcType([CE],[F],false)},{prop:"patch",name:"patch",pkg:"regexp/syntax",typ:$funcType([CE,$Uint32],[],false)},{prop:"append",name:"append",pkg:"regexp/syntax",typ:$funcType([CE,F],[F],false)}];CO.methods=[{prop:"init",name:"init",pkg:"regexp/syntax",typ:$funcType([],[],false)},{prop:"compile",name:"compile",pkg:"regexp/syntax",typ:$funcType([CG],[G],false)},{prop:"inst",name:"inst",pkg:"regexp/syntax",typ:$funcType([BL],[G],false)},{prop:"nop",name:"nop",pkg:"regexp/syntax",typ:$funcType([],[G],false)},{prop:"fail",name:"fail",pkg:"regexp/syntax",typ:$funcType([],[G],false)},{prop:"cap",name:"cap",pkg:"regexp/syntax",typ:$funcType([$Uint32],[G],false)},{prop:"cat",name:"cat",pkg:"regexp/syntax",typ:$funcType([G,G],[G],false)},{prop:"alt",name:"alt",pkg:"regexp/syntax",typ:$funcType([G,G],[G],false)},{prop:"quest",name:"quest",pkg:"regexp/syntax",typ:$funcType([G,$Bool],[G],false)},{prop:"star",name:"star",pkg:"regexp/syntax",typ:$funcType([G,$Bool],[G],false)},{prop:"plus",name:"plus",pkg:"regexp/syntax",typ:$funcType([G,$Bool],[G],false)},{prop:"empty",name:"empty",pkg:"regexp/syntax",typ:$funcType([BN],[G],false)},{prop:"rune",name:"rune",pkg:"regexp/syntax",typ:$funcType([CA,O],[G],false)}];CP.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];N.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];CQ.methods=[{prop:"newRegexp",name:"newRegexp",pkg:"regexp/syntax",typ:$funcType([BW],[CG],false)},{prop:"reuse",name:"reuse",pkg:"regexp/syntax",typ:$funcType([CG],[],false)},{prop:"push",name:"push",pkg:"regexp/syntax",typ:$funcType([CG],[CG],false)},{prop:"maybeConcat",name:"maybeConcat",pkg:"regexp/syntax",typ:$funcType([$Int32,O],[$Bool],false)},{prop:"newLiteral",name:"newLiteral",pkg:"regexp/syntax",typ:$funcType([$Int32,O],[CG],false)},{prop:"literal",name:"literal",pkg:"regexp/syntax",typ:$funcType([$Int32],[],false)},{prop:"op",name:"op",pkg:"regexp/syntax",typ:$funcType([BW],[CG],false)},{prop:"repeat",name:"repeat",pkg:"regexp/syntax",typ:$funcType([BW,$Int,$Int,$String,$String,$String],[$String,$error],false)},{prop:"concat",name:"concat",pkg:"regexp/syntax",typ:$funcType([],[CG],false)},{prop:"alternate",name:"alternate",pkg:"regexp/syntax",typ:$funcType([],[CG],false)},{prop:"collapse",name:"collapse",pkg:"regexp/syntax",typ:$funcType([CH,BW],[CG],false)},{prop:"factor",name:"factor",pkg:"regexp/syntax",typ:$funcType([CH],[CH],false)},{prop:"leadingString",name:"leadingString",pkg:"regexp/syntax",typ:$funcType([CG],[CA,O],false)},{prop:"removeLeadingString",name:"removeLeadingString",pkg:"regexp/syntax",typ:$funcType([CG,$Int],[CG],false)},{prop:"leadingRegexp",name:"leadingRegexp",pkg:"regexp/syntax",typ:$funcType([CG],[CG],false)},{prop:"removeLeadingRegexp",name:"removeLeadingRegexp",pkg:"regexp/syntax",typ:$funcType([CG,$Bool],[CG],false)},{prop:"parseRepeat",name:"parseRepeat",pkg:"regexp/syntax",typ:$funcType([$String],[$Int,$Int,$String,$Bool],false)},{prop:"parsePerlFlags",name:"parsePerlFlags",pkg:"regexp/syntax",typ:$funcType([$String],[$String,$error],false)},{prop:"parseInt",name:"parseInt",pkg:"regexp/syntax",typ:$funcType([$String],[$Int,$String,$Bool],false)},{prop:"parseVerticalBar",name:"parseVerticalBar",pkg:"regexp/syntax",typ:$funcType([],[$error],false)},{prop:"swapVerticalBar",name:"swapVerticalBar",pkg:"regexp/syntax",typ:$funcType([],[$Bool],false)},{prop:"parseRightParen",name:"parseRightParen",pkg:"regexp/syntax",typ:$funcType([],[$error],false)},{prop:"parseEscape",name:"parseEscape",pkg:"regexp/syntax",typ:$funcType([$String],[$Int32,$String,$error],false)},{prop:"parseClassChar",name:"parseClassChar",pkg:"regexp/syntax",typ:$funcType([$String,$String],[$Int32,$String,$error],false)},{prop:"parsePerlClassEscape",name:"parsePerlClassEscape",pkg:"regexp/syntax",typ:$funcType([$String,CA],[CA,$String],false)},{prop:"parseNamedClass",name:"parseNamedClass",pkg:"regexp/syntax",typ:$funcType([$String,CA],[CA,$String,$error],false)},{prop:"appendGroup",name:"appendGroup",pkg:"regexp/syntax",typ:$funcType([CA,Z],[CA],false)},{prop:"parseUnicodeClass",name:"parseUnicodeClass",pkg:"regexp/syntax",typ:$funcType([$String,CA],[CA,$String,$error],false)},{prop:"parseClass",name:"parseClass",pkg:"regexp/syntax",typ:$funcType([$String],[$String,$error],false)}];AM.methods=[{prop:"Less",name:"Less",pkg:"",typ:$funcType([$Int,$Int],[$Bool],false)},{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Swap",name:"Swap",pkg:"",typ:$funcType([$Int,$Int],[],false)}];CE.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"skipNop",name:"skipNop",pkg:"regexp/syntax",typ:$funcType([$Uint32],[CR],false)},{prop:"Prefix",name:"Prefix",pkg:"",typ:$funcType([],[$String,$Bool],false)},{prop:"StartCond",name:"StartCond",pkg:"",typ:$funcType([],[BN],false)}];BL.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];CR.methods=[{prop:"op",name:"op",pkg:"regexp/syntax",typ:$funcType([],[BL],false)},{prop:"MatchRune",name:"MatchRune",pkg:"",typ:$funcType([$Int32],[$Bool],false)},{prop:"MatchRunePos",name:"MatchRunePos",pkg:"",typ:$funcType([$Int32],[$Int],false)},{prop:"MatchEmptyWidth",name:"MatchEmptyWidth",pkg:"",typ:$funcType([$Int32,$Int32],[$Bool],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];CG.methods=[{prop:"Equal",name:"Equal",pkg:"",typ:$funcType([CG],[$Bool],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"MaxCap",name:"MaxCap",pkg:"",typ:$funcType([],[$Int],false)},{prop:"CapNames",name:"CapNames",pkg:"",typ:$funcType([],[CD],false)},{prop:"capNames",name:"capNames",pkg:"regexp/syntax",typ:$funcType([CD],[],false)},{prop:"Simplify",name:"Simplify",pkg:"",typ:$funcType([],[CG],false)}];BW.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];G.init("regexp/syntax",[{prop:"i",name:"i",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"out",name:"out",embedded:false,exported:false,typ:F,tag:""}]);H.init("regexp/syntax",[{prop:"p",name:"p",embedded:false,exported:false,typ:CE,tag:""}]);M.init("",[{prop:"Code",name:"Code",embedded:false,exported:true,typ:N,tag:""},{prop:"Expr",name:"Expr",embedded:false,exported:true,typ:$String,tag:""}]);P.init("regexp/syntax",[{prop:"flags",name:"flags",embedded:false,exported:false,typ:O,tag:""},{prop:"stack",name:"stack",embedded:false,exported:false,typ:CH,tag:""},{prop:"free",name:"free",embedded:false,exported:false,typ:CG,tag:""},{prop:"numCap",name:"numCap",embedded:false,exported:false,typ:$Int,tag:""},{prop:"wholeRegexp",name:"wholeRegexp",embedded:false,exported:false,typ:$String,tag:""},{prop:"tmpClass",name:"tmpClass",embedded:false,exported:false,typ:CA,tag:""}]);Z.init("regexp/syntax",[{prop:"sign",name:"sign",embedded:false,exported:false,typ:$Int,tag:""},{prop:"class$1",name:"class",embedded:false,exported:false,typ:CA,tag:""}]);AM.init("regexp/syntax",[{prop:"p",name:"p",embedded:false,exported:false,typ:CK,tag:""}]);BK.init("",[{prop:"Inst",name:"Inst",embedded:false,exported:true,typ:CF,tag:""},{prop:"Start",name:"Start",embedded:false,exported:true,typ:$Int,tag:""},{prop:"NumCap",name:"NumCap",embedded:false,exported:true,typ:$Int,tag:""}]);BQ.init("",[{prop:"Op",name:"Op",embedded:false,exported:true,typ:BL,tag:""},{prop:"Out",name:"Out",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Arg",name:"Arg",embedded:false,exported:true,typ:$Uint32,tag:""},{prop:"Rune",name:"Rune",embedded:false,exported:true,typ:CA,tag:""}]);BV.init("",[{prop:"Op",name:"Op",embedded:false,exported:true,typ:BW,tag:""},{prop:"Flags",name:"Flags",embedded:false,exported:true,typ:O,tag:""},{prop:"Sub",name:"Sub",embedded:false,exported:true,typ:CH,tag:""},{prop:"Sub0",name:"Sub0",embedded:false,exported:true,typ:CI,tag:""},{prop:"Rune",name:"Rune",embedded:false,exported:true,typ:CA,tag:""},{prop:"Rune0",name:"Rune0",embedded:false,exported:true,typ:CJ,tag:""},{prop:"Min",name:"Min",embedded:false,exported:true,typ:$Int,tag:""},{prop:"Max",name:"Max",embedded:false,exported:true,typ:$Int,tag:""},{prop:"Cap",name:"Cap",embedded:false,exported:true,typ:$Int,tag:""},{prop:"Name",name:"Name",embedded:false,exported:true,typ:$String,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=C.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}J=new CA([0,9,11,1114111]);K=new CA([0,1114111]);L=$toNativeArray($kindUint8,[0,7,17,24,33,45,52,61,68,77,84,96,110,117,121,125,130,136,142,151]);AA=new A.RangeTable.ptr(new CB([new A.Range16.ptr(0,65535,1)]),new CC([new A.Range32.ptr(65536,1114111,1)]),0);AR=new CA([48,57]);AS=new CA([9,10,12,13,32,32]);AT=new CA([48,57,65,90,95,95,97,122]);AU=$makeMap($String.keyFor,[{k:"\\d",v:new Z.ptr(1,AR)},{k:"\\D",v:new Z.ptr(-1,AR)},{k:"\\s",v:new Z.ptr(1,AS)},{k:"\\S",v:new Z.ptr(-1,AS)},{k:"\\w",v:new Z.ptr(1,AT)},{k:"\\W",v:new Z.ptr(-1,AT)}]);AV=new CA([48,57,65,90,97,122]);AW=new CA([65,90,97,122]);AX=new CA([0,127]);AY=new CA([9,9,32,32]);AZ=new CA([0,31,127,127]);BA=new CA([48,57]);BB=new CA([33,126]);BC=new CA([97,122]);BD=new CA([32,126]);BE=new CA([33,47,58,64,91,96,123,126]);BF=new CA([9,13,32,32]);BG=new CA([65,90]);BH=new CA([48,57,65,90,95,95,97,122]);BI=new CA([48,57,65,70,97,102]);BJ=$makeMap($String.keyFor,[{k:"[:alnum:]",v:new Z.ptr(1,AV)},{k:"[:^alnum:]",v:new Z.ptr(-1,AV)},{k:"[:alpha:]",v:new Z.ptr(1,AW)},{k:"[:^alpha:]",v:new Z.ptr(-1,AW)},{k:"[:ascii:]",v:new Z.ptr(1,AX)},{k:"[:^ascii:]",v:new Z.ptr(-1,AX)},{k:"[:blank:]",v:new Z.ptr(1,AY)},{k:"[:^blank:]",v:new Z.ptr(-1,AY)},{k:"[:cntrl:]",v:new Z.ptr(1,AZ)},{k:"[:^cntrl:]",v:new Z.ptr(-1,AZ)},{k:"[:digit:]",v:new Z.ptr(1,BA)},{k:"[:^digit:]",v:new Z.ptr(-1,BA)},{k:"[:graph:]",v:new Z.ptr(1,BB)},{k:"[:^graph:]",v:new Z.ptr(-1,BB)},{k:"[:lower:]",v:new Z.ptr(1,BC)},{k:"[:^lower:]",v:new Z.ptr(-1,BC)},{k:"[:print:]",v:new Z.ptr(1,BD)},{k:"[:^print:]",v:new Z.ptr(-1,BD)},{k:"[:punct:]",v:new Z.ptr(1,BE)},{k:"[:^punct:]",v:new Z.ptr(-1,BE)},{k:"[:space:]",v:new Z.ptr(1,BF)},{k:"[:^space:]",v:new Z.ptr(-1,BF)},{k:"[:upper:]",v:new Z.ptr(1,BG)},{k:"[:^upper:]",v:new Z.ptr(-1,BG)},{k:"[:word:]",v:new Z.ptr(1,BH)},{k:"[:^word:]",v:new Z.ptr(-1,BH)},{k:"[:xdigit:]",v:new Z.ptr(1,BI)},{k:"[:^xdigit:]",v:new Z.ptr(-1,BI)}]);BM=new CD(["InstAlt","InstAltMatch","InstCapture","InstEmptyWidth","InstMatch","InstFail","InstNop","InstRune","InstRune1","InstRuneAny","InstRuneAnyNotNL"]);}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["regexp"]=(function(){var $pkg={},$init,G,B,C,A,D,H,E,F,I,J,K,Q,R,S,T,U,V,X,AC,AD,AH,AO,AT,BC,BD,BE,BF,BO,BP,BQ,BR,BS,BT,BU,BV,BW,BX,BY,BZ,CA,CB,CC,CD,CE,CF,CG,CH,CI,CJ,CK,CL,CM,CN,CO,CP,CQ,CR,CS,CT,CU,CV,CW,CX,CY,CZ,DA,DB,DC,DD,DE,DF,L,Y,AB,AJ,AK,AP,AQ,AX,AY,BJ,M,N,O,P,W,Z,AA,AE,AF,AG,AI,AL,AM,AN,AR,AS,AU,AW,AZ,BB,BL,BN;G=$packages["bytes"];B=$packages["github.com/gopherjs/gopherjs/nosync"];C=$packages["io"];A=$packages["regexp/syntax"];D=$packages["sort"];H=$packages["strconv"];E=$packages["strings"];F=$packages["unicode"];I=$packages["unicode/utf8"];J=$pkg.job=$newType(0,$kindStruct,"regexp.job",true,"regexp",false,function(pc_,arg_,pos_){this.$val=this;if(arguments.length===0){this.pc=0;this.arg=false;this.pos=0;return;}this.pc=pc_;this.arg=arg_;this.pos=pos_;});K=$pkg.bitState=$newType(0,$kindStruct,"regexp.bitState",true,"regexp",false,function(end_,cap_,matchcap_,jobs_,visited_,inputs_){this.$val=this;if(arguments.length===0){this.end=0;this.cap=BV.nil;this.matchcap=BV.nil;this.jobs=BW.nil;this.visited=BT.nil;this.inputs=new U.ptr(new BE.ptr(BX.nil),new BD.ptr(""),new BF.ptr($ifaceNil,false,0));return;}this.end=end_;this.cap=cap_;this.matchcap=matchcap_;this.jobs=jobs_;this.visited=visited_;this.inputs=inputs_;});Q=$pkg.queue=$newType(0,$kindStruct,"regexp.queue",true,"regexp",false,function(sparse_,dense_){this.$val=this;if(arguments.length===0){this.sparse=BT.nil;this.dense=CP.nil;return;}this.sparse=sparse_;this.dense=dense_;});R=$pkg.entry=$newType(0,$kindStruct,"regexp.entry",true,"regexp",false,function(pc_,t_){this.$val=this;if(arguments.length===0){this.pc=0;this.t=BY.nil;return;}this.pc=pc_;this.t=t_;});S=$pkg.thread=$newType(0,$kindStruct,"regexp.thread",true,"regexp",false,function(inst_,cap_){this.$val=this;if(arguments.length===0){this.inst=BZ.nil;this.cap=BV.nil;return;}this.inst=inst_;this.cap=cap_;});T=$pkg.machine=$newType(0,$kindStruct,"regexp.machine",true,"regexp",false,function(re_,p_,q0_,q1_,pool_,matched_,matchcap_,inputs_){this.$val=this;if(arguments.length===0){this.re=CL.nil;this.p=CM.nil;this.q0=new Q.ptr(BT.nil,CP.nil);this.q1=new Q.ptr(BT.nil,CP.nil);this.pool=CQ.nil;this.matched=false;this.matchcap=BV.nil;this.inputs=new U.ptr(new BE.ptr(BX.nil),new BD.ptr(""),new BF.ptr($ifaceNil,false,0));return;}this.re=re_;this.p=p_;this.q0=q0_;this.q1=q1_;this.pool=pool_;this.matched=matched_;this.matchcap=matchcap_;this.inputs=inputs_;});U=$pkg.inputs=$newType(0,$kindStruct,"regexp.inputs",true,"regexp",false,function(bytes_,string_,reader_){this.$val=this;if(arguments.length===0){this.bytes=new BE.ptr(BX.nil);this.string=new BD.ptr("");this.reader=new BF.ptr($ifaceNil,false,0);return;}this.bytes=bytes_;this.string=string_;this.reader=reader_;});V=$pkg.lazyFlag=$newType(8,$kindUint64,"regexp.lazyFlag",true,"regexp",false,null);X=$pkg.onePassMachine=$newType(0,$kindStruct,"regexp.onePassMachine",true,"regexp",false,function(inputs_,matchcap_){this.$val=this;if(arguments.length===0){this.inputs=new U.ptr(new BE.ptr(BX.nil),new BD.ptr(""),new BF.ptr($ifaceNil,false,0));this.matchcap=BV.nil;return;}this.inputs=inputs_;this.matchcap=matchcap_;});AC=$pkg.onePassProg=$newType(0,$kindStruct,"regexp.onePassProg",true,"regexp",false,function(Inst_,Start_,NumCap_){this.$val=this;if(arguments.length===0){this.Inst=CG.nil;this.Start=0;this.NumCap=0;return;}this.Inst=Inst_;this.Start=Start_;this.NumCap=NumCap_;});AD=$pkg.onePassInst=$newType(0,$kindStruct,"regexp.onePassInst",true,"regexp",false,function(Inst_,Next_){this.$val=this;if(arguments.length===0){this.Inst=new A.Inst.ptr(0,0,0,BS.nil);this.Next=BT.nil;return;}this.Inst=Inst_;this.Next=Next_;});AH=$pkg.queueOnePass=$newType(0,$kindStruct,"regexp.queueOnePass",true,"regexp",false,function(sparse_,dense_,size_,nextIndex_){this.$val=this;if(arguments.length===0){this.sparse=BT.nil;this.dense=BT.nil;this.size=0;this.nextIndex=0;return;}this.sparse=sparse_;this.dense=dense_;this.size=size_;this.nextIndex=nextIndex_;});AO=$pkg.runeSlice=$newType(12,$kindSlice,"regexp.runeSlice",true,"regexp",false,null);AT=$pkg.Regexp=$newType(0,$kindStruct,"regexp.Regexp",true,"regexp",true,function(expr_,prog_,onepass_,numSubexp_,maxBitStateLen_,subexpNames_,prefix_,prefixBytes_,prefixRune_,prefixEnd_,mpool_,matchcap_,prefixComplete_,cond_,longest_){this.$val=this;if(arguments.length===0){this.expr="";this.prog=CM.nil;this.onepass=CD.nil;this.numSubexp=0;this.maxBitStateLen=0;this.subexpNames=CN.nil;this.prefix="";this.prefixBytes=BX.nil;this.prefixRune=0;this.prefixEnd=0;this.mpool=0;this.matchcap=0;this.prefixComplete=false;this.cond=0;this.longest=false;return;}this.expr=expr_;this.prog=prog_;this.onepass=onepass_;this.numSubexp=numSubexp_;this.maxBitStateLen=maxBitStateLen_;this.subexpNames=subexpNames_;this.prefix=prefix_;this.prefixBytes=prefixBytes_;this.prefixRune=prefixRune_;this.prefixEnd=prefixEnd_;this.mpool=mpool_;this.matchcap=matchcap_;this.prefixComplete=prefixComplete_;this.cond=cond_;this.longest=longest_;});BC=$pkg.input=$newType(8,$kindInterface,"regexp.input",true,"regexp",false,null);BD=$pkg.inputString=$newType(0,$kindStruct,"regexp.inputString",true,"regexp",false,function(str_){this.$val=this;if(arguments.length===0){this.str="";return;}this.str=str_;});BE=$pkg.inputBytes=$newType(0,$kindStruct,"regexp.inputBytes",true,"regexp",false,function(str_){this.$val=this;if(arguments.length===0){this.str=BX.nil;return;}this.str=str_;});BF=$pkg.inputReader=$newType(0,$kindStruct,"regexp.inputReader",true,"regexp",false,function(r_,atEOT_,pos_){this.$val=this;if(arguments.length===0){this.r=$ifaceNil;this.atEOT=false;this.pos=0;return;}this.r=r_;this.atEOT=atEOT_;this.pos=pos_;});BO=$sliceType($emptyInterface);BP=$arrayType($Int,0);BQ=$arrayType(B.Pool,5);BR=$arrayType($Uint8,16);BS=$sliceType($Int32);BT=$sliceType($Uint32);BU=$ptrType(K);BV=$sliceType($Int);BW=$sliceType(J);BX=$sliceType($Uint8);BY=$ptrType(S);BZ=$ptrType(A.Inst);CA=$ptrType(V);CB=$ptrType($Int);CC=$ptrType(X);CD=$ptrType(AC);CE=$ptrType(E.Builder);CF=$ptrType(AH);CG=$sliceType(AD);CH=$ptrType($Uint32);CI=$sliceType(BS);CJ=$ptrType(BS);CK=$sliceType($Bool);CL=$ptrType(AT);CM=$ptrType(A.Prog);CN=$sliceType($String);CO=$ptrType(T);CP=$sliceType(R);CQ=$sliceType(BY);CR=$arrayType($Int,2);CS=$arrayType($Int,4);CT=$sliceType(BX);CU=$sliceType(BV);CV=$sliceType(CT);CW=$sliceType(CN);CX=$ptrType(Q);CY=$ptrType(U);CZ=$funcType([$String],[$String],false);DA=$funcType([BX,BV],[BX],false);DB=$funcType([BX],[BX],false);DC=$funcType([BV],[],false);DD=$ptrType(BD);DE=$ptrType(BE);DF=$ptrType(BF);M=function(){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=L.Get();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}a=$assertType(b,BU,true);c=a[0];d=a[1];if(!d){c=new K.ptr(0,BV.nil,BV.nil,BW.nil,BT.nil,new U.ptr(new BE.ptr(BX.nil),new BD.ptr(""),new BF.ptr($ifaceNil,false,0)));}$s=-1;return c;}return;}if($f===undefined){$f={$blk:M};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};N=function(a){var a;a.inputs.clear();L.Put(a);};O=function(a){var a,b;if(!P(a)){return 0;}return(b=262144/a.Inst.$length,(b===b&&b!==1/0&&b!==-1/0)?b>>0:$throwRuntimeError("integer divide by zero"));};P=function(a){var a;return a.Inst.$length<=500;};K.ptr.prototype.reset=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;d=this;d.end=b;if(d.jobs.$capacity===0){d.jobs=$makeSlice(BW,0,256);}else{d.jobs=$subslice(d.jobs,0,0);}f=(e=(((($imul(a.Inst.$length,((b+1>>0))))+32>>0)-1>>0))/32,(e===e&&e!==1/0&&e!==-1/0)?e>>0:$throwRuntimeError("integer divide by zero"));if(d.visited.$capacity=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+i]=0));h++;}}if(d.cap.$capacity=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+m]=-1));l++;}if(d.matchcap.$capacity=r.$length)?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+q]=-1));p++;}};K.prototype.reset=function(a,b,c){return this.$val.reset(a,b,c);};K.ptr.prototype.shouldVisit=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m;c=this;d=(((($imul(((a>>0)),((c.end+1>>0))))+b>>0)>>>0));if(!(((((e=c.visited,f=(g=d/32,(g===g&&g!==1/0&&g!==-1/0)?g>>>0:$throwRuntimeError("integer divide by zero")),((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]))&(((h=(((d&31)>>>0)),h<32?(1<>>0)))>>>0)===0))){return false;}j=(i=d/32,(i===i&&i!==1/0&&i!==-1/0)?i>>>0:$throwRuntimeError("integer divide by zero"));(m=c.visited,((j<0||j>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+j]=(((k=c.visited,((j<0||j>=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+j]))|(((l=(((d&31)>>>0)),l<32?(1<>>0)))>>>0)));return true;};K.prototype.shouldVisit=function(a,b){return this.$val.shouldVisit(a,b);};K.ptr.prototype.push=function(a,b,c,d){var a,b,c,d,e,f;e=this;if(!(((f=a.prog.Inst,((b<0||b>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+b])).Op===5))&&(d||e.shouldVisit(b,c))){e.jobs=$append(e.jobs,new J.ptr(b,d,c));}};K.prototype.push=function(a,b,c,d){return this.$val.push(a,b,c,d);};AT.ptr.prototype.tryBacktrack=function(a,b,c,d){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;f=e.longest;a.push(e,c,d,false);case 1:if(!(a.jobs.$length>0)){$s=2;continue;}g=a.jobs.$length-1>>0;i=(h=a.jobs,((g<0||g>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+g])).pc;k=(j=a.jobs,((g<0||g>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+g])).pos;m=(l=a.jobs,((g<0||g>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+g])).arg;a.jobs=$subslice(a.jobs,0,g);$s=3;continue;case 4:if(!a.shouldVisit(i,k)){$s=1;continue;}case 3:o=$clone((n=e.prog.Inst,((i<0||i>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+i])),A.Inst);p=o.Op;if(p===(5)){$s=6;continue;}if(p===(0)){$s=7;continue;}if(p===(1)){$s=8;continue;}if(p===(7)){$s=9;continue;}if(p===(8)){$s=10;continue;}if(p===(10)){$s=11;continue;}if(p===(9)){$s=12;continue;}if(p===(2)){$s=13;continue;}if(p===(3)){$s=14;continue;}if(p===(6)){$s=15;continue;}if(p===(4)){$s=16;continue;}$s=17;continue;case 6:$panic(new $String("unexpected InstFail"));$s=18;continue;case 7:if(m){$s=19;continue;}$s=20;continue;case 19:m=false;i=o.Arg;$s=4;continue;$s=21;continue;case 20:a.push(e,i,k,true);i=o.Out;$s=4;continue;case 21:$s=18;continue;case 8:s=(q=e.prog.Inst,r=o.Out,((r<0||r>=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+r])).Op;if((s===(7))||(s===(8))||(s===(9))||(s===(10))){$s=23;continue;}$s=24;continue;case 23:a.push(e,o.Arg,k,false);i=o.Arg;k=a.end;$s=4;continue;case 24:case 22:a.push(e,o.Out,a.end,false);i=o.Out;$s=4;continue;$s=18;continue;case 9:u=b.step(k);$s=25;case 25:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}t=u;v=t[0];w=t[1];if(!o.MatchRune(v)){$s=26;continue;}$s=27;continue;case 26:$s=1;continue;case 27:k=k+(w)>>0;i=o.Out;$s=4;continue;$s=18;continue;case 10:y=b.step(k);$s=28;case 28:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}x=y;z=x[0];aa=x[1];if(!((z===(ab=o.Rune,(0>=ab.$length?($throwRuntimeError("index out of range"),undefined):ab.$array[ab.$offset+0]))))){$s=29;continue;}$s=30;continue;case 29:$s=1;continue;case 30:k=k+(aa)>>0;i=o.Out;$s=4;continue;$s=18;continue;case 11:ad=b.step(k);$s=31;case 31:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}ac=ad;ae=ac[0];af=ac[1];if((ae===10)||(ae===-1)){$s=32;continue;}$s=33;continue;case 32:$s=1;continue;case 33:k=k+(af)>>0;i=o.Out;$s=4;continue;$s=18;continue;case 12:ah=b.step(k);$s=34;case 34:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}ag=ah;ai=ag[0];aj=ag[1];if(ai===-1){$s=35;continue;}$s=36;continue;case 35:$s=1;continue;case 36:k=k+(aj)>>0;i=o.Out;$s=4;continue;$s=18;continue;case 13:if(m){$s=37;continue;}$s=38;continue;case 37:(ak=a.cap,al=o.Arg,((al<0||al>=ak.$length)?($throwRuntimeError("index out of range"),undefined):ak.$array[ak.$offset+al]=k));$s=1;continue;$s=39;continue;case 38:if(0<=o.Arg&&o.Arg<((a.cap.$length>>>0))){a.push(e,i,(am=a.cap,an=o.Arg,((an<0||an>=am.$length)?($throwRuntimeError("index out of range"),undefined):am.$array[am.$offset+an])),true);(ao=a.cap,ap=o.Arg,((ap<0||ap>=ao.$length)?($throwRuntimeError("index out of range"),undefined):ao.$array[ao.$offset+ap]=k));}i=o.Out;$s=4;continue;case 39:$s=18;continue;case 14:aq=b.context(k);$s=40;case 40:if($c){$c=false;aq=aq.$blk();}if(aq&&aq.$blk!==undefined){break s;}ar=aq;if(!ar.match(((o.Arg<<24>>>24)))){$s=41;continue;}$s=42;continue;case 41:$s=1;continue;case 42:i=o.Out;$s=4;continue;$s=18;continue;case 15:i=o.Out;$s=4;continue;$s=18;continue;case 16:if(a.cap.$length===0){$s=-1;return true;}if(a.cap.$length>1){(as=a.cap,(1>=as.$length?($throwRuntimeError("index out of range"),undefined):as.$array[as.$offset+1]=k));}au=(at=a.matchcap,(1>=at.$length?($throwRuntimeError("index out of range"),undefined):at.$array[at.$offset+1]));if((au===-1)||(f&&k>0&&k>au)){$copySlice(a.matchcap,a.cap);}if(!f){$s=-1;return true;}if(k===a.end){$s=-1;return true;}$s=1;continue;$s=18;continue;case 17:$panic(new $String("bad inst"));case 18:case 5:$s=1;continue;case 2:$s=-1;return f&&a.matchcap.$length>1&&(av=a.matchcap,(1>=av.$length?($throwRuntimeError("index out of range"),undefined):av.$array[av.$offset+1]))>=0;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.tryBacktrack};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.tryBacktrack=function(a,b,c,d){return this.$val.tryBacktrack(a,b,c,d);};AT.ptr.prototype.backtrack=function(a,b,c,d,e){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:f=this;g=f.cond;if(g===255){$s=-1;return BV.nil;}if(!((((g&4)>>>0)===0))&&!((c===0))){$s=-1;return BV.nil;}h=M();$s=1;case 1:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}i=h;j=i.inputs.init($ifaceNil,a,b);k=j[0];l=j[1];i.reset(f.prog,l,d);if(!((((g&4)>>>0)===0))){$s=2;continue;}$s=3;continue;case 2:if(i.cap.$length>0){(m=i.cap,(0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0]=c));}n=f.tryBacktrack(i,k,((f.prog.Start>>>0)),c);$s=7;case 7:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}if(!n){$s=5;continue;}$s=6;continue;case 5:N(i);$s=-1;return BV.nil;case 6:$s=4;continue;case 3:o=-1;case 8:if(!(c<=l&&!((o===0)))){$s=9;continue;}if(f.prefix.length>0){$s=10;continue;}$s=11;continue;case 10:p=k.index(f,c);$s=12;case 12:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=p;if(q<0){N(i);$s=-1;return BV.nil;}c=c+(q)>>0;case 11:if(i.cap.$length>0){(r=i.cap,(0>=r.$length?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+0]=c));}s=f.tryBacktrack(i,k,((f.prog.Start>>>0)),c);$s=15;case 15:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}if(s){$s=13;continue;}$s=14;continue;case 13:$s=16;continue;case 14:u=k.step(c);$s=17;case 17:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}t=u;o=t[1];c=c+(o)>>0;$s=8;continue;case 9:N(i);$s=-1;return BV.nil;case 4:case 16:e=$appendSlice(e,i.matchcap);N(i);$s=-1;return e;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.backtrack};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.backtrack=function(a,b,c,d,e){return this.$val.backtrack(a,b,c,d,e);};U.ptr.prototype.newBytes=function(a){var a,b;b=this;b.bytes.str=a;return b.bytes;};U.prototype.newBytes=function(a){return this.$val.newBytes(a);};U.ptr.prototype.newString=function(a){var a,b;b=this;b.string.str=a;return b.string;};U.prototype.newString=function(a){return this.$val.newString(a);};U.ptr.prototype.newReader=function(a){var a,b;b=this;b.reader.r=a;b.reader.atEOT=false;b.reader.pos=0;return b.reader;};U.prototype.newReader=function(a){return this.$val.newReader(a);};U.ptr.prototype.clear=function(){var a;a=this;if(!(a.bytes.str===BX.nil)){a.bytes.str=BX.nil;}else if(!($interfaceIsEqual(a.reader.r,$ifaceNil))){a.reader.r=$ifaceNil;}else{a.string.str="";}};U.prototype.clear=function(){return this.$val.clear();};U.ptr.prototype.init=function(a,b,c){var a,b,c,d;d=this;if(!($interfaceIsEqual(a,$ifaceNil))){return[d.newReader(a),0];}if(!(b===BX.nil)){return[d.newBytes(b),b.$length];}return[d.newString(c),c.length];};U.prototype.init=function(a,b,c){return this.$val.init(a,b,c);};T.ptr.prototype.init=function(a){var a,b,c,d,e;b=this;c=b.pool;d=0;while(true){if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]);e.cap=$subslice(e.cap,0,a);d++;}b.matchcap=$subslice(b.matchcap,0,a);};T.prototype.init=function(a){return this.$val.init(a);};T.ptr.prototype.alloc=function(a){var a,b,c,d,e,f;b=this;c=BY.nil;d=b.pool.$length;if(d>0){c=(e=b.pool,f=d-1>>0,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]));b.pool=$subslice(b.pool,0,(d-1>>0));}else{c=new S.ptr(BZ.nil,BV.nil);c.cap=$makeSlice(BV,b.matchcap.$length,b.matchcap.$capacity);}c.inst=a;return c;};T.prototype.alloc=function(a){return this.$val.alloc(a);};W=function(a,b){var a,b,c,d,e;return((c=(d=$shiftLeft64((new $Uint64(0,a)),32),e=(new $Uint64(0,((b>>>0)))),new $Uint64(d.$high|e.$high,(d.$low|e.$low)>>>0)),new V(c.$high,c.$low)));};V.prototype.match=function(a){var a,b,c,d;b=this;if(a===0){return true;}c=(($shiftRightUint64(b,32).$low>>0));if(!((((a&1)>>>0)===0))){if(!((c===10))&&c>=0){return false;}a=(a&~(1))<<24>>>24;}if(!((((a&4)>>>0)===0))){if(c>=0){return false;}a=(a&~(4))<<24>>>24;}if(a===0){return true;}d=((b.$low>>0));if(!((((a&2)>>>0)===0))){if(!((d===10))&&d>=0){return false;}a=(a&~(2))<<24>>>24;}if(!((((a&8)>>>0)===0))){if(d>=0){return false;}a=(a&~(8))<<24>>>24;}if(a===0){return true;}if(!(A.IsWordChar(c)===A.IsWordChar(d))){a=(a&~(16))<<24>>>24;}else{a=(a&~(32))<<24>>>24;}return a===0;};$ptrType(V).prototype.match=function(a){return this.$get().match(a);};T.ptr.prototype.match=function(a,b){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=[c];d=this;e=d.re.cond;if(e===255){$s=-1;return false;}d.matched=false;f=d.matchcap;g=0;while(true){if(!(g=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+h]=-1));g++;}j=d.q0;k=d.q1;l=j;m=k;n=-1;o=-1;p=n;q=o;r=0;s=0;t=r;u=s;w=a.step(b);$s=1;case 1:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}v=w;p=v[0];t=v[1];if(!((p===-1))){$s=2;continue;}$s=3;continue;case 2:y=a.step(b+t>>0);$s=4;case 4:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}x=y;q=x[0];u=x[1];case 3:c[0]=new V(0,0);if(b===0){$s=5;continue;}$s=6;continue;case 5:c[0]=W(-1,p);$s=7;continue;case 6:z=a.context(b);$s=8;case 8:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}c[0]=z;case 7:case 9:if(l.dense.$length===0){$s=11;continue;}$s=12;continue;case 11:if(!((((e&4)>>>0)===0))&&!((b===0))){$s=10;continue;}if(d.matched){$s=10;continue;}if(!(d.re.prefix.length>0&&!((q===d.re.prefixRune)))){aa=false;$s=15;continue s;}ab=a.canCheckPrefix();$s=16;case 16:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}aa=ab;case 15:if(aa){$s=13;continue;}$s=14;continue;case 13:ac=a.index(d.re,b);$s=17;case 17:if($c){$c=false;ac=ac.$blk();}if(ac&&ac.$blk!==undefined){break s;}ad=ac;if(ad<0){$s=10;continue;}b=b+(ad)>>0;af=a.step(b);$s=18;case 18:if($c){$c=false;af=af.$blk();}if(af&&af.$blk!==undefined){break s;}ae=af;p=ae[0];t=ae[1];ah=a.step(b+t>>0);$s=19;case 19:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}ag=ah;q=ag[0];u=ag[1];case 14:case 12:if(!d.matched){if(d.matchcap.$length>0){(ai=d.matchcap,(0>=ai.$length?($throwRuntimeError("index out of range"),undefined):ai.$array[ai.$offset+0]=b));}d.add(l,((d.p.Start>>>0)),b,d.matchcap,(c.$ptr||(c.$ptr=new CA(function(){return this.$target[0];},function($v){this.$target[0]=$v;},c))),BY.nil);}c[0]=W(p,q);d.step(l,m,b,b+t>>0,p,(c.$ptr||(c.$ptr=new CA(function(){return this.$target[0];},function($v){this.$target[0]=$v;},c))));if(t===0){$s=10;continue;}if((d.matchcap.$length===0)&&d.matched){$s=10;continue;}b=b+(t)>>0;aj=q;ak=u;p=aj;t=ak;if(!((p===-1))){$s=20;continue;}$s=21;continue;case 20:am=a.step(b+t>>0);$s=22;case 22:if($c){$c=false;am=am.$blk();}if(am&&am.$blk!==undefined){break s;}al=am;q=al[0];u=al[1];case 21:an=m;ao=l;l=an;m=ao;$s=9;continue;case 10:d.clear(m);$s=-1;return d.matched;}return;}if($f===undefined){$f={$blk:T.ptr.prototype.match};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};T.prototype.match=function(a,b){return this.$val.match(a,b);};T.ptr.prototype.clear=function(a){var a,b,c,d,e;b=this;c=a.dense;d=0;while(true){if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]),R);if(!(e.t===BY.nil)){b.pool=$append(b.pool,e.t);}d++;}a.dense=$subslice(a.dense,0,0);};T.prototype.clear=function(a){return this.$val.clear(a);};T.ptr.prototype.step=function(a,b,c,d,e,f){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w;g=this;h=g.re.longest;i=0;while(true){if(!(i=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+i]));l=k.t;if(l===BY.nil){i=i+(1)>>0;continue;}if(h&&g.matched&&l.cap.$length>0&&(m=g.matchcap,(0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0]))<(n=l.cap,(0>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+0]))){g.pool=$append(g.pool,l);i=i+(1)>>0;continue;}o=l.inst;p=false;q=o.Op;if(q===(4)){if(l.cap.$length>0&&(!h||!g.matched||(r=g.matchcap,(1>=r.$length?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+1]))=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+1]=c));$copySlice(g.matchcap,l.cap);}if(!h){t=$subslice(a.dense,(i+1>>0));u=0;while(true){if(!(u=t.$length)?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+u]),R);if(!(v.t===BY.nil)){g.pool=$append(g.pool,v.t);}u++;}a.dense=$subslice(a.dense,0,0);}g.matched=true;}else if(q===(7)){p=o.MatchRune(e);}else if(q===(8)){p=e===(w=o.Rune,(0>=w.$length?($throwRuntimeError("index out of range"),undefined):w.$array[w.$offset+0]));}else if(q===(9)){p=true;}else if(q===(10)){p=!((e===10));}else{$panic(new $String("bad inst"));}if(p){l=g.add(b,o.Out,d,l.cap,f,l);}if(!(l===BY.nil)){g.pool=$append(g.pool,l);}i=i+(1)>>0;}a.dense=$subslice(a.dense,0,0);};T.prototype.step=function(a,b,c,d,e,f){return this.$val.step(a,b,c,d,e,f);};T.ptr.prototype.add=function(a,b,c,d,e,f){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,$s;$s=0;s:while(true){switch($s){case 0:g=this;case 1:if(b===0){$s=-1;return f;}i=(h=a.sparse,((b<0||b>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+b]));if(i<((a.dense.$length>>>0))&&((j=a.dense,((i<0||i>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+i])).pc===b)){$s=-1;return f;}k=a.dense.$length;a.dense=$subslice(a.dense,0,(k+1>>0));m=(l=a.dense,((k<0||k>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+k]));m.t=BY.nil;m.pc=b;(n=a.sparse,((b<0||b>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+b]=((k>>>0))));p=(o=g.p.Inst,((b<0||b>=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+b]));q=p.Op;if(q===(5)){$s=3;continue;}if((q===(0))||(q===(1))){$s=4;continue;}if(q===(3)){$s=5;continue;}if(q===(6)){$s=6;continue;}if(q===(2)){$s=7;continue;}if((q===(4))||(q===(7))||(q===(8))||(q===(9))||(q===(10))){$s=8;continue;}$s=9;continue;case 3:$s=10;continue;case 4:f=g.add(a,p.Out,c,d,e,f);b=p.Arg;$s=1;continue;$s=10;continue;case 5:if(e.match(((p.Arg<<24>>>24)))){$s=11;continue;}$s=12;continue;case 11:b=p.Out;$s=1;continue;case 12:$s=10;continue;case 6:b=p.Out;$s=1;continue;$s=10;continue;case 7:if(((p.Arg>>0))=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+r]));(t=p.Arg,((t<0||t>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+t]=c));g.add(a,p.Out,c,d,e,BY.nil);(u=p.Arg,((u<0||u>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+u]=s));$s=15;continue;case 14:b=p.Out;$s=1;continue;case 15:$s=10;continue;case 8:if(f===BY.nil){f=g.alloc(p);}else{f.inst=p;}if(d.$length>0&&!((v=f.cap,$indexPtr(v.$array,v.$offset+0,CB))===$indexPtr(d.$array,d.$offset+0,CB))){$copySlice(f.cap,d);}m.t=f;f=BY.nil;$s=10;continue;case 9:$panic(new $String("unhandled"));case 10:case 2:$s=-1;return f;}return;}};T.prototype.add=function(a,b,c,d,e,f){return this.$val.add(a,b,c,d,e,f);};Z=function(){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=Y.Get();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}a=$assertType(b,CC,true);c=a[0];d=a[1];if(!d){c=new X.ptr(new U.ptr(new BE.ptr(BX.nil),new BD.ptr(""),new BF.ptr($ifaceNil,false,0)),BV.nil);}$s=-1;return c;}return;}if($f===undefined){$f={$blk:Z};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AA=function(a){var a;a.inputs.clear();Y.Put(a);};AT.ptr.prototype.doOnePass=function(a,b,c,d,e,f){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,b,ba,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;b=$f.b;ba=$f.ba;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:g=[g];h=this;i=h.cond;if(i===255){$s=-1;return BV.nil;}j=Z();$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}k=j;if(k.matchcap.$capacity=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+o]=-1));n++;}q=k.inputs.init(a,b,c);r=q[0];s=-1;t=-1;u=s;v=t;w=0;x=0;y=w;z=x;ab=r.step(d);$s=2;case 2:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}aa=ab;u=aa[0];y=aa[1];if(!((u===-1))){$s=3;continue;}$s=4;continue;case 3:ad=r.step(d+y>>0);$s=5;case 5:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}ac=ad;v=ac[0];z=ac[1];case 4:ae=new V(0,0);if(d===0){$s=6;continue;}$s=7;continue;case 6:ae=W(-1,u);$s=8;continue;case 7:af=r.context(d);$s=9;case 9:if($c){$c=false;af=af.$blk();}if(af&&af.$blk!==undefined){break s;}ae=af;case 8:ag=h.onepass.Start;g[0]=$clone((ah=h.onepass.Inst,((ag<0||ag>=ah.$length)?($throwRuntimeError("index out of range"),undefined):ah.$array[ah.$offset+ag])),AD);if(!((d===0)&&ae.match(((g[0].Inst.Arg<<24>>>24)))&&h.prefix.length>0)){ai=false;$s=12;continue s;}aj=r.canCheckPrefix();$s=13;case 13:if($c){$c=false;aj=aj.$blk();}if(aj&&aj.$blk!==undefined){break s;}ai=aj;case 12:if(ai){$s=10;continue;}$s=11;continue;case 10:ak=r.hasPrefix(h);$s=16;case 16:if($c){$c=false;ak=ak.$blk();}if(ak&&ak.$blk!==undefined){break s;}if(!ak){$s=14;continue;}$s=15;continue;case 14:$s=17;continue;case 15:d=d+(h.prefix.length)>>0;am=r.step(d);$s=18;case 18:if($c){$c=false;am=am.$blk();}if(am&&am.$blk!==undefined){break s;}al=am;u=al[0];y=al[1];ao=r.step(d+y>>0);$s=19;case 19:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}an=ao;v=an[0];z=an[1];ap=r.context(d);$s=20;case 20:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}ae=ap;ag=((h.prefixEnd>>0));case 11:case 21:AD.copy(g[0],(aq=h.onepass.Inst,((ag<0||ag>=aq.$length)?($throwRuntimeError("index out of range"),undefined):aq.$array[aq.$offset+ag])));ag=((g[0].Inst.Out>>0));ar=g[0].Inst.Op;if(ar===(4)){$s=24;continue;}if(ar===(7)){$s=25;continue;}if(ar===(8)){$s=26;continue;}if(ar===(9)){$s=27;continue;}if(ar===(10)){$s=28;continue;}if((ar===(0))||(ar===(1))){$s=29;continue;}if(ar===(5)){$s=30;continue;}if(ar===(6)){$s=31;continue;}if(ar===(3)){$s=32;continue;}if(ar===(2)){$s=33;continue;}$s=34;continue;case 24:l=true;if(k.matchcap.$length>0){(as=k.matchcap,(0>=as.$length?($throwRuntimeError("index out of range"),undefined):as.$array[as.$offset+0]=0));(at=k.matchcap,(1>=at.$length?($throwRuntimeError("index out of range"),undefined):at.$array[at.$offset+1]=d));}$s=17;continue;$s=35;continue;case 25:if(!g[0].Inst.MatchRune(u)){$s=36;continue;}$s=37;continue;case 36:$s=17;continue;case 37:$s=35;continue;case 26:if(!((u===(au=g[0].Inst.Rune,(0>=au.$length?($throwRuntimeError("index out of range"),undefined):au.$array[au.$offset+0]))))){$s=38;continue;}$s=39;continue;case 38:$s=17;continue;case 39:$s=35;continue;case 27:$s=35;continue;case 28:if(u===10){$s=40;continue;}$s=41;continue;case 40:$s=17;continue;case 41:$s=35;continue;case 29:ag=((AF(g[0],u)>>0));$s=21;continue;$s=35;continue;case 30:$s=17;continue;$s=35;continue;case 31:$s=21;continue;$s=35;continue;case 32:if(!ae.match(((g[0].Inst.Arg<<24>>>24)))){$s=42;continue;}$s=43;continue;case 42:$s=17;continue;case 43:$s=21;continue;$s=35;continue;case 33:if(((g[0].Inst.Arg>>0))=av.$length)?($throwRuntimeError("index out of range"),undefined):av.$array[av.$offset+aw]=d));}$s=21;continue;$s=35;continue;case 34:$panic(new $String("bad inst"));case 35:case 23:if(y===0){$s=22;continue;}ae=W(u,v);d=d+(y)>>0;ax=v;ay=z;u=ax;y=ay;if(!((u===-1))){$s=44;continue;}$s=45;continue;case 44:ba=r.step(d+y>>0);$s=46;case 46:if($c){$c=false;ba=ba.$blk();}if(ba&&ba.$blk!==undefined){break s;}az=ba;v=az[0];z=az[1];case 45:$s=21;continue;case 22:case 17:if(!l){AA(k);$s=-1;return BV.nil;}f=$appendSlice(f,k.matchcap);AA(k);$s=-1;return f;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.doOnePass};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.b=b;$f.ba=ba;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.doOnePass=function(a,b,c,d,e,f){return this.$val.doOnePass(a,b,c,d,e,f);};AT.ptr.prototype.doMatch=function(a,b,c){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=d.doExecute(a,b,c,0,0,BV.nil);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return!(e===BV.nil);}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.doMatch};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.doMatch=function(a,b,c){return this.$val.doMatch(a,b,c);};AT.ptr.prototype.doExecute=function(a,b,c,d,e,f){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:g=this;if(f===BV.nil){f=$subslice(new BV(AB),0,0,0);}if(!(g.onepass===CD.nil)){$s=1;continue;}$s=2;continue;case 1:h=g.doOnePass(a,b,c,d,e,f);$s=3;case 3:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}$s=-1;return h;case 2:if($interfaceIsEqual(a,$ifaceNil)&&(b.$length+c.length>>0)=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f]));if(!((g.Op===3))||((((((g.Arg<<24>>>24)))&4)>>>0)===0)){h="";i=g.Op===4;j=((a.Start>>>0));b=h;c=i;d=j;return[b,c,d];}d=g.Out;g=(k=a.Inst,((d<0||d>=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+d]));while(true){if(!(g.Op===6)){break;}d=g.Out;g=(l=a.Inst,((d<0||d>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+d]));}if(!((AG(g)===7))||!((g.Rune.$length===1))){m="";n=g.Op===4;o=((a.Start>>>0));b=m;c=n;d=o;return[b,c,d];}p=new E.Builder.ptr(CE.nil,BX.nil);while(true){if(!((AG(g)===7)&&(g.Rune.$length===1)&&(((((g.Arg<<16>>>16))&1)>>>0)===0))){break;}p.WriteRune((q=g.Rune,(0>=q.$length?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+0])));r=g.Out;s=(t=a.Inst,u=g.Out,((u<0||u>=t.$length)?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+u]));d=r;g=s;}if((g.Op===3)&&!((((((g.Arg<<24>>>24))&8)>>>0)===0))&&((v=a.Inst,w=g.Out,((w<0||w>=v.$length)?($throwRuntimeError("index out of range"),undefined):v.$array[v.$offset+w])).Op===4)){c=true;}x=p.String();y=c;z=d;b=x;c=y;d=z;return[b,c,d];};AF=function(a,b){var a,b,c,d;c=a.Inst.MatchRunePos(b);if(c>=0){return(d=a.Next,((c<0||c>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+c]));}if(a.Inst.Op===1){return a.Inst.Out;}return 0;};AG=function(a){var a,b,c;b=a.Op;c=b;if((c===(8))||(c===(9))||(c===(10))){b=7;}return b;};AH.ptr.prototype.empty=function(){var a;a=this;return a.nextIndex>=a.size;};AH.prototype.empty=function(){return this.$val.empty();};AH.ptr.prototype.next=function(){var a,b,c,d;a=0;b=this;a=(c=b.dense,d=b.nextIndex,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]));b.nextIndex=b.nextIndex+(1)>>>0;return a;};AH.prototype.next=function(){return this.$val.next();};AH.ptr.prototype.clear=function(){var a;a=this;a.size=0;a.nextIndex=0;};AH.prototype.clear=function(){return this.$val.clear();};AH.ptr.prototype.contains=function(a){var a,b,c,d,e,f;b=this;if(a>=((b.sparse.$length>>>0))){return false;}return(c=b.sparse,((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]))=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+a])),((e<0||e>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]))===a);};AH.prototype.contains=function(a){return this.$val.contains(a);};AH.ptr.prototype.insert=function(a){var a,b;b=this;if(!b.contains(a)){b.insertNew(a);}};AH.prototype.insert=function(a){return this.$val.insert(a);};AH.ptr.prototype.insertNew=function(a){var a,b,c,d,e;b=this;if(a>=((b.sparse.$length>>>0))){return;}(c=b.sparse,((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]=b.size));(d=b.dense,e=b.size,((e<0||e>=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]=a));b.size=b.size+(1)>>>0;};AH.prototype.insertNew=function(a){return this.$val.insertNew(a);};AI=function(a){var a,b;b=CF.nil;b=new AH.ptr($makeSlice(BT,a),$makeSlice(BT,a),0,0);return b;};AL=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);e=[e];f=[f];g=[g];h=[h];i=[i];j=[j];k=a.$get().$length;l=b.$get().$length;if(!(((k&1)===0))||!(((l&1)===0))){$panic(new $String("mergeRuneSets odd length []rune"));}m=0;n=0;f[0]=m;j[0]=n;g[0]=$makeSlice(BS,0);h[0]=$makeSlice(BT,0);i[0]=true;$deferred.push([(function(e,f,g,h,i,j){return function(){if(!i[0]){g[0]=BS.nil;h[0]=BT.nil;}};})(e,f,g,h,i,j),[]]);e[0]=-1;o=(function(e,f,g,h,i,j){return function(o,p,q){var o,p,q,r,s,t,u,v,w;if(e[0]>0&&(r=p.$get(),s=o.$get(),((s<0||s>=r.$length)?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+s]))<=((e[0]<0||e[0]>=g[0].$length)?($throwRuntimeError("index out of range"),undefined):g[0].$array[g[0].$offset+e[0]])){return false;}g[0]=$append(g[0],(t=p.$get(),u=o.$get(),((u<0||u>=t.$length)?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+u])),(v=p.$get(),w=o.$get()+1>>0,((w<0||w>=v.$length)?($throwRuntimeError("index out of range"),undefined):v.$array[v.$offset+w])));o.$set(o.$get()+(2)>>0);e[0]=e[0]+(2)>>0;h[0]=$append(h[0],q);return true;};})(e,f,g,h,i,j);case 1:if(!(f[0]=l){$s=4;continue;}if(f[0]>=k){$s=5;continue;}if((p=b.$get(),((j[0]<0||j[0]>=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+j[0]]))<(q=a.$get(),((f[0]<0||f[0]>=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+f[0]]))){$s=6;continue;}$s=7;continue;case 4:r=o((f.$ptr||(f.$ptr=new CB(function(){return this.$target[0];},function($v){this.$target[0]=$v;},f))),a,c);$s=9;case 9:if($c){$c=false;r=r.$blk();}if(r&&r.$blk!==undefined){break s;}i[0]=r;$s=8;continue;case 5:s=o((j.$ptr||(j.$ptr=new CB(function(){return this.$target[0];},function($v){this.$target[0]=$v;},j))),b,d);$s=10;case 10:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}i[0]=s;$s=8;continue;case 6:t=o((j.$ptr||(j.$ptr=new CB(function(){return this.$target[0];},function($v){this.$target[0]=$v;},j))),b,d);$s=11;case 11:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}i[0]=t;$s=8;continue;case 7:u=o((f.$ptr||(f.$ptr=new CB(function(){return this.$target[0];},function($v){this.$target[0]=$v;},f))),a,c);$s=12;case 12:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}i[0]=u;case 8:case 3:if(!i[0]){$s=-1;return[AJ,AK];}$s=1;continue;case 2:$s=-1;return[g[0],h[0]];}return;}}catch(err){$err=err;$s=-1;return[BS.nil,BT.nil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AL};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AM=function(a,b){var a,b,c,d,e,f,g,h,i,j;c=b.Inst;d=0;while(true){if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]),A.Inst);g=f.Op;if((g===(0))||(g===(1))||(g===(7))){}else if((g===(2))||(g===(3))||(g===(6))||(g===(4))||(g===(5))){(h=a.Inst,((e<0||e>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+e])).Next=BT.nil;}else if((g===(8))||(g===(9))||(g===(10))){(i=a.Inst,((e<0||e>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+e])).Next=BT.nil;AD.copy((j=a.Inst,((e<0||e>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+e])),new AD.ptr($clone(f,A.Inst),BT.nil));}d++;}};AN=function(a){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;b=new AC.ptr($makeSlice(CG,a.Inst.$length),a.Start,a.NumCap);c=a.Inst;d=0;while(true){if(!(d=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]),A.Inst);AD.copy((g=b.Inst,((e<0||e>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+e])),new AD.ptr($clone(f,A.Inst),BT.nil));d++;}h=b.Inst;i=0;while(true){if(!(i=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+j])).Inst.Op;if((l===(0))||(l===(1))){o=(m=(n=b.Inst,((j<0||j>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+j])),(m.$ptr_Out||(m.$ptr_Out=new CH(function(){return this.$target.Inst.Out;},function($v){this.$target.Inst.Out=$v;},m))));r=(p=(q=b.Inst,((j<0||j>=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+j])),(p.$ptr_Arg||(p.$ptr_Arg=new CH(function(){return this.$target.Inst.Arg;},function($v){this.$target.Inst.Arg=$v;},p))));u=$clone((s=b.Inst,t=r.$get(),((t<0||t>=s.$length)?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+t])),AD);if(!((u.Inst.Op===0)||(u.Inst.Op===1))){v=o;w=r;r=v;o=w;AD.copy(u,(x=b.Inst,y=r.$get(),((y<0||y>=x.$length)?($throwRuntimeError("index out of range"),undefined):x.$array[x.$offset+y])));if(!((u.Inst.Op===0)||(u.Inst.Op===1))){i++;continue;}}ab=$clone((z=b.Inst,aa=o.$get(),((aa<0||aa>=z.$length)?($throwRuntimeError("index out of range"),undefined):z.$array[z.$offset+aa])),AD);if((ab.Inst.Op===0)||(ab.Inst.Op===1)){i++;continue;}af=(ac=(ad=b.Inst,ae=r.$get(),((ae<0||ae>=ad.$length)?($throwRuntimeError("index out of range"),undefined):ad.$array[ad.$offset+ae])),(ac.$ptr_Out||(ac.$ptr_Out=new CH(function(){return this.$target.Inst.Out;},function($v){this.$target.Inst.Out=$v;},ac))));aj=(ag=(ah=b.Inst,ai=r.$get(),((ai<0||ai>=ah.$length)?($throwRuntimeError("index out of range"),undefined):ah.$array[ah.$offset+ai])),(ag.$ptr_Arg||(ag.$ptr_Arg=new CH(function(){return this.$target.Inst.Arg;},function($v){this.$target.Inst.Arg=$v;},ag))));ak=false;if(u.Inst.Out===((j>>>0))){ak=true;}else if(u.Inst.Arg===((j>>>0))){ak=true;al=aj;am=af;af=al;aj=am;}if(ak){af.$set(o.$get());}if(o.$get()===af.$get()){r.$set(aj.$get());}}else{i++;continue;}i++;}return b;};AO.prototype.Len=function(){var a;a=this;return a.$length;};$ptrType(AO).prototype.Len=function(){return this.$get().Len();};AO.prototype.Less=function(a,b){var a,b,c;c=this;return((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a])<((b<0||b>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]);};$ptrType(AO).prototype.Less=function(a,b){return this.$get().Less(a,b);};AO.prototype.Swap=function(a,b){var a,b,c,d,e;c=this;d=((b<0||b>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]);e=((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]);((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]=d);((b<0||b>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]=e);};$ptrType(AO).prototype.Swap=function(a,b){return this.$get().Swap(a,b);};AR=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=[b];c=[c];d=[d];e=[e];if(a[0].Inst.$length>=1000){$s=-1;return CD.nil;}c[0]=AI(a[0].Inst.$length);e[0]=AI(a[0].Inst.$length);b[0]=$throwNilPointerError;d[0]=$makeSlice(CI,a[0].Inst.$length);b[0]=(function(a,b,c,d,e){return function $b(f,g){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,az,ba,bb,bc,bd,be,bf,bg,bh,bi,bj,bk,bl,bm,bn,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;az=$f.az;ba=$f.ba;bb=$f.bb;bc=$f.bc;bd=$f.bd;be=$f.be;bf=$f.bf;bg=$f.bg;bh=$f.bh;bi=$f.bi;bj=$f.bj;bk=$f.bk;bl=$f.bl;bm=$f.bm;bn=$f.bn;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:h=false;h=true;j=(i=a[0].Inst,((f<0||f>=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+f]));if(e[0].contains(f)){$s=-1;return h;}e[0].insert(f);k=j.Inst.Op;if((k===(0))||(k===(1))){$s=2;continue;}if((k===(2))||(k===(6))){$s=3;continue;}if(k===(3)){$s=4;continue;}if((k===(4))||(k===(5))){$s=5;continue;}if(k===(7)){$s=6;continue;}if(k===(8)){$s=7;continue;}if(k===(9)){$s=8;continue;}if(k===(10)){$s=9;continue;}$s=10;continue;case 2:m=b[0](j.Inst.Out,g);$s=12;case 12:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}if(!(m)){l=false;$s=11;continue s;}n=b[0](j.Inst.Arg,g);$s=13;case 13:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}l=n;case 11:h=l;p=(o=j.Inst.Out,((o<0||o>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+o]));r=(q=j.Inst.Arg,((q<0||q>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+q]));if(p&&r){h=false;$s=1;continue;}if(r){s=j.Inst.Arg;t=j.Inst.Out;j.Inst.Out=s;j.Inst.Arg=t;u=r;v=p;p=u;r=v;}if(p){((f<0||f>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+f]=true);j.Inst.Op=1;}x=AL($indexPtr(d[0].$array,d[0].$offset+j.Inst.Out,CJ),$indexPtr(d[0].$array,d[0].$offset+j.Inst.Arg,CJ),j.Inst.Out,j.Inst.Arg);$s=14;case 14:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}w=x;((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]=w[0]);j.Next=w[1];if(j.Next.$length>0&&((y=j.Next,(0>=y.$length?($throwRuntimeError("index out of range"),undefined):y.$array[y.$offset+0]))===4294967295)){h=false;$s=1;continue;}$s=10;continue;case 3:z=b[0](j.Inst.Out,g);$s=15;case 15:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}h=z;((f<0||f>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+f]=(aa=j.Inst.Out,((aa<0||aa>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+aa])));((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]=$appendSlice(new BS([]),(ab=j.Inst.Out,((ab<0||ab>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+ab]))));j.Next=$makeSlice(BT,((ac=((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]).$length/2,(ac===ac&&ac!==1/0&&ac!==-1/0)?ac>>0:$throwRuntimeError("integer divide by zero"))+1>>0));ad=j.Next;ae=0;while(true){if(!(ae=ag.$length)?($throwRuntimeError("index out of range"),undefined):ag.$array[ag.$offset+af]=j.Inst.Out));ae++;}$s=10;continue;case 4:ah=b[0](j.Inst.Out,g);$s=16;case 16:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}h=ah;((f<0||f>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+f]=(ai=j.Inst.Out,((ai<0||ai>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+ai])));((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]=$appendSlice(new BS([]),(aj=j.Inst.Out,((aj<0||aj>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+aj]))));j.Next=$makeSlice(BT,((ak=((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]).$length/2,(ak===ak&&ak!==1/0&&ak!==-1/0)?ak>>0:$throwRuntimeError("integer divide by zero"))+1>>0));al=j.Next;am=0;while(true){if(!(am=ao.$length)?($throwRuntimeError("index out of range"),undefined):ao.$array[ao.$offset+an]=j.Inst.Out));am++;}$s=10;continue;case 5:((f<0||f>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+f]=(j.Inst.Op===4));$s=10;continue;case 6:((f<0||f>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+f]=false);if(j.Next.$length>0){$s=1;continue;}c[0].insert(j.Inst.Out);if(j.Inst.Rune.$length===0){((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]=new BS([]));j.Next=new BT([j.Inst.Out]);$s=1;continue;}ap=$makeSlice(BS,0);if((j.Inst.Rune.$length===1)&&!((((((j.Inst.Arg<<16>>>16))&1)>>>0)===0))){$s=17;continue;}$s=18;continue;case 17:ar=(aq=j.Inst.Rune,(0>=aq.$length?($throwRuntimeError("index out of range"),undefined):aq.$array[aq.$offset+0]));ap=$append(ap,ar,ar);as=F.SimpleFold(ar);while(true){if(!(!((as===ar)))){break;}ap=$append(ap,as,as);as=F.SimpleFold(as);}$r=D.Sort(($subslice(new AO(ap.$array),ap.$offset,ap.$offset+ap.$length)));$s=20;case 20:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=19;continue;case 18:ap=$appendSlice(ap,j.Inst.Rune);case 19:((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]=ap);j.Next=$makeSlice(BT,((at=((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]).$length/2,(at===at&&at!==1/0&&at!==-1/0)?at>>0:$throwRuntimeError("integer divide by zero"))+1>>0));au=j.Next;av=0;while(true){if(!(av=ax.$length)?($throwRuntimeError("index out of range"),undefined):ax.$array[ax.$offset+aw]=j.Inst.Out));av++;}j.Inst.Op=7;$s=10;continue;case 7:((f<0||f>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+f]=false);if(j.Next.$length>0){$s=1;continue;}c[0].insert(j.Inst.Out);ay=new BS([]);if(!((((((j.Inst.Arg<<16>>>16))&1)>>>0)===0))){$s=21;continue;}$s=22;continue;case 21:ba=(az=j.Inst.Rune,(0>=az.$length?($throwRuntimeError("index out of range"),undefined):az.$array[az.$offset+0]));ay=$append(ay,ba,ba);bb=F.SimpleFold(ba);while(true){if(!(!((bb===ba)))){break;}ay=$append(ay,bb,bb);bb=F.SimpleFold(bb);}$r=D.Sort(($subslice(new AO(ay.$array),ay.$offset,ay.$offset+ay.$length)));$s=24;case 24:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=23;continue;case 22:ay=$append(ay,(bc=j.Inst.Rune,(0>=bc.$length?($throwRuntimeError("index out of range"),undefined):bc.$array[bc.$offset+0])),(bd=j.Inst.Rune,(0>=bd.$length?($throwRuntimeError("index out of range"),undefined):bd.$array[bd.$offset+0])));case 23:((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]=ay);j.Next=$makeSlice(BT,((be=((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]).$length/2,(be===be&&be!==1/0&&be!==-1/0)?be>>0:$throwRuntimeError("integer divide by zero"))+1>>0));bf=j.Next;bg=0;while(true){if(!(bg=bi.$length)?($throwRuntimeError("index out of range"),undefined):bi.$array[bi.$offset+bh]=j.Inst.Out));bg++;}j.Inst.Op=7;$s=10;continue;case 8:((f<0||f>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+f]=false);if(j.Next.$length>0){$s=1;continue;}c[0].insert(j.Inst.Out);((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]=$appendSlice(new BS([]),AQ));j.Next=new BT([j.Inst.Out]);$s=10;continue;case 9:((f<0||f>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+f]=false);if(j.Next.$length>0){$s=1;continue;}c[0].insert(j.Inst.Out);((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]=$appendSlice(new BS([]),AP));j.Next=$makeSlice(BT,((bj=((f<0||f>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+f]).$length/2,(bj===bj&&bj!==1/0&&bj!==-1/0)?bj>>0:$throwRuntimeError("integer divide by zero"))+1>>0));bk=j.Next;bl=0;while(true){if(!(bl=bn.$length)?($throwRuntimeError("index out of range"),undefined):bn.$array[bn.$offset+bm]=j.Inst.Out));bl++;}case 10:case 1:$s=-1;return h;}return;}if($f===undefined){$f={$blk:$b};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.az=az;$f.ba=ba;$f.bb=bb;$f.bc=bc;$f.bd=bd;$f.be=be;$f.bf=bf;$f.bg=bg;$f.bh=bh;$f.bi=bi;$f.bj=bj;$f.bk=bk;$f.bl=bl;$f.bm=bm;$f.bn=bn;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};})(a,b,c,d,e);c[0].clear();c[0].insert(((a[0].Start>>>0)));f=$makeSlice(CK,a[0].Inst.$length);case 1:if(!(!c[0].empty())){$s=2;continue;}e[0].clear();g=c[0].next();h=b[0](g,f);$s=5;case 5:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}if(!h){$s=3;continue;}$s=4;continue;case 3:a[0]=CD.nil;$s=2;continue;case 4:$s=1;continue;case 2:if(!(a[0]===CD.nil)){i=a[0].Inst;j=0;while(true){if(!(j=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+k])).Inst.Rune=((k<0||k>=d[0].$length)?($throwRuntimeError("index out of range"),undefined):d[0].$array[d[0].$offset+k]);j++;}}$s=-1;return a[0];}return;}if($f===undefined){$f={$blk:AR};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};AS=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=CD.nil;if(a.Start===0){b=CD.nil;$s=-1;return b;}if(!(((c=a.Inst,d=a.Start,((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d])).Op===3))||!(((((((e=a.Inst,f=a.Start,((f<0||f>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+f])).Arg<<24>>>24))&4)>>>0)===4))){b=CD.nil;$s=-1;return b;}g=a.Inst;h=0;case 1:if(!(h=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+h]),A.Inst);l=(j=a.Inst,k=i.Out,((k<0||k>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+k])).Op;m=i.Op;if((m===(0))||(m===(1))){if((l===4)||((n=a.Inst,o=i.Arg,((o<0||o>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o])).Op===4)){b=CD.nil;$s=-1;return b;}}else if(m===(3)){if(l===4){if(((((i.Arg<<24>>>24))&8)>>>0)===8){h++;$s=1;continue;}b=CD.nil;$s=-1;return b;}}else if(l===4){b=CD.nil;$s=-1;return b;}h++;$s=1;continue;case 2:b=AN(a);p=AR(b);$s=3;case 3:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}b=p;if(!(b===CD.nil)){AM(b,a);}b=b;$s=-1;return b;}return;}if($f===undefined){$f={$blk:AS};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};AT.ptr.prototype.String=function(){var a;a=this;return a.expr;};AT.prototype.String=function(){return this.$val.String();};AT.ptr.prototype.Copy=function(){var a,b;a=this;b=$clone(a,AT);return b;};AT.prototype.Copy=function(){return this.$val.Copy();};AU=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=AW(a,212,false);$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return b;}return;}if($f===undefined){$f={$blk:AU};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Compile=AU;AT.ptr.prototype.Longest=function(){var a;a=this;a.longest=true;};AT.prototype.Longest=function(){return this.$val.Longest();};AW=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=A.Parse(a,b);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;f=d[0];g=d[1];if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[CL.nil,g];}h=f.MaxCap();i=f.CapNames();f=f.Simplify();j=A.Compile(f);k=j[0];g=j[1];if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[CL.nil,g];}l=k.NumCap;if(l<2){l=2;}m=AS(k);$s=2;case 2:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}n=new AT.ptr(a,k,m,h,0,i,"",BX.nil,0,0,0,l,false,k.StartCond(),c);if(n.onepass===CD.nil){o=k.Prefix();n.prefix=o[0];n.prefixComplete=o[1];n.maxBitStateLen=O(k);}else{p=AE(k);n.prefix=p[0];n.prefixComplete=p[1];n.prefixEnd=p[2];}if(!(n.prefix==="")){n.prefixBytes=(new BX($stringToBytes(n.prefix)));q=I.DecodeRuneInString(n.prefix);n.prefixRune=q[0];}r=k.Inst.$length;s=0;while(true){if(!(!((((s<0||s>=AX.length)?($throwRuntimeError("index out of range"),undefined):AX[s])===0))&&((s<0||s>=AX.length)?($throwRuntimeError("index out of range"),undefined):AX[s])>0;}n.mpool=s;$s=-1;return[n,$ifaceNil];}return;}if($f===undefined){$f={$blk:AW};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.$s=$s;$f.$r=$r;return $f;};AT.ptr.prototype.get=function(){var a,b,c,d,e,f,g,h,i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;d=(c=a.mpool,((c<0||c>=AY.length)?($throwRuntimeError("index out of range"),undefined):AY[c])).Get();$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}b=$assertType(d,CO,true);e=b[0];f=b[1];if(!f){e=new T.ptr(CL.nil,CM.nil,new Q.ptr(BT.nil,CP.nil),new Q.ptr(BT.nil,CP.nil),CQ.nil,false,BV.nil,new U.ptr(new BE.ptr(BX.nil),new BD.ptr(""),new BF.ptr($ifaceNil,false,0)));}e.re=a;e.p=a.prog;if(e.matchcap.$capacity=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+h]);i.cap=$makeSlice(BV,a.matchcap);h++;}}k=(j=a.mpool,((j<0||j>=AX.length)?($throwRuntimeError("index out of range"),undefined):AX[j]));if(k===0){k=a.prog.Inst.$length;}if(e.q0.sparse.$length=AY.length)?($throwRuntimeError("index out of range"),undefined):AY[c])).Put(a);};AT.prototype.put=function(a){return this.$val.put(a);};AZ=function(a){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=AU(a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}b=c;d=b[0];e=b[1];if(!($interfaceIsEqual(e,$ifaceNil))){$s=2;continue;}$s=3;continue;case 2:f=e.Error();$s=4;case 4:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$panic(new $String("regexp: Compile("+BB(a)+"): "+f));case 3:$s=-1;return d;}return;}if($f===undefined){$f={$blk:AZ};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};$pkg.MustCompile=AZ;BB=function(a){var a;if(H.CanBackquote(a)){return"`"+a+"`";}return H.Quote(a);};AT.ptr.prototype.NumSubexp=function(){var a;a=this;return a.numSubexp;};AT.prototype.NumSubexp=function(){return this.$val.NumSubexp();};AT.ptr.prototype.SubexpNames=function(){var a;a=this;return a.subexpNames;};AT.prototype.SubexpNames=function(){return this.$val.SubexpNames();};BD.ptr.prototype.step=function(a){var a,b,c;b=this;if(a>0)),1];}return I.DecodeRuneInString($substring(b.str,a));}return[-1,0];};BD.prototype.step=function(a){return this.$val.step(a);};BD.ptr.prototype.canCheckPrefix=function(){var a;a=this;return true;};BD.prototype.canCheckPrefix=function(){return this.$val.canCheckPrefix();};BD.ptr.prototype.hasPrefix=function(a){var a,b;b=this;return E.HasPrefix(b.str,a.prefix);};BD.prototype.hasPrefix=function(a){return this.$val.hasPrefix(a);};BD.ptr.prototype.index=function(a,b){var a,b,c;c=this;return E.Index($substring(c.str,b),a.prefix);};BD.prototype.index=function(a,b){return this.$val.index(a,b);};BD.ptr.prototype.context=function(a){var a,b,c,d,e,f,g,h;b=this;c=-1;d=-1;e=c;f=d;if((((a-1>>0)>>>0))<((b.str.length>>>0))){e=((b.str.charCodeAt((a-1>>0))>>0));if(e>=128){g=I.DecodeLastRuneInString($substring(b.str,0,a));e=g[0];}}if(((a>>>0))<((b.str.length>>>0))){f=((b.str.charCodeAt(a)>>0));if(f>=128){h=I.DecodeRuneInString($substring(b.str,a));f=h[0];}}return W(e,f);};BD.prototype.context=function(a){return this.$val.context(a);};BE.ptr.prototype.step=function(a){var a,b,c,d;b=this;if(a=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]));if(d<128){return[((d>>0)),1];}return I.DecodeRune($subslice(b.str,a));}return[-1,0];};BE.prototype.step=function(a){return this.$val.step(a);};BE.ptr.prototype.canCheckPrefix=function(){var a;a=this;return true;};BE.prototype.canCheckPrefix=function(){return this.$val.canCheckPrefix();};BE.ptr.prototype.hasPrefix=function(a){var a,b;b=this;return G.HasPrefix(b.str,a.prefixBytes);};BE.prototype.hasPrefix=function(a){return this.$val.hasPrefix(a);};BE.ptr.prototype.index=function(a,b){var a,b,c;c=this;return G.Index($subslice(c.str,b),a.prefixBytes);};BE.prototype.index=function(a,b){return this.$val.index(a,b);};BE.ptr.prototype.context=function(a){var a,b,c,d,e,f,g,h,i,j,k;b=this;c=-1;d=-1;e=c;f=d;if((((a-1>>0)>>>0))<((b.str.$length>>>0))){e=(((g=b.str,h=a-1>>0,((h<0||h>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+h]))>>0));if(e>=128){i=I.DecodeLastRune($subslice(b.str,0,a));e=i[0];}}if(((a>>>0))<((b.str.$length>>>0))){f=(((j=b.str,((a<0||a>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+a]))>>0));if(f>=128){k=I.DecodeRune($subslice(b.str,a));f=k[0];}}return W(e,f);};BE.prototype.context=function(a){return this.$val.context(a);};BF.ptr.prototype.step=function(a){var a,b,c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;if(!b.atEOT&&!((a===b.pos))){$s=-1;return[-1,0];}d=b.r.ReadRune();$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}c=d;e=c[0];f=c[1];g=c[2];if(!($interfaceIsEqual(g,$ifaceNil))){b.atEOT=true;$s=-1;return[-1,0];}b.pos=b.pos+(f)>>0;$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:BF.ptr.prototype.step};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};BF.prototype.step=function(a){return this.$val.step(a);};BF.ptr.prototype.canCheckPrefix=function(){var a;a=this;return false;};BF.prototype.canCheckPrefix=function(){return this.$val.canCheckPrefix();};BF.ptr.prototype.hasPrefix=function(a){var a,b;b=this;return false;};BF.prototype.hasPrefix=function(a){return this.$val.hasPrefix(a);};BF.ptr.prototype.index=function(a,b){var a,b,c;c=this;return-1;};BF.prototype.index=function(a,b){return this.$val.index(a,b);};BF.ptr.prototype.context=function(a){var a,b;b=this;return new V(0,0);};BF.prototype.context=function(a){return this.$val.context(a);};AT.ptr.prototype.LiteralPrefix=function(){var a,b,c,d,e;a="";b=false;c=this;d=c.prefix;e=c.prefixComplete;a=d;b=e;return[a,b];};AT.prototype.LiteralPrefix=function(){return this.$val.LiteralPrefix();};AT.ptr.prototype.MatchReader=function(a){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=b.doMatch(a,BX.nil,"");$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return c;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.MatchReader};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.MatchReader=function(a){return this.$val.MatchReader(a);};AT.ptr.prototype.MatchString=function(a){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=b.doMatch($ifaceNil,BX.nil,a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return c;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.MatchString};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.MatchString=function(a){return this.$val.MatchString(a);};AT.ptr.prototype.Match=function(a){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=b.doMatch($ifaceNil,a,"");$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return c;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.Match};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.Match=function(a){return this.$val.Match(a);};AT.ptr.prototype.ReplaceAllString=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=[b];c=[c];c[0]=this;d=2;if(E.Contains(b[0],"$")){d=$imul(2,((c[0].numSubexp+1>>0)));}e=c[0].replaceAll(BX.nil,a[0],d,(function(a,b,c){return function(e,f){var e,f;return c[0].expand(e,b[0],BX.nil,a[0],f);};})(a,b,c));$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;$s=-1;return($bytesToString(f));}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.ReplaceAllString};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.ReplaceAllString=function(a,b){return this.$val.ReplaceAllString(a,b);};AT.ptr.prototype.ReplaceAllLiteralString=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=[b];c=this;d=c.replaceAll(BX.nil,a,2,(function(b){return function(d,e){var d,e;return $appendSlice(d,b[0]);};})(b));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return($bytesToString(d));}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.ReplaceAllLiteralString};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.ReplaceAllLiteralString=function(a,b){return this.$val.ReplaceAllLiteralString(a,b);};AT.ptr.prototype.ReplaceAllStringFunc=function(a,b){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=[b];c=this;d=c.replaceAll(BX.nil,a[0],2,(function(a,b){return function $b(d,e){var d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:f=d;g=b[0]($substring(a[0],(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]),(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1])));$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;$s=-1;return $appendSlice(f,h);}return;}if($f===undefined){$f={$blk:$b};}$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};})(a,b));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;$s=-1;return($bytesToString(e));}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.ReplaceAllStringFunc};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.ReplaceAllStringFunc=function(a,b){return this.$val.ReplaceAllStringFunc(a,b);};AT.ptr.prototype.replaceAll=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;f=0;g=0;h=BX.nil;i=0;if(!(a===BX.nil)){i=a.$length;}else{i=b.length;}if(c>e.prog.NumCap){c=e.prog.NumCap;}j=CR.zero();case 1:if(!(g<=i)){$s=2;continue;}k=e.doExecute($ifaceNil,a,b,g,c,$subslice(new BV(j),0,0));$s=3;case 3:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}l=k;if(l.$length===0){$s=2;continue;}if(!(a===BX.nil)){h=$appendSlice(h,$subslice(a,f,(0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])));}else{h=$appendSlice(h,$substring(b,f,(0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])));}if((1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1])>f||((0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])===0)){$s=4;continue;}$s=5;continue;case 4:m=d(h,l);$s=6;case 6:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}h=m;case 5:f=(1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1]);n=0;if(!(a===BX.nil)){o=I.DecodeRune($subslice(a,g));n=o[1];}else{p=I.DecodeRuneInString($substring(b,g));n=p[1];}if((g+n>>0)>(1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1])){g=g+(n)>>0;}else if((g+1>>0)>(1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1])){g=g+(1)>>0;}else{g=(1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1]);}$s=1;continue;case 2:if(!(a===BX.nil)){h=$appendSlice(h,$subslice(a,f));}else{h=$appendSlice(h,$substring(b,f));}$s=-1;return h;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.replaceAll};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.replaceAll=function(a,b,c,d){return this.$val.replaceAll(a,b,c,d);};AT.ptr.prototype.ReplaceAll=function(a,b){var a,b,c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=[b];c=[c];d=[d];c[0]=this;e=2;if(G.IndexByte(b[0],36)>=0){e=$imul(2,((c[0].numSubexp+1>>0)));}d[0]="";f=c[0].replaceAll(a[0],"",e,(function(a,b,c,d){return function(f,g){var f,g;if(!((d[0].length===b[0].$length))){d[0]=($bytesToString(b[0]));}return c[0].expand(f,d[0],a[0],"",g);};})(a,b,c,d));$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;$s=-1;return g;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.ReplaceAll};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.ReplaceAll=function(a,b){return this.$val.ReplaceAll(a,b);};AT.ptr.prototype.ReplaceAllLiteral=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=[b];c=this;d=c.replaceAll(a,"",2,(function(b){return function(d,e){var d,e;return $appendSlice(d,b[0]);};})(b));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.ReplaceAllLiteral};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.ReplaceAllLiteral=function(a,b){return this.$val.ReplaceAllLiteral(a,b);};AT.ptr.prototype.ReplaceAllFunc=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=[b];c=this;d=c.replaceAll(a[0],"",2,(function(a,b){return function $b(d,e){var d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:f=d;g=b[0]($subslice(a[0],(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]),(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1])));$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;$s=-1;return $appendSlice(f,h);}return;}if($f===undefined){$f={$blk:$b};}$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};})(a,b));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.ReplaceAllFunc};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.ReplaceAllFunc=function(a,b){return this.$val.ReplaceAllFunc(a,b);};BL=function(){var a,b,c,d,e,f,g;a=(new BX($stringToBytes("\\.+*?()|[]{}^$")));b=0;while(true){if(!(b=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+b]);e=(d=c%16,d===d?d:$throwRuntimeError("integer divide by zero"));((e<0||e>=BJ.length)?($throwRuntimeError("index out of range"),undefined):BJ[e]=((((e<0||e>=BJ.length)?($throwRuntimeError("index out of range"),undefined):BJ[e])|(((f=((g=c/16,(g===g&&g!==1/0&&g!==-1/0)?g>>>0:$throwRuntimeError("integer divide by zero"))),f<32?(1<>>24)))>>>0));b++;}};AT.ptr.prototype.pad=function(a){var a,b,c;b=this;if(a===BV.nil){return BV.nil;}c=$imul(((1+b.numSubexp>>0)),2);while(true){if(!(a.$length=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+1])===j){if((0>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+0])===l){o=false;}p=0;if(b===BX.nil){q=I.DecodeRuneInString($substring(a,j,f));p=q[1];}else{r=I.DecodeRune($subslice(b,j,f));p=r[1];}if(p>0){j=j+(p)>>0;}else{j=f+1>>0;}}else{j=(1>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+1]);}l=(1>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+1]);if(o){$s=4;continue;}$s=5;continue;case 4:$r=d(e.pad(n));$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}k=k+(1)>>0;case 5:$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.allMatches};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.allMatches=function(a,b,c,d){return this.$val.allMatches(a,b,c,d);};AT.ptr.prototype.Find=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=CR.zero();d=b.doExecute($ifaceNil,a,"",0,2,$subslice(new BV(c),0,0));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(e===BV.nil){$s=-1;return BX.nil;}$s=-1;return $subslice(a,(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]),(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1]));}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.Find};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.Find=function(a){return this.$val.Find(a);};AT.ptr.prototype.FindIndex=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=BV.nil;c=this;d=c.doExecute($ifaceNil,a,"",0,2,BV.nil);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(e===BV.nil){b=BV.nil;$s=-1;return b;}b=$subslice(e,0,2);$s=-1;return b;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindIndex=function(a){return this.$val.FindIndex(a);};AT.ptr.prototype.FindString=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=CR.zero();d=b.doExecute($ifaceNil,BX.nil,a,0,2,$subslice(new BV(c),0,0));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(e===BV.nil){$s=-1;return"";}$s=-1;return $substring(a,(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]),(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1]));}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindString};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindString=function(a){return this.$val.FindString(a);};AT.ptr.prototype.FindStringIndex=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=BV.nil;c=this;d=c.doExecute($ifaceNil,BX.nil,a,0,2,BV.nil);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(e===BV.nil){b=BV.nil;$s=-1;return b;}b=$subslice(e,0,2);$s=-1;return b;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindStringIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindStringIndex=function(a){return this.$val.FindStringIndex(a);};AT.ptr.prototype.FindReaderIndex=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=BV.nil;c=this;d=c.doExecute(a,BX.nil,"",0,2,BV.nil);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(e===BV.nil){b=BV.nil;$s=-1;return b;}b=$subslice(e,0,2);$s=-1;return b;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindReaderIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindReaderIndex=function(a){return this.$val.FindReaderIndex(a);};AT.ptr.prototype.FindSubmatch=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=CS.zero();d=b.doExecute($ifaceNil,a,"",0,b.prog.NumCap,$subslice(new BV(c),0,0));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(e===BV.nil){$s=-1;return CT.nil;}f=$makeSlice(CT,(1+b.numSubexp>>0));g=f;h=0;while(true){if(!(h=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+j]))>=0){((i<0||i>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+i]=$subslice(a,(k=$imul(2,i),((k<0||k>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+k])),(l=($imul(2,i))+1>>0,((l<0||l>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+l]))));}h++;}$s=-1;return f;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindSubmatch};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindSubmatch=function(a){return this.$val.FindSubmatch(a);};AT.ptr.prototype.Expand=function(a,b,c,d){var a,b,c,d,e;e=this;return e.expand(a,($bytesToString(b)),c,"",d);};AT.prototype.Expand=function(a,b,c,d){return this.$val.Expand(a,b,c,d);};AT.ptr.prototype.ExpandString=function(a,b,c,d){var a,b,c,d,e;e=this;return e.expand(a,b,BX.nil,c,d);};AT.prototype.ExpandString=function(a,b,c,d){return this.$val.ExpandString(a,b,c,d);};AT.ptr.prototype.expand=function(a,b,c,d,e){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;f=this;while(true){if(!(b.length>0)){break;}g=E.Index(b,"$");if(g<0){break;}a=$appendSlice(a,$substring(b,0,g));b=$substring(b,g);if(b.length>1&&(b.charCodeAt(1)===36)){a=$append(a,36);b=$substring(b,2);continue;}h=BN(b);i=h[0];j=h[1];k=h[2];l=h[3];if(!l){a=$append(a,36);b=$substring(b,1);continue;}b=k;if(j>=0){if((($imul(2,j))+1>>0)=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+m]))>=0){if(!(c===BX.nil)){a=$appendSlice(a,$subslice(c,(n=$imul(2,j),((n<0||n>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+n])),(o=($imul(2,j))+1>>0,((o<0||o>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+o]))));}else{a=$appendSlice(a,$substring(d,(p=$imul(2,j),((p<0||p>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+p])),(q=($imul(2,j))+1>>0,((q<0||q>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+q]))));}}}else{r=f.subexpNames;s=0;while(true){if(!(s=r.$length)?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+s]);if(i===u&&(($imul(2,t))+1>>0)=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+v]))>=0){if(!(c===BX.nil)){a=$appendSlice(a,$subslice(c,(w=$imul(2,t),((w<0||w>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+w])),(x=($imul(2,t))+1>>0,((x<0||x>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+x]))));}else{a=$appendSlice(a,$substring(d,(y=$imul(2,t),((y<0||y>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+y])),(z=($imul(2,t))+1>>0,((z<0||z>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+z]))));}break;}s++;}}}a=$appendSlice(a,b);return a;};AT.prototype.expand=function(a,b,c,d,e){return this.$val.expand(a,b,c,d,e);};BN=function(a){var a,b,c,d,e,f,g,h,i,j,k;b="";c=0;d="";e=false;if(a.length<2||!((a.charCodeAt(0)===36))){return[b,c,d,e];}f=false;if(a.charCodeAt(1)===123){f=true;a=$substring(a,2);}else{a=$substring(a,1);}g=0;while(true){if(!(g>0;}if(g===0){return[b,c,d,e];}b=$substring(a,0,g);if(f){if(g>=a.length||!((a.charCodeAt(g)===125))){return[b,c,d,e];}g=g+(1)>>0;}c=0;k=0;while(true){if(!(k=100000000){c=-1;break;}c=(($imul(c,10))+((b.charCodeAt(k)>>0))>>0)-48>>0;k=k+(1)>>0;}if((b.charCodeAt(0)===48)&&b.length>1){c=-1;}d=$substring(a,g);e=true;return[b,c,d,e];};AT.ptr.prototype.FindSubmatchIndex=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=b.doExecute($ifaceNil,a,"",0,b.prog.NumCap,BV.nil);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=b.pad(c);$s=2;case 2:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindSubmatchIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindSubmatchIndex=function(a){return this.$val.FindSubmatchIndex(a);};AT.ptr.prototype.FindStringSubmatch=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=CS.zero();d=b.doExecute($ifaceNil,BX.nil,a,0,b.prog.NumCap,$subslice(new BV(c),0,0));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;if(e===BV.nil){$s=-1;return CN.nil;}f=$makeSlice(CN,(1+b.numSubexp>>0));g=f;h=0;while(true){if(!(h=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+j]))>=0){((i<0||i>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+i]=$substring(a,(k=$imul(2,i),((k<0||k>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+k])),(l=($imul(2,i))+1>>0,((l<0||l>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+l]))));}h++;}$s=-1;return f;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindStringSubmatch};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindStringSubmatch=function(a){return this.$val.FindStringSubmatch(a);};AT.ptr.prototype.FindStringSubmatchIndex=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=b.doExecute($ifaceNil,BX.nil,a,0,b.prog.NumCap,BV.nil);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=b.pad(c);$s=2;case 2:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindStringSubmatchIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindStringSubmatchIndex=function(a){return this.$val.FindStringSubmatchIndex(a);};AT.ptr.prototype.FindReaderSubmatchIndex=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=b.doExecute(a,BX.nil,"",0,b.prog.NumCap,BV.nil);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=b.pad(c);$s=2;case 2:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindReaderSubmatchIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindReaderSubmatchIndex=function(a){return this.$val.FindReaderSubmatchIndex(a);};AT.ptr.prototype.FindAll=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];c=[c];d=this;if(b<0){b=a[0].$length+1>>0;}c[0]=CT.nil;$r=d.allMatches("",a[0],b,(function(a,c){return function(e){var e;if(c[0]===CT.nil){c[0]=$makeSlice(CT,0,10);}c[0]=$append(c[0],$subslice(a[0],(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]),(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1])));};})(a,c));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c[0];}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindAll};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindAll=function(a,b){return this.$val.FindAll(a,b);};AT.ptr.prototype.FindAllIndex=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=[c];d=this;if(b<0){b=a.$length+1>>0;}c[0]=CU.nil;$r=d.allMatches("",a,b,(function(c){return function(e){var e;if(c[0]===CU.nil){c[0]=$makeSlice(CU,0,10);}c[0]=$append(c[0],$subslice(e,0,2));};})(c));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c[0];}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindAllIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindAllIndex=function(a,b){return this.$val.FindAllIndex(a,b);};AT.ptr.prototype.FindAllString=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];c=[c];d=this;if(b<0){b=a[0].length+1>>0;}c[0]=CN.nil;$r=d.allMatches(a[0],BX.nil,b,(function(a,c){return function(e){var e;if(c[0]===CN.nil){c[0]=$makeSlice(CN,0,10);}c[0]=$append(c[0],$substring(a[0],(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]),(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1])));};})(a,c));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c[0];}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindAllString};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindAllString=function(a,b){return this.$val.FindAllString(a,b);};AT.ptr.prototype.FindAllStringIndex=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=[c];d=this;if(b<0){b=a.length+1>>0;}c[0]=CU.nil;$r=d.allMatches(a,BX.nil,b,(function(c){return function(e){var e;if(c[0]===CU.nil){c[0]=$makeSlice(CU,0,10);}c[0]=$append(c[0],$subslice(e,0,2));};})(c));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c[0];}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindAllStringIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindAllStringIndex=function(a,b){return this.$val.FindAllStringIndex(a,b);};AT.ptr.prototype.FindAllSubmatch=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];c=[c];d=this;if(b<0){b=a[0].$length+1>>0;}c[0]=CV.nil;$r=d.allMatches("",a[0],b,(function(a,c){return function(e){var e,f,g,h,i,j,k,l,m;if(c[0]===CV.nil){c[0]=$makeSlice(CV,0,10);}g=$makeSlice(CT,(f=e.$length/2,(f===f&&f!==1/0&&f!==-1/0)?f>>0:$throwRuntimeError("integer divide by zero")));h=g;i=0;while(true){if(!(i=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+k]))>=0){((j<0||j>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+j]=$subslice(a[0],(l=$imul(2,j),((l<0||l>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+l])),(m=($imul(2,j))+1>>0,((m<0||m>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+m]))));}i++;}c[0]=$append(c[0],g);};})(a,c));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c[0];}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindAllSubmatch};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindAllSubmatch=function(a,b){return this.$val.FindAllSubmatch(a,b);};AT.ptr.prototype.FindAllSubmatchIndex=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=[c];d=this;if(b<0){b=a.$length+1>>0;}c[0]=CU.nil;$r=d.allMatches("",a,b,(function(c){return function(e){var e;if(c[0]===CU.nil){c[0]=$makeSlice(CU,0,10);}c[0]=$append(c[0],e);};})(c));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c[0];}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindAllSubmatchIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindAllSubmatchIndex=function(a,b){return this.$val.FindAllSubmatchIndex(a,b);};AT.ptr.prototype.FindAllStringSubmatch=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];c=[c];d=this;if(b<0){b=a[0].length+1>>0;}c[0]=CW.nil;$r=d.allMatches(a[0],BX.nil,b,(function(a,c){return function(e){var e,f,g,h,i,j,k,l,m;if(c[0]===CW.nil){c[0]=$makeSlice(CW,0,10);}g=$makeSlice(CN,(f=e.$length/2,(f===f&&f!==1/0&&f!==-1/0)?f>>0:$throwRuntimeError("integer divide by zero")));h=g;i=0;while(true){if(!(i=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+k]))>=0){((j<0||j>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+j]=$substring(a[0],(l=$imul(2,j),((l<0||l>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+l])),(m=($imul(2,j))+1>>0,((m<0||m>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+m]))));}i++;}c[0]=$append(c[0],g);};})(a,c));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c[0];}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindAllStringSubmatch};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindAllStringSubmatch=function(a,b){return this.$val.FindAllStringSubmatch(a,b);};AT.ptr.prototype.FindAllStringSubmatchIndex=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=[c];d=this;if(b<0){b=a.length+1>>0;}c[0]=CU.nil;$r=d.allMatches(a,BX.nil,b,(function(c){return function(e){var e;if(c[0]===CU.nil){c[0]=$makeSlice(CU,0,10);}c[0]=$append(c[0],e);};})(c));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c[0];}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.FindAllStringSubmatchIndex};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.FindAllStringSubmatchIndex=function(a,b){return this.$val.FindAllStringSubmatchIndex(a,b);};AT.ptr.prototype.Split=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(b===0){$s=-1;return CN.nil;}if(c.expr.length>0&&(a.length===0)){$s=-1;return new CN([""]);}d=c.FindAllStringIndex(a,b);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;f=$makeSlice(CN,0,e.$length);g=0;h=0;i=e;j=0;while(true){if(!(j=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]);if(b>0&&f.$length>=(b-1>>0)){break;}h=(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]);if(!(((1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1])===0))){f=$append(f,$substring(a,g,h));}g=(1>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+1]);j++;}if(!((h===a.length))){f=$append(f,$substring(a,g));}$s=-1;return f;}return;}if($f===undefined){$f={$blk:AT.ptr.prototype.Split};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};AT.prototype.Split=function(a,b){return this.$val.Split(a,b);};BU.methods=[{prop:"reset",name:"reset",pkg:"regexp",typ:$funcType([CM,$Int,$Int],[],false)},{prop:"shouldVisit",name:"shouldVisit",pkg:"regexp",typ:$funcType([$Uint32,$Int],[$Bool],false)},{prop:"push",name:"push",pkg:"regexp",typ:$funcType([CL,$Uint32,$Int,$Bool],[],false)}];CO.methods=[{prop:"init",name:"init",pkg:"regexp",typ:$funcType([$Int],[],false)},{prop:"alloc",name:"alloc",pkg:"regexp",typ:$funcType([BZ],[BY],false)},{prop:"match",name:"match",pkg:"regexp",typ:$funcType([BC,$Int],[$Bool],false)},{prop:"clear",name:"clear",pkg:"regexp",typ:$funcType([CX],[],false)},{prop:"step",name:"step",pkg:"regexp",typ:$funcType([CX,CX,$Int,$Int,$Int32,CA],[],false)},{prop:"add",name:"add",pkg:"regexp",typ:$funcType([CX,$Uint32,$Int,BV,CA,BY],[BY],false)}];CY.methods=[{prop:"newBytes",name:"newBytes",pkg:"regexp",typ:$funcType([BX],[BC],false)},{prop:"newString",name:"newString",pkg:"regexp",typ:$funcType([$String],[BC],false)},{prop:"newReader",name:"newReader",pkg:"regexp",typ:$funcType([C.RuneReader],[BC],false)},{prop:"clear",name:"clear",pkg:"regexp",typ:$funcType([],[],false)},{prop:"init",name:"init",pkg:"regexp",typ:$funcType([C.RuneReader,BX,$String],[BC,$Int],false)}];V.methods=[{prop:"match",name:"match",pkg:"regexp",typ:$funcType([A.EmptyOp],[$Bool],false)}];CF.methods=[{prop:"empty",name:"empty",pkg:"regexp",typ:$funcType([],[$Bool],false)},{prop:"next",name:"next",pkg:"regexp",typ:$funcType([],[$Uint32],false)},{prop:"clear",name:"clear",pkg:"regexp",typ:$funcType([],[],false)},{prop:"contains",name:"contains",pkg:"regexp",typ:$funcType([$Uint32],[$Bool],false)},{prop:"insert",name:"insert",pkg:"regexp",typ:$funcType([$Uint32],[],false)},{prop:"insertNew",name:"insertNew",pkg:"regexp",typ:$funcType([$Uint32],[],false)}];AO.methods=[{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Less",name:"Less",pkg:"",typ:$funcType([$Int,$Int],[$Bool],false)},{prop:"Swap",name:"Swap",pkg:"",typ:$funcType([$Int,$Int],[],false)}];CL.methods=[{prop:"tryBacktrack",name:"tryBacktrack",pkg:"regexp",typ:$funcType([BU,BC,$Uint32,$Int],[$Bool],false)},{prop:"backtrack",name:"backtrack",pkg:"regexp",typ:$funcType([BX,$String,$Int,$Int,BV],[BV],false)},{prop:"doOnePass",name:"doOnePass",pkg:"regexp",typ:$funcType([C.RuneReader,BX,$String,$Int,$Int,BV],[BV],false)},{prop:"doMatch",name:"doMatch",pkg:"regexp",typ:$funcType([C.RuneReader,BX,$String],[$Bool],false)},{prop:"doExecute",name:"doExecute",pkg:"regexp",typ:$funcType([C.RuneReader,BX,$String,$Int,$Int,BV],[BV],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"Copy",name:"Copy",pkg:"",typ:$funcType([],[CL],false)},{prop:"Longest",name:"Longest",pkg:"",typ:$funcType([],[],false)},{prop:"get",name:"get",pkg:"regexp",typ:$funcType([],[CO],false)},{prop:"put",name:"put",pkg:"regexp",typ:$funcType([CO],[],false)},{prop:"NumSubexp",name:"NumSubexp",pkg:"",typ:$funcType([],[$Int],false)},{prop:"SubexpNames",name:"SubexpNames",pkg:"",typ:$funcType([],[CN],false)},{prop:"LiteralPrefix",name:"LiteralPrefix",pkg:"",typ:$funcType([],[$String,$Bool],false)},{prop:"MatchReader",name:"MatchReader",pkg:"",typ:$funcType([C.RuneReader],[$Bool],false)},{prop:"MatchString",name:"MatchString",pkg:"",typ:$funcType([$String],[$Bool],false)},{prop:"Match",name:"Match",pkg:"",typ:$funcType([BX],[$Bool],false)},{prop:"ReplaceAllString",name:"ReplaceAllString",pkg:"",typ:$funcType([$String,$String],[$String],false)},{prop:"ReplaceAllLiteralString",name:"ReplaceAllLiteralString",pkg:"",typ:$funcType([$String,$String],[$String],false)},{prop:"ReplaceAllStringFunc",name:"ReplaceAllStringFunc",pkg:"",typ:$funcType([$String,CZ],[$String],false)},{prop:"replaceAll",name:"replaceAll",pkg:"regexp",typ:$funcType([BX,$String,$Int,DA],[BX],false)},{prop:"ReplaceAll",name:"ReplaceAll",pkg:"",typ:$funcType([BX,BX],[BX],false)},{prop:"ReplaceAllLiteral",name:"ReplaceAllLiteral",pkg:"",typ:$funcType([BX,BX],[BX],false)},{prop:"ReplaceAllFunc",name:"ReplaceAllFunc",pkg:"",typ:$funcType([BX,DB],[BX],false)},{prop:"pad",name:"pad",pkg:"regexp",typ:$funcType([BV],[BV],false)},{prop:"allMatches",name:"allMatches",pkg:"regexp",typ:$funcType([$String,BX,$Int,DC],[],false)},{prop:"Find",name:"Find",pkg:"",typ:$funcType([BX],[BX],false)},{prop:"FindIndex",name:"FindIndex",pkg:"",typ:$funcType([BX],[BV],false)},{prop:"FindString",name:"FindString",pkg:"",typ:$funcType([$String],[$String],false)},{prop:"FindStringIndex",name:"FindStringIndex",pkg:"",typ:$funcType([$String],[BV],false)},{prop:"FindReaderIndex",name:"FindReaderIndex",pkg:"",typ:$funcType([C.RuneReader],[BV],false)},{prop:"FindSubmatch",name:"FindSubmatch",pkg:"",typ:$funcType([BX],[CT],false)},{prop:"Expand",name:"Expand",pkg:"",typ:$funcType([BX,BX,BX,BV],[BX],false)},{prop:"ExpandString",name:"ExpandString",pkg:"",typ:$funcType([BX,$String,$String,BV],[BX],false)},{prop:"expand",name:"expand",pkg:"regexp",typ:$funcType([BX,$String,BX,$String,BV],[BX],false)},{prop:"FindSubmatchIndex",name:"FindSubmatchIndex",pkg:"",typ:$funcType([BX],[BV],false)},{prop:"FindStringSubmatch",name:"FindStringSubmatch",pkg:"",typ:$funcType([$String],[CN],false)},{prop:"FindStringSubmatchIndex",name:"FindStringSubmatchIndex",pkg:"",typ:$funcType([$String],[BV],false)},{prop:"FindReaderSubmatchIndex",name:"FindReaderSubmatchIndex",pkg:"",typ:$funcType([C.RuneReader],[BV],false)},{prop:"FindAll",name:"FindAll",pkg:"",typ:$funcType([BX,$Int],[CT],false)},{prop:"FindAllIndex",name:"FindAllIndex",pkg:"",typ:$funcType([BX,$Int],[CU],false)},{prop:"FindAllString",name:"FindAllString",pkg:"",typ:$funcType([$String,$Int],[CN],false)},{prop:"FindAllStringIndex",name:"FindAllStringIndex",pkg:"",typ:$funcType([$String,$Int],[CU],false)},{prop:"FindAllSubmatch",name:"FindAllSubmatch",pkg:"",typ:$funcType([BX,$Int],[CV],false)},{prop:"FindAllSubmatchIndex",name:"FindAllSubmatchIndex",pkg:"",typ:$funcType([BX,$Int],[CU],false)},{prop:"FindAllStringSubmatch",name:"FindAllStringSubmatch",pkg:"",typ:$funcType([$String,$Int],[CW],false)},{prop:"FindAllStringSubmatchIndex",name:"FindAllStringSubmatchIndex",pkg:"",typ:$funcType([$String,$Int],[CU],false)},{prop:"Split",name:"Split",pkg:"",typ:$funcType([$String,$Int],[CN],false)}];DD.methods=[{prop:"step",name:"step",pkg:"regexp",typ:$funcType([$Int],[$Int32,$Int],false)},{prop:"canCheckPrefix",name:"canCheckPrefix",pkg:"regexp",typ:$funcType([],[$Bool],false)},{prop:"hasPrefix",name:"hasPrefix",pkg:"regexp",typ:$funcType([CL],[$Bool],false)},{prop:"index",name:"index",pkg:"regexp",typ:$funcType([CL,$Int],[$Int],false)},{prop:"context",name:"context",pkg:"regexp",typ:$funcType([$Int],[V],false)}];DE.methods=[{prop:"step",name:"step",pkg:"regexp",typ:$funcType([$Int],[$Int32,$Int],false)},{prop:"canCheckPrefix",name:"canCheckPrefix",pkg:"regexp",typ:$funcType([],[$Bool],false)},{prop:"hasPrefix",name:"hasPrefix",pkg:"regexp",typ:$funcType([CL],[$Bool],false)},{prop:"index",name:"index",pkg:"regexp",typ:$funcType([CL,$Int],[$Int],false)},{prop:"context",name:"context",pkg:"regexp",typ:$funcType([$Int],[V],false)}];DF.methods=[{prop:"step",name:"step",pkg:"regexp",typ:$funcType([$Int],[$Int32,$Int],false)},{prop:"canCheckPrefix",name:"canCheckPrefix",pkg:"regexp",typ:$funcType([],[$Bool],false)},{prop:"hasPrefix",name:"hasPrefix",pkg:"regexp",typ:$funcType([CL],[$Bool],false)},{prop:"index",name:"index",pkg:"regexp",typ:$funcType([CL,$Int],[$Int],false)},{prop:"context",name:"context",pkg:"regexp",typ:$funcType([$Int],[V],false)}];J.init("regexp",[{prop:"pc",name:"pc",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"arg",name:"arg",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"pos",name:"pos",embedded:false,exported:false,typ:$Int,tag:""}]);K.init("regexp",[{prop:"end",name:"end",embedded:false,exported:false,typ:$Int,tag:""},{prop:"cap",name:"cap",embedded:false,exported:false,typ:BV,tag:""},{prop:"matchcap",name:"matchcap",embedded:false,exported:false,typ:BV,tag:""},{prop:"jobs",name:"jobs",embedded:false,exported:false,typ:BW,tag:""},{prop:"visited",name:"visited",embedded:false,exported:false,typ:BT,tag:""},{prop:"inputs",name:"inputs",embedded:false,exported:false,typ:U,tag:""}]);Q.init("regexp",[{prop:"sparse",name:"sparse",embedded:false,exported:false,typ:BT,tag:""},{prop:"dense",name:"dense",embedded:false,exported:false,typ:CP,tag:""}]);R.init("regexp",[{prop:"pc",name:"pc",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"t",name:"t",embedded:false,exported:false,typ:BY,tag:""}]);S.init("regexp",[{prop:"inst",name:"inst",embedded:false,exported:false,typ:BZ,tag:""},{prop:"cap",name:"cap",embedded:false,exported:false,typ:BV,tag:""}]);T.init("regexp",[{prop:"re",name:"re",embedded:false,exported:false,typ:CL,tag:""},{prop:"p",name:"p",embedded:false,exported:false,typ:CM,tag:""},{prop:"q0",name:"q0",embedded:false,exported:false,typ:Q,tag:""},{prop:"q1",name:"q1",embedded:false,exported:false,typ:Q,tag:""},{prop:"pool",name:"pool",embedded:false,exported:false,typ:CQ,tag:""},{prop:"matched",name:"matched",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"matchcap",name:"matchcap",embedded:false,exported:false,typ:BV,tag:""},{prop:"inputs",name:"inputs",embedded:false,exported:false,typ:U,tag:""}]);U.init("regexp",[{prop:"bytes",name:"bytes",embedded:false,exported:false,typ:BE,tag:""},{prop:"string",name:"string",embedded:false,exported:false,typ:BD,tag:""},{prop:"reader",name:"reader",embedded:false,exported:false,typ:BF,tag:""}]);X.init("regexp",[{prop:"inputs",name:"inputs",embedded:false,exported:false,typ:U,tag:""},{prop:"matchcap",name:"matchcap",embedded:false,exported:false,typ:BV,tag:""}]);AC.init("",[{prop:"Inst",name:"Inst",embedded:false,exported:true,typ:CG,tag:""},{prop:"Start",name:"Start",embedded:false,exported:true,typ:$Int,tag:""},{prop:"NumCap",name:"NumCap",embedded:false,exported:true,typ:$Int,tag:""}]);AD.init("",[{prop:"Inst",name:"Inst",embedded:true,exported:true,typ:A.Inst,tag:""},{prop:"Next",name:"Next",embedded:false,exported:true,typ:BT,tag:""}]);AH.init("regexp",[{prop:"sparse",name:"sparse",embedded:false,exported:false,typ:BT,tag:""},{prop:"dense",name:"dense",embedded:false,exported:false,typ:BT,tag:""},{prop:"size",name:"size",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"nextIndex",name:"nextIndex",embedded:false,exported:false,typ:$Uint32,tag:""}]);AO.init($Int32);AT.init("regexp",[{prop:"expr",name:"expr",embedded:false,exported:false,typ:$String,tag:""},{prop:"prog",name:"prog",embedded:false,exported:false,typ:CM,tag:""},{prop:"onepass",name:"onepass",embedded:false,exported:false,typ:CD,tag:""},{prop:"numSubexp",name:"numSubexp",embedded:false,exported:false,typ:$Int,tag:""},{prop:"maxBitStateLen",name:"maxBitStateLen",embedded:false,exported:false,typ:$Int,tag:""},{prop:"subexpNames",name:"subexpNames",embedded:false,exported:false,typ:CN,tag:""},{prop:"prefix",name:"prefix",embedded:false,exported:false,typ:$String,tag:""},{prop:"prefixBytes",name:"prefixBytes",embedded:false,exported:false,typ:BX,tag:""},{prop:"prefixRune",name:"prefixRune",embedded:false,exported:false,typ:$Int32,tag:""},{prop:"prefixEnd",name:"prefixEnd",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"mpool",name:"mpool",embedded:false,exported:false,typ:$Int,tag:""},{prop:"matchcap",name:"matchcap",embedded:false,exported:false,typ:$Int,tag:""},{prop:"prefixComplete",name:"prefixComplete",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"cond",name:"cond",embedded:false,exported:false,typ:A.EmptyOp,tag:""},{prop:"longest",name:"longest",embedded:false,exported:false,typ:$Bool,tag:""}]);BC.init([{prop:"canCheckPrefix",name:"canCheckPrefix",pkg:"regexp",typ:$funcType([],[$Bool],false)},{prop:"context",name:"context",pkg:"regexp",typ:$funcType([$Int],[V],false)},{prop:"hasPrefix",name:"hasPrefix",pkg:"regexp",typ:$funcType([CL],[$Bool],false)},{prop:"index",name:"index",pkg:"regexp",typ:$funcType([CL,$Int],[$Int],false)},{prop:"step",name:"step",pkg:"regexp",typ:$funcType([$Int],[$Int32,$Int],false)}]);BD.init("regexp",[{prop:"str",name:"str",embedded:false,exported:false,typ:$String,tag:""}]);BE.init("regexp",[{prop:"str",name:"str",embedded:false,exported:false,typ:BX,tag:""}]);BF.init("regexp",[{prop:"r",name:"r",embedded:false,exported:false,typ:C.RuneReader,tag:""},{prop:"atEOT",name:"atEOT",embedded:false,exported:false,typ:$Bool,tag:""},{prop:"pos",name:"pos",embedded:false,exported:false,typ:$Int,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=G.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=H.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=I.$init();$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}L=new B.Pool.ptr(BO.nil,$throwNilPointerError);Y=new B.Pool.ptr(BO.nil,$throwNilPointerError);AB=BP.zero();AY=BQ.zero();BJ=BR.zero();AJ=new BS([]);AK=new BT([4294967295]);AP=new BS([0,9,11,1114111]);AQ=new BS([0,1114111]);AX=$toNativeArray($kindInt,[128,512,2048,16384,0]);BL();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc"]=(function(){var $pkg={},$init,F,A,I,G,B,D,C,J,E,H,K,L,M,N,O,Q,R,V,AF,AP,AT,AU,AW,AY,AZ,BA,BB,BC,BD,BE,BF,BG,X,AA,AB,AC,AG,AQ,AR,AV,a,W,Y,Z,AD,AE,AH,AI,AL,AM,AS;F=$packages["bytes"];A=$packages["encoding/binary"];I=$packages["errors"];G=$packages["fmt"];B=$packages["io"];D=$packages["math"];C=$packages["reflect"];J=$packages["regexp"];E=$packages["strconv"];H=$packages["strings"];K=$packages["sync"];L=$pkg.byteWriter=$newType(0,$kindStruct,"struc.byteWriter",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",false,function(buf_,pos_){this.$val=this;if(arguments.length===0){this.buf=AW.nil;this.pos=0;return;}this.buf=buf_;this.pos=pos_;});M=$pkg.binaryFallback=$newType(0,$kindStruct,"struc.binaryFallback",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",false,function(typ_,ptr_,flag_){this.$val=this;if(arguments.length===0){this.typ=BB.nil;this.ptr=0;this.flag=0;return;}this.typ=typ_;this.ptr=ptr_;this.flag=flag_;});N=$pkg.Custom=$newType(8,$kindInterface,"struc.Custom",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",true,null);O=$pkg.customFallback=$newType(0,$kindStruct,"struc.customFallback",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",false,function(custom_){this.$val=this;if(arguments.length===0){this.custom=$ifaceNil;return;}this.custom=custom_;});Q=$pkg.Field=$newType(0,$kindStruct,"struc.Field",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",true,function(Name_,Ptr_,Index_,Type_,defType_,Array_,Slice_,Len_,Order_,Sizeof_,Sizefrom_,Fields_,kind_){this.$val=this;if(arguments.length===0){this.Name="";this.Ptr=false;this.Index=0;this.Type=0;this.defType=0;this.Array=false;this.Slice=false;this.Len=0;this.Order=$ifaceNil;this.Sizeof=AZ.nil;this.Sizefrom=AZ.nil;this.Fields=R.nil;this.kind=0;return;}this.Name=Name_;this.Ptr=Ptr_;this.Index=Index_;this.Type=Type_;this.defType=defType_;this.Array=Array_;this.Slice=Slice_;this.Len=Len_;this.Order=Order_;this.Sizeof=Sizeof_;this.Sizefrom=Sizefrom_;this.Fields=Fields_;this.kind=kind_;});R=$pkg.Fields=$newType(12,$kindSlice,"struc.Fields",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",true,null);V=$pkg.strucTag=$newType(0,$kindStruct,"struc.strucTag",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",false,function(Type_,Order_,Sizeof_,Skip_,Sizefrom_){this.$val=this;if(arguments.length===0){this.Type="";this.Order=$ifaceNil;this.Sizeof="";this.Skip=false;this.Sizefrom="";return;}this.Type=Type_;this.Order=Order_;this.Sizeof=Sizeof_;this.Skip=Skip_;this.Sizefrom=Sizefrom_;});AF=$pkg.Options=$newType(0,$kindStruct,"struc.Options",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",true,function(ByteAlign_,PtrSize_,Order_){this.$val=this;if(arguments.length===0){this.ByteAlign=0;this.PtrSize=0;this.Order=$ifaceNil;return;}this.ByteAlign=ByteAlign_;this.PtrSize=PtrSize_;this.Order=Order_;});AP=$pkg.Type=$newType(4,$kindInt,"struc.Type",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",true,null);AT=$pkg.Size_t=$newType(8,$kindUint64,"struc.Size_t",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",true,null);AU=$pkg.Off_t=$newType(8,$kindInt64,"struc.Off_t",true,"github.com/v2fly/BrowserBridge/vendor/github.com/lunixbochs/struc",true,null);AW=$sliceType($Uint8);AY=$sliceType($emptyInterface);AZ=$sliceType($Int);BA=$sliceType(C.Value);BB=$ptrType(C.rtype);BC=$ptrType(Q);BD=$sliceType($String);BE=$arrayType($Uint8,8);BF=$ptrType(V);BG=$ptrType(AF);L.ptr.prototype.Write=function(b){var b,c,d;c=this;d=c.buf.$length-c.pos>>0;if(d0){$copySlice($subslice(c.buf,c.pos),b);c.pos=c.pos+(b.$length)>>0;}return[b.$length,$ifaceNil];};L.prototype.Write=function(b){return this.$val.Write(b);};M.ptr.prototype.String=function(){var b;b=this;return $clone(b,M).String();};M.prototype.String=function(){return this.$val.String();};M.ptr.prototype.Sizeof=function(b,c){var b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=$clone(b,C.Value).Interface();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=A.Size(e);$s=2;case 2:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$s=-1;return f;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Sizeof};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Sizeof=function(b,c){return this.$val.Sizeof(b,c);};M.ptr.prototype.Pack=function(b,c,d){var b,c,d,e,f,g,h,i,j,k,l,m,n,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;f=new L.ptr(b,0);h=(g=A.BigEndian,new g.constructor.elem(g));if(!($interfaceIsEqual(d.Order,$ifaceNil))){h=d.Order;}i=new f.constructor.elem(f);j=h;k=$clone(c,C.Value).Interface();$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}l=k;m=A.Write(i,j,l);$s=2;case 2:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}n=m;$s=-1;return[f.pos,n];}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Pack};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Pack=function(b,c,d){return this.$val.Pack(b,c,d);};M.ptr.prototype.Unpack=function(b,c,d){var b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;g=(f=A.BigEndian,new f.constructor.elem(f));if(!($interfaceIsEqual(d.Order,$ifaceNil))){g=d.Order;}h=b;i=g;j=$clone(c,C.Value).Interface();$s=1;case 1:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}k=j;l=A.Read(h,i,k);$s=2;case 2:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}$s=-1;return l;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Unpack};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Unpack=function(b,c,d){return this.$val.Unpack(b,c,d);};O.ptr.prototype.Pack=function(b,c,d){var b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;f=e.custom.Pack(b,d);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$s=-1;return f;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.Pack};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.Pack=function(b,c,d){return this.$val.Pack(b,c,d);};O.ptr.prototype.Unpack=function(b,c,d){var b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;f=e.custom.Unpack(b,1,d);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$s=-1;return f;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.Unpack};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.Unpack=function(b,c,d){return this.$val.Unpack(b,c,d);};O.ptr.prototype.Sizeof=function(b,c){var b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=d.custom.Size(c);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.Sizeof};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.Sizeof=function(b,c){return this.$val.Sizeof(b,c);};O.ptr.prototype.String=function(){var b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=b.custom.String();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return c;}return;}if($f===undefined){$f={$blk:O.ptr.prototype.String};}$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};O.prototype.String=function(){return this.$val.String();};Q.ptr.prototype.String=function(){var b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c="";if(b.Type===1){$s=1;continue;}$s=2;continue;case 1:d=G.Sprintf("{type: Pad, len: %d}",new AY([new $Int(b.Len)]));$s=4;case 4:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;case 2:e=G.Sprintf("type: %s, order: %v",new AY([new $String(new AP(b.Type).String()),b.Order]));$s=5;case 5:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}c=e;case 3:if(!(b.Sizefrom===AZ.nil)){$s=6;continue;}if(b.Len>0){$s=7;continue;}$s=8;continue;case 6:f=G.Sprintf(", sizefrom: %v",new AY([b.Sizefrom]));$s=9;case 9:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}c=c+(f);$s=8;continue;case 7:g=G.Sprintf(", len: %d",new AY([new $Int(b.Len)]));$s=10;case 10:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}c=c+(g);case 8:if(!(b.Sizeof===AZ.nil)){$s=11;continue;}$s=12;continue;case 11:h=G.Sprintf(", sizeof: %v",new AY([b.Sizeof]));$s=13;case 13:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}c=c+(h);case 12:$s=-1;return"{"+c+"}";}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.String};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.String=function(){return this.$val.String();};Q.ptr.prototype.Size=function(b,c){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=new AP(d.Type).Resolve(c);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;g=0;if(f===15){$s=2;continue;}if(f===1){$s=3;continue;}if(d.Slice||(d.kind===24)){$s=4;continue;}if(f===19){$s=5;continue;}$s=6;continue;case 2:h=new BA([$clone(b,C.Value)]);if(d.Slice){$s=8;continue;}$s=9;continue;case 8:h=$makeSlice(BA,$clone(b,C.Value).Len());i=0;case 10:if(!(i<$clone(b,C.Value).Len())){$s=11;continue;}j=$clone(b,C.Value).Index(i);$s=12;case 12:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}((i<0||i>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]=j);i=i+(1)>>0;$s=10;continue;case 11:case 9:k=h;l=0;case 13:if(!(l=k.$length)?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+l]);n=d.Fields.Sizeof($clone(m,C.Value),c);$s=15;case 15:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}g=g+(n)>>0;l++;$s=13;continue;case 14:$s=7;continue;case 3:g=d.Len;$s=7;continue;case 4:o=$clone(b,C.Value).Len();if(d.Len>1){o=d.Len;}g=$imul(o,new AP(f).Size());$s=7;continue;case 5:p=$clone($clone(b,C.Value).Addr(),C.Value).Interface();$s=16;case 16:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=$assertType(p,N).Size(c);$s=17;case 17:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}$s=-1;return q;case 6:g=new AP(f).Size();case 7:r=c.ByteAlign;if(r>0&&g=b.$length?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+0]=1);}else{(0>=b.$length?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+0]=0);}$s=20;continue;case 16:(0>=b.$length?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+0]=((p.$low<<24>>>24)));$s=20;continue;case 17:$r=i.PutUint16(b,((p.$low<<16>>>16)));$s=21;case 21:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=20;continue;case 18:$r=i.PutUint32(b,((p.$low>>>0)));$s=22;case 22:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=20;continue;case 19:$r=i.PutUint64(b,(p));$s=23;case 23:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 20:case 14:$s=12;continue;case 8:f=new AP(l).Size();t=$clone(c,C.Value).Float();u=l;if(u===(12)){$s=25;continue;}if(u===(13)){$s=26;continue;}$s=27;continue;case 25:$r=i.PutUint32(b,D.Float32bits(($fround(t))));$s=28;case 28:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=27;continue;case 26:$r=i.PutUint64(b,D.Float64bits(t));$s=29;case 29:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 27:case 24:$s=12;continue;case 9:v=h.kind;if(v===(24)){$s=31;continue;}$s=32;continue;case 31:f=$clone(c,C.Value).Len();w=$clone(c,C.Value).String();$s=34;case 34:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}$copySlice(b,(new AW($stringToBytes(w))));$s=33;continue;case 32:f=$clone(c,C.Value).Len();x=$clone(c,C.Value).Bytes();$s=35;case 35:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}$copySlice(b,x);case 33:case 30:$s=12;continue;case 10:z=$clone($clone(c,C.Value).Addr(),C.Value).Interface();$s=36;case 36:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}aa=$assertType(z,N).Pack(b,e);$s=37;case 37:if($c){$c=false;aa=aa.$blk();}if(aa&&aa.$blk!==undefined){break s;}y=aa;f=y[0];g=y[1];$s=-1;return[f,g];case 11:ab=G.Sprintf("no pack handler for type: %s",new AY([new AP(l)]));$s=38;case 38:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}$panic(new $String(ab));case 12:case 5:$s=-1;return[f,g];}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.packVal};}$f.aa=aa;$f.ab=ab;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.packVal=function(b,c,d,e){return this.$val.packVal(b,c,d,e);};Q.ptr.prototype.Pack=function(b,c,d,e){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:f=this;g=new AP(f.Type).Resolve(e);$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;if(h===1){i=0;while(true){if(!(i=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+i]=0);i=i+(1)>>0;}$s=-1;return[d,$ifaceNil];}if(f.Slice){$s=2;continue;}$s=3;continue;case 2:j=$clone(c,C.Value).Len();if(!f.Array&&(h===5)&&((f.defType===5)||(f.kind===24))){$s=5;continue;}$s=6;continue;case 5:k=AW.nil;if(f.kind===24){$s=7;continue;}$s=8;continue;case 7:l=$clone(c,C.Value).String();$s=10;case 10:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}k=(new AW($stringToBytes(l)));$s=9;continue;case 8:m=$clone(c,C.Value).Bytes();$s=11;case 11:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}k=m;case 9:$copySlice(b,k);if(j>0);$copySlice($subslice(b,j),n);$s=-1;return[d,$ifaceNil];}$s=-1;return[$clone(c,C.Value).Len(),$ifaceNil];case 6:o=0;p=new C.Value.ptr(BB.nil,0,0);if(j>0;}s=s+(1)>>0;$s=16;continue;case 17:$s=-1;return[o,$ifaceNil];case 3:z=f.packVal(b,$clone(c,C.Value),d,e);$s=22;case 22:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}$s=-1;return z;case 4:$s=-1;return[0,$ifaceNil];}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.Pack};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.Pack=function(b,c,d,e){return this.$val.Pack(b,c,d,e);};Q.ptr.prototype.unpackVal=function(b,c,d,e){var aa,ab,ac,ad,ae,af,ag,ah,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:f=this;g=f.Order;if(!($interfaceIsEqual(e.Order,$ifaceNil))){g=e.Order;}if(f.Ptr){$s=1;continue;}$s=2;continue;case 1:h=$clone(c,C.Value).Elem();$s=3;case 3:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}c=h;case 2:i=new AP(f.Type).Resolve(e);$s=4;case 4:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}j=i;k=j;if((k===(12))||(k===(13))){$s=6;continue;}if((k===(2))||(k===(4))||(k===(6))||(k===(8))||(k===(10))||(k===(5))||(k===(7))||(k===(9))||(k===(11))){$s=7;continue;}$s=8;continue;case 6:l=0;m=j;if(m===(12)){$s=11;continue;}if(m===(13)){$s=12;continue;}$s=13;continue;case 11:n=g.Uint32(b);$s=14;case 14:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}o=D.Float32frombits(n);$s=15;case 15:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}l=(o);$s=13;continue;case 12:p=g.Uint64(b);$s=16;case 16:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=D.Float64frombits(p);$s=17;case 17:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}l=q;case 13:case 10:r=f.kind;if((r===(13))||(r===(14))){$s=19;continue;}$s=20;continue;case 19:$clone(c,C.Value).SetFloat(l);$s=21;continue;case 20:s=G.Errorf("struc: refusing to unpack float into field %s of type %s",new AY([new $String(f.Name),new $String(new C.Kind(f.kind).String())]));$s=22;case 22:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}$s=-1;return s;case 21:case 18:$s=9;continue;case 7:t=new $Uint64(0,0);u=j;if(u===(4)){$s=24;continue;}if(u===(6)){$s=25;continue;}if(u===(8)){$s=26;continue;}if(u===(10)){$s=27;continue;}if((u===(2))||(u===(5))){$s=28;continue;}if(u===(7)){$s=29;continue;}if(u===(9)){$s=30;continue;}if(u===(11)){$s=31;continue;}$s=32;continue;case 24:t=((v=(new $Int64(0,(((0>=b.$length?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+0])<<24>>24)))),new $Uint64(v.$high,v.$low)));$s=32;continue;case 25:x=g.Uint16(b);$s=33;case 33:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}t=((w=(new $Int64(0,((x<<16>>16)))),new $Uint64(w.$high,w.$low)));$s=32;continue;case 26:z=g.Uint32(b);$s=34;case 34:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}t=((y=(new $Int64(0,((z>>0)))),new $Uint64(y.$high,y.$low)));$s=32;continue;case 27:ac=g.Uint64(b);$s=35;case 35:if($c){$c=false;ac=ac.$blk();}if(ac&&ac.$blk!==undefined){break s;}t=((aa=((ab=ac,new $Int64(ab.$high,ab.$low))),new $Uint64(aa.$high,aa.$low)));$s=32;continue;case 28:t=(new $Uint64(0,(0>=b.$length?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+0])));$s=32;continue;case 29:ad=g.Uint16(b);$s=36;case 36:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}t=(new $Uint64(0,ad));$s=32;continue;case 30:ae=g.Uint32(b);$s=37;case 37:if($c){$c=false;ae=ae.$blk();}if(ae&&ae.$blk!==undefined){break s;}t=(new $Uint64(0,ae));$s=32;continue;case 31:af=g.Uint64(b);$s=38;case 38:if($c){$c=false;af=af.$blk();}if(af&&af.$blk!==undefined){break s;}t=(af);case 32:case 23:ag=f.kind;if(ag===(1)){$clone(c,C.Value).SetBool(!((t.$high===0&&t.$low===0)));}else if((ag===(2))||(ag===(3))||(ag===(4))||(ag===(5))||(ag===(6))){$clone(c,C.Value).SetInt((new $Int64(t.$high,t.$low)));}else{$clone(c,C.Value).SetUint(t);}$s=9;continue;case 8:ah=G.Sprintf("no unpack handler for type: %s",new AY([new AP(j)]));$s=39;case 39:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}$panic(new $String(ah));case 9:case 5:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.unpackVal};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.unpackVal=function(b,c,d,e){return this.$val.unpackVal(b,c,d,e);};Q.ptr.prototype.Unpack=function(b,c,d,e){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:f=this;g=new AP(f.Type).Resolve(e);$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;if((h===1)||(f.kind===24)){$s=2;continue;}if(f.Slice){$s=3;continue;}$s=4;continue;case 2:if(h===1){$s=-1;return $ifaceNil;}else{$clone(c,C.Value).SetString(($bytesToString(b)));$s=-1;return $ifaceNil;}$s=5;continue;case 3:if($clone(c,C.Value).Cap()>0));p=$clone(c,C.Value).Index(n);$s=18;case 18:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=$clone(p,C.Value);r=e;s=f.unpackVal(o,q,1,r);$s=19;case 19:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}t=s;if(!($interfaceIsEqual(t,$ifaceNil))){$s=-1;return t;}l=l+(m)>>0;n=n+(1)>>0;$s=16;continue;case 17:$s=-1;return $ifaceNil;case 4:u=f.unpackVal(b,$clone(c,C.Value),d,e);$s=20;case 20:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}$s=-1;return u;case 5:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:Q.ptr.prototype.Unpack};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.$s=$s;$f.$r=$r;return $f;};Q.prototype.Unpack=function(b,c,d,e){return this.$val.Unpack(b,c,d,e);};R.prototype.SetByteOrder=function(b){var b,c,d,e,f;c=this;d=c;e=0;while(true){if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);if(!(f===BC.nil)){f.Order=b;}e++;}};$ptrType(R).prototype.SetByteOrder=function(b){return this.$get().SetByteOrder(b);};R.prototype.String=function(){var b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=$makeSlice(BD,b.$length);d=b;e=0;case 1:if(!(e=d.$length)?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+e]);if(!(g===BC.nil)){$s=3;continue;}$s=4;continue;case 3:h=g.String();$s=5;case 5:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=h);case 4:e++;$s=1;continue;case 2:$s=-1;return"{"+H.Join(c,", ")+"}";}return;}if($f===undefined){$f={$blk:R.prototype.String};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(R).prototype.String=function(){return this.$get().String();};R.prototype.Sizeof=function(b,c){var b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;case 1:if(!($clone(b,C.Value).Kind()===22)){$s=2;continue;}e=$clone(b,C.Value).Elem();$s=3;case 3:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}b=e;$s=1;continue;case 2:f=0;g=d;h=0;case 4:if(!(h=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+h]);if(!(j===BC.nil)){$s=6;continue;}$s=7;continue;case 6:k=$clone(b,C.Value).Field(i);$s=8;case 8:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}l=j.Size($clone(k,C.Value),c);$s=9;case 9:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}f=f+(l)>>0;case 7:h++;$s=4;continue;case 5:$s=-1;return f;}return;}if($f===undefined){$f={$blk:R.prototype.Sizeof};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(R).prototype.Sizeof=function(b,c){return this.$get().Sizeof(b,c);};R.prototype.sizefrom=function(b,c){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=$clone(b,C.Value).FieldByIndex(c);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;g=$clone(f,C.Value).Kind();if((g===(2))||(g===(3))||(g===(4))||(g===(5))||(g===(6))){$s=3;continue;}if((g===(7))||(g===(8))||(g===(9))||(g===(10))||(g===(11))){$s=4;continue;}$s=5;continue;case 3:$s=-1;return(((h=$clone(f,C.Value).Int(),h.$low+((h.$high>>31)*4294967296))>>0));case 4:i=(($clone(f,C.Value).Uint().$low>>0));if(i<0){$s=-1;return 0;}$s=-1;return i;case 5:j=$clone(b,C.Value).Type().FieldByIndex(c);$s=7;case 7:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}k=j.Name;l=$clone(b,C.Value).Interface();$s=8;case 8:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}m=l;n=new $String(k);o=G.Sprintf("sizeof field %T.%s not an integer type",new AY([m,n]));$s=9;case 9:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}$panic(new $String(o));case 6:case 2:$s=-1;return 0;}return;}if($f===undefined){$f={$blk:R.prototype.sizefrom};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(R).prototype.sizefrom=function(b,c){return this.$get().sizefrom(b,c);};R.prototype.Pack=function(b,c,d){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;case 1:if(!($clone(c,C.Value).Kind()===22)){$s=2;continue;}f=$clone(c,C.Value).Elem();$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}c=f;$s=1;continue;case 2:g=0;h=e;i=0;case 4:if(!(i=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]);if(k===BC.nil){$s=6;continue;}$s=7;continue;case 6:i++;$s=4;continue;case 7:l=$clone(c,C.Value).Field(j);$s=8;case 8:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}m=l;n=k.Len;if(!(k.Sizefrom===AZ.nil)){$s=9;continue;}$s=10;continue;case 9:o=e.sizefrom($clone(c,C.Value),k.Sizefrom);$s=11;case 11:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;case 10:if(n<=0&&k.Slice){n=$clone(m,C.Value).Len();}if(!(k.Sizeof===AZ.nil)){$s=12;continue;}$s=13;continue;case 12:p=$clone(c,C.Value).FieldByIndex(k.Sizeof);$s=14;case 14:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=$clone(p,C.Value).Len();$s=15;case 15:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}r=q;s=k.kind;if((s===(2))||(s===(3))||(s===(4))||(s===(5))||(s===(6))){$s=17;continue;}if((s===(7))||(s===(8))||(s===(9))||(s===(10))||(s===(11))){$s=18;continue;}$s=19;continue;case 17:t=$clone(C.New($clone(m,C.Value).Type()),C.Value).Elem();$s=21;case 21:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}m=t;$clone(m,C.Value).SetInt((new $Int64(0,r)));$s=20;continue;case 18:u=$clone(C.New($clone(m,C.Value).Type()),C.Value).Elem();$s=22;case 22:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}m=u;$clone(m,C.Value).SetUint((new $Uint64(0,r)));$s=20;continue;case 19:v=G.Sprintf("sizeof field is not int or uint type: %s, %s",new AY([new $String(k.Name),$clone(m,C.Value).Type()]));$s=23;case 23:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}$panic(new $String(v));case 20:case 16:case 13:x=k.Pack($subslice(b,g),$clone(m,C.Value),n,d);$s=24;case 24:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}w=x;y=w[0];z=w[1];if(!($interfaceIsEqual(z,$ifaceNil))){$s=-1;return[y,z];}else{g=g+(y)>>0;}i++;$s=4;continue;case 5:$s=-1;return[g,$ifaceNil];}return;}if($f===undefined){$f={$blk:R.prototype.Pack};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(R).prototype.Pack=function(b,c,d){return this.$get().Pack(b,c,d);};R.prototype.Unpack=function(b,c,d){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,ao,ap,aq,ar,as,at,au,av,aw,ax,ay,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;ao=$f.ao;ap=$f.ap;aq=$f.aq;ar=$f.ar;as=$f.as;at=$f.at;au=$f.au;av=$f.av;aw=$f.aw;ax=$f.ax;ay=$f.ay;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;case 1:if(!($clone(c,C.Value).Kind()===22)){$s=2;continue;}f=$clone(c,C.Value).Elem();$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}c=f;$s=1;continue;case 2:g=BE.zero();h=AW.nil;i=e;j=0;case 4:if(!(j=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]);if(l===BC.nil){$s=6;continue;}$s=7;continue;case 6:j++;$s=4;continue;case 7:m=$clone(c,C.Value).Field(k);$s=8;case 8:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}n=m;o=l.Len;if(!(l.Sizefrom===AZ.nil)){$s=9;continue;}$s=10;continue;case 9:p=e.sizefrom($clone(c,C.Value),l.Sizefrom);$s=11;case 11:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}o=p;case 10:if(!($clone(n,C.Value).Kind()===22)){q=false;$s=14;continue s;}r=$clone(n,C.Value).Elem();$s=15;case 15:if($c){$c=false;r=r.$blk();}if(r&&r.$blk!==undefined){break s;}s=$clone(r,C.Value).IsValid();$s=16;case 16:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}q=!s;case 14:if(q){$s=12;continue;}$s=13;continue;case 12:t=$clone(n,C.Value).Type().Elem();$s=17;case 17:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}u=C.New(t);$s=18;case 18:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}$r=$clone(n,C.Value).Set($clone(u,C.Value));$s=19;case 19:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 13:if(l.Type===15){$s=20;continue;}$s=21;continue;case 20:if(l.Slice){$s=23;continue;}$s=24;continue;case 23:v=n;if(!l.Array){$s=26;continue;}$s=27;continue;case 26:w=C.MakeSlice($clone(n,C.Value).Type(),o,o);$s=28;case 28:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}v=w;case 27:x=0;case 29:if(!(x>0;$s=29;continue;case 30:if(!l.Array){$s=34;continue;}$s=35;continue;case 34:$r=$clone(n,C.Value).Set($clone(v,C.Value));$s=36;case 36:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 35:$s=25;continue;case 24:ah=AE($clone(n,C.Value));$s=37;case 37:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}ag=ah;ai=ag[0];aj=ag[1];if(!($interfaceIsEqual(aj,$ifaceNil))){$s=-1;return aj;}ak=ai.Unpack(b,$clone(n,C.Value),d);$s=38;case 38:if($c){$c=false;ak=ak.$blk();}if(ak&&ak.$blk!==undefined){break s;}al=ak;if(!($interfaceIsEqual(al,$ifaceNil))){$s=-1;return al;}case 25:j++;$s=4;continue;$s=22;continue;case 21:am=new AP(l.Type).Resolve(d);$s=39;case 39:if($c){$c=false;am=am.$blk();}if(am&&am.$blk!==undefined){break s;}an=am;if(an===19){$s=40;continue;}$s=41;continue;case 40:ao=$clone($clone(n,C.Value).Addr(),C.Value).Interface();$s=43;case 43:if($c){$c=false;ao=ao.$blk();}if(ao&&ao.$blk!==undefined){break s;}ap=$assertType(ao,N).Unpack(b,o,d);$s=44;case 44:if($c){$c=false;ap=ap.$blk();}if(ap&&ap.$blk!==undefined){break s;}aq=ap;if(!($interfaceIsEqual(aq,$ifaceNil))){$s=-1;return aq;}$s=42;continue;case 41:ar=new AP(l.Type).Resolve(d);$s=45;case 45:if($c){$c=false;ar=ar.$blk();}if(ar&&ar.$blk!==undefined){break s;}as=new AP(ar).Size();$s=46;case 46:if($c){$c=false;as=as.$blk();}if(as&&as.$blk!==undefined){break s;}at=$imul(o,as);if(at<8){h=$subslice(new AW(g),0,at);}else{h=$makeSlice(AW,at);}av=B.ReadFull(b,h);$s=47;case 47:if($c){$c=false;av=av.$blk();}if(av&&av.$blk!==undefined){break s;}au=av;aw=au[1];if(!($interfaceIsEqual(aw,$ifaceNil))){$s=-1;return aw;}ax=l.Unpack($subslice(h,0,at),$clone(n,C.Value),o,d);$s=48;case 48:if($c){$c=false;ax=ax.$blk();}if(ax&&ax.$blk!==undefined){break s;}ay=ax;if(!($interfaceIsEqual(ay,$ifaceNil))){$s=-1;return ay;}case 42:case 22:j++;$s=4;continue;case 5:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:R.prototype.Unpack};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.ao=ao;$f.ap=ap;$f.aq=aq;$f.ar=ar;$f.as=as;$f.at=at;$f.au=au;$f.av=av;$f.aw=aw;$f.ax=ax;$f.ay=ay;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(R).prototype.Unpack=function(b,c,d){return this.$get().Unpack(b,c,d);};W=function(b){var b,c,d,e,f,g,h,i,j,k,l;d=new V.ptr("",(c=A.BigEndian,new c.constructor.elem(c)),"",false,"");e=new C.StructTag(b).Get("struc");if(e===""){e=new C.StructTag(b).Get("struct");}f=H.Split(e,",");g=0;while(true){if(!(g=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+g]);if(H.HasPrefix(h,"sizeof=")){i=H.SplitN(h,"=",2);d.Sizeof=(1>=i.$length?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+1]);}else if(H.HasPrefix(h,"sizefrom=")){j=H.SplitN(h,"=",2);d.Sizefrom=(1>=j.$length?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+1]);}else if(h==="big"){d.Order=(k=A.BigEndian,new k.constructor.elem(k));}else if(h==="little"){d.Order=(l=A.LittleEndian,new l.constructor.elem(l));}else if(h==="skip"){d.Skip=true;}else{d.Type=h;}g++;}return d;};Y=function(b){var aa,ab,ac,ad,ae,af,ag,ah,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=BC.nil;d=BF.nil;e=$ifaceNil;d=W(b.Tag);f=false;g=b.Type.Kind();$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}c=new Q.ptr(b.Name,false,0,0,0,false,false,1,d.Order,AZ.nil,AZ.nil,R.nil,g);h=c.kind;if(h===(17)){$s=3;continue;}if(h===(23)){$s=4;continue;}if(h===(22)){$s=5;continue;}$s=6;continue;case 3:c.Slice=true;c.Array=true;i=b.Type.Len();$s=7;case 7:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}c.Len=i;j=b.Type.Elem();$s=8;case 8:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}k=j.Kind();$s=9;case 9:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}c.kind=k;$s=6;continue;case 4:c.Slice=true;c.Len=-1;l=b.Type.Elem();$s=10;case 10:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}m=l.Kind();$s=11;case 11:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}c.kind=m;$s=6;continue;case 5:c.Ptr=true;n=b.Type.Elem();$s=12;case 12:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}o=n.Kind();$s=13;case 13:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}c.kind=o;case 6:case 2:p=C.New(b.Type);r=$clone(p,C.Value).Interface();$s=14;case 14:if($c){$c=false;r=r.$blk();}if(r&&r.$blk!==undefined){break s;}q=$assertType(r,N,true);s=q[1];if(s){c.Type=19;$s=-1;return[c,d,e];}t=false;u=(v=AV[C.Kind.keyFor(c.kind)],v!==undefined?[v.v,true]:[0,false]);c.defType=u[0];t=u[1];w=X.ReplaceAllLiteralString(d.Type,"");$s=15;case 15:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}x=w;y=(z=AQ[$String.keyFor(x)],z!==undefined?[z.v,true]:[0,false]);c.Type=y[0];f=y[1];if(f){$s=16;continue;}$s=17;continue;case 16:c.Len=1;aa=X.FindAllStringSubmatch(d.Type,-1);$s=18;case 18:if($c){$c=false;aa=aa.$blk();}if(aa&&aa.$blk!==undefined){break s;}ab=aa;if(ab.$length>0&&(0>=ab.$length?($throwRuntimeError("index out of range"),undefined):ab.$array[ab.$offset+0]).$length>1){c.Slice=true;ad=(ac=(0>=ab.$length?($throwRuntimeError("index out of range"),undefined):ab.$array[ab.$offset+0]),(1>=ac.$length?($throwRuntimeError("index out of range"),undefined):ac.$array[ac.$offset+1]));if(ad===""){c.Len=-1;}else{ae=E.Atoi(ad);c.Len=ae[0];e=ae[1];}}$s=-1;return[c,d,e];case 17:af=b.Type;if($interfaceIsEqual(af,(C.TypeOf(new AT(0,0))))){$s=20;continue;}if($interfaceIsEqual(af,(C.TypeOf(new AU(0,0))))){$s=21;continue;}if(t){$s=22;continue;}$s=23;continue;case 20:c.Type=17;$s=24;continue;case 21:c.Type=18;$s=24;continue;case 22:c.Type=c.defType;$s=24;continue;case 23:ag=G.Sprintf("struc: Could not resolve field '%v' type '%v'.",new AY([new $String(b.Name),b.Type]));$s=25;case 25:if($c){$c=false;ag=ag.$blk();}if(ag&&ag.$blk!==undefined){break s;}ah=I.New(ag);$s=26;case 26:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}e=ah;case 24:case 19:$s=-1;return[c,d,e];}return;}if($f===undefined){$f={$blk:Y};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};Z=function(b){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:case 1:if(!($clone(b,C.Value).Kind()===22)){$s=2;continue;}c=$clone(b,C.Value).Elem();$s=3;case 3:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}b=c;$s=1;continue;case 2:d=$clone(b,C.Value).Type();if($clone(b,C.Value).NumField()<1){$s=-1;return[R.nil,I.New("struc: Struct has no fields.")];}e={};f=$makeSlice(R,$clone(b,C.Value).NumField());g=0;case 4:h=d.NumField();$s=6;case 6:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}if(!(g>0;$s=4;continue;case 10:if(!($interfaceIsEqual(o,$ifaceNil))){$s=-1;return[R.nil,o];}p=$clone(b,C.Value).Field(g);$s=13;case 13:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=$clone(p,C.Value).CanSet();$s=14;case 14:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}if(!q){$s=11;continue;}$s=12;continue;case 11:g=g+(1)>>0;$s=4;continue;case 12:m.Index=g;if(!(n.Sizeof==="")){$s=15;continue;}$s=16;continue;case 15:s=d.FieldByName(n.Sizeof);$s=17;case 17:if($c){$c=false;s=s.$blk();}if(s&&s.$blk!==undefined){break s;}r=s;t=$clone(r[0],C.StructField);u=r[1];if(!u){$s=18;continue;}$s=19;continue;case 18:v=G.Errorf("struc: `sizeof=%s` field does not exist",new AY([new $String(n.Sizeof)]));$s=20;case 20:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}$s=-1;return[R.nil,v];case 19:m.Sizeof=t.Index;w=n.Sizeof;(e||$throwRuntimeError("assignment to entry in nil map"))[$String.keyFor(w)]={k:w,v:j.Index};case 16:x=(y=e[$String.keyFor(j.Name)],y!==undefined?[y.v,true]:[AZ.nil,false]);z=x[0];aa=x[1];if(aa){m.Sizefrom=z;}if(!(n.Sizefrom==="")){$s=21;continue;}$s=22;continue;case 21:ac=d.FieldByName(n.Sizefrom);$s=23;case 23:if($c){$c=false;ac=ac.$blk();}if(ac&&ac.$blk!==undefined){break s;}ab=ac;ad=$clone(ab[0],C.StructField);ae=ab[1];if(!ae){$s=24;continue;}$s=25;continue;case 24:af=G.Errorf("struc: `sizefrom=%s` field does not exist",new AY([new $String(n.Sizefrom)]));$s=26;case 26:if($c){$c=false;af=af.$blk();}if(af&&af.$blk!==undefined){break s;}$s=-1;return[R.nil,af];case 25:m.Sizefrom=ad.Index;case 22:if((m.Len===-1)&&m.Sizefrom===AZ.nil){$s=27;continue;}$s=28;continue;case 27:ag=G.Errorf("struc: field `%s` is a slice with no length or sizeof field",new AY([new $String(j.Name)]));$s=29;case 29:if($c){$c=false;ag=ag.$blk();}if(ag&&ag.$blk!==undefined){break s;}$s=-1;return[R.nil,ag];case 28:if(m.Type===15){$s=30;continue;}$s=31;continue;case 30:ah=j.Type;if(m.Ptr){$s=32;continue;}$s=33;continue;case 32:ai=ah.Elem();$s=34;case 34:if($c){$c=false;ai=ai.$blk();}if(ai&&ai.$blk!==undefined){break s;}ah=ai;case 33:if(m.Slice){$s=35;continue;}$s=36;continue;case 35:aj=ah.Elem();$s=37;case 37:if($c){$c=false;aj=aj.$blk();}if(aj&&aj.$blk!==undefined){break s;}ah=aj;case 36:al=Z($clone(C.New(ah),C.Value));$s=38;case 38:if($c){$c=false;al=al.$blk();}if(al&&al.$blk!==undefined){break s;}ak=al;m.Fields=ak[0];o=ak[1];if(!($interfaceIsEqual(o,$ifaceNil))){$s=-1;return[R.nil,o];}case 31:((g<0||g>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+g]=m);g=g+(1)>>0;$s=4;continue;case 5:$s=-1;return[f,$ifaceNil];}return;}if($f===undefined){$f={$blk:Z};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};AD=function(b){var b,c,d,e,f,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);$r=AB.RLock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$deferred.push([$methodVal(AB,"RUnlock"),[]]);c=(d=AA[C.Type.keyFor(b)],d!==undefined?[d.v,true]:[R.nil,false]);e=c[0];f=c[1];if(f){$s=-1;return e;}$s=-1;return R.nil;}return;}}catch(err){$err=err;$s=-1;return R.nil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AD};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AE=function(b){var b,c,d,e,f,g,h,i,j,k,l,m,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);case 1:if(!($clone(b,C.Value).Kind()===22)){$s=2;continue;}c=$clone(b,C.Value).Elem();$s=3;case 3:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}b=c;$s=1;continue;case 2:d=$clone(b,C.Value).Type();e=AD(d);$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!(f===R.nil)){$s=-1;return[f,$ifaceNil];}$r=AC.Lock();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$deferred.push([$methodVal(AC,"Unlock"),[]]);g=AD(d);$s=6;case 6:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;if(!(h===R.nil)){$s=-1;return[h,$ifaceNil];}j=Z($clone(b,C.Value));$s=7;case 7:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}i=j;k=i[0];l=i[1];if(!($interfaceIsEqual(l,$ifaceNil))){$s=-1;return[R.nil,l];}$r=AB.Lock();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}m=d;(AA||$throwRuntimeError("assignment to entry in nil map"))[C.Type.keyFor(m)]={k:m,v:k};$r=AB.Unlock();$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return[k,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[R.nil,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AE};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AF.ptr.prototype.Validate=function(){var b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;if(b.PtrSize===0){$s=1;continue;}$s=2;continue;case 1:b.PtrSize=32;$s=3;continue;case 2:c=b.PtrSize;if((c===(8))||(c===(16))||(c===(32))||(c===(64))){$s=5;continue;}$s=6;continue;case 5:$s=7;continue;case 6:d=G.Errorf("Invalid Options.PtrSize: %d. Must be in (8, 16, 32, 64)",new AY([new $Int(b.PtrSize)]));$s=8;case 8:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;case 7:case 4:case 3:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.Validate};}$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.Validate=function(){return this.$val.Validate();};AH=function(){var b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=AG.Validate();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}b;$s=-1;return;}return;}if($f===undefined){$f={$blk:AH};}$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};AI=function(b){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=C.ValueOf(b);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;case 2:if(!($clone(d,C.Value).Kind()===22)){$s=3;continue;}e=$clone(d,C.Value).Elem();$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=$clone(e,C.Value).Kind();$s=5;case 5:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;if((g===25)||(g===22)){$s=6;continue;}$s=7;continue;case 6:h=$clone(d,C.Value).Elem();$s=9;case 9:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}d=h;$s=8;continue;case 7:$s=3;continue;case 8:$s=2;continue;case 3:i=$clone(d,C.Value).Kind();if(i===(25)){$s=11;continue;}$s=12;continue;case 11:k=AE($clone(d,C.Value));$s=14;case 14:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;l=j[0];m=j[1];$s=-1;return[d,l,m];case 12:if(!$clone(d,C.Value).IsValid()){$s=15;continue;}$s=16;continue;case 15:n=G.Errorf("Invalid reflect.Value for %+v",new AY([b]));$s=17;case 17:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}$s=-1;return[new C.Value.ptr(BB.nil,0,0),$ifaceNil,n];case 16:o=$assertType(b,N,true);p=o[0];q=o[1];if(q){$s=-1;return[d,(r=new O.ptr(p),new r.constructor.elem(r)),$ifaceNil];}$s=-1;return[d,(s=($clone(d,M)),new s.constructor.elem(s)),$ifaceNil];case 13:case 10:$s=-1;return[new C.Value.ptr(BB.nil,0,0),$ifaceNil,$ifaceNil];}return;}if($f===undefined){$f={$blk:AI};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.$s=$s;$f.$r=$r;return $f;};AL=function(b,c){var b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=AM(b,c,BG.nil);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return d;}return;}if($f===undefined){$f={$blk:AL};}$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Unpack=AL;AM=function(b,c,d){var b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(d===BG.nil){d=AG;}e=d.Validate();$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return f;}h=AI(c);$s=2;case 2:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;i=g[0];j=g[1];k=g[2];if(!($interfaceIsEqual(k,$ifaceNil))){$s=-1;return k;}l=j.Unpack(b,$clone(i,C.Value),d);$s=3;case 3:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}$s=-1;return l;}return;}if($f===undefined){$f={$blk:AM};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};$pkg.UnpackWithOptions=AM;AP.prototype.Resolve=function(b){var b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this.$val;d=c;if(d===(18)){$s=2;continue;}if(d===(17)){$s=3;continue;}$s=4;continue;case 2:e=b.PtrSize;if(e===(8)){$s=6;continue;}if(e===(16)){$s=7;continue;}if(e===(32)){$s=8;continue;}if(e===(64)){$s=9;continue;}$s=10;continue;case 6:$s=-1;return 4;case 7:$s=-1;return 6;case 8:$s=-1;return 8;case 9:$s=-1;return 10;case 10:f=G.Sprintf("unsupported ptr bits: %d",new AY([new $Int(b.PtrSize)]));$s=12;case 12:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$panic(new $String(f));case 11:case 5:$s=4;continue;case 3:g=b.PtrSize;if(g===(8)){$s=14;continue;}if(g===(16)){$s=15;continue;}if(g===(32)){$s=16;continue;}if(g===(64)){$s=17;continue;}$s=18;continue;case 14:$s=-1;return 5;case 15:$s=-1;return 7;case 16:$s=-1;return 9;case 17:$s=-1;return 11;case 18:h=G.Sprintf("unsupported ptr bits: %d",new AY([new $Int(b.PtrSize)]));$s=20;case 20:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}$panic(new $String(h));case 19:case 13:case 4:case 1:$s=-1;return c;}return;}if($f===undefined){$f={$blk:AP.prototype.Resolve};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(AP).prototype.Resolve=function(b){return new AP(this.$get()).Resolve(b);};AP.prototype.String=function(){var b,c;b=this.$val;return(c=AR[AP.keyFor(b)],c!==undefined?c.v:"");};$ptrType(AP).prototype.String=function(){return new AP(this.$get()).String();};AP.prototype.Size=function(){var b,c;b=this.$val;c=b;if((c===(17))||(c===(18))){$panic(new $String("Size_t/Off_t types must be converted to another type using options.PtrSize"));}else if((c===(1))||(c===(14))||(c===(4))||(c===(5))||(c===(2))){return 1;}else if((c===(6))||(c===(7))){return 2;}else if((c===(8))||(c===(9))||(c===(12))){return 4;}else if((c===(10))||(c===(11))||(c===(13))){return 8;}else{$panic(new $String("Cannot resolve size of type:"+new AP(b).String()));}};$ptrType(AP).prototype.Size=function(){return new AP(this.$get()).Size();};AS=function(){var b,c,d,e,f,g,h;b=AQ;c=0;d=$keys(b);while(true){if(!(c10)");J=A.New("invalid pointer");K=A.New("nil resource body");L=A.New("insufficient data for resource body length");M=A.New("segment length too long");N=A.New("zero length segment");O=A.New("resource length too long");P=A.New("too many Questions to pack (>65535)");Q=A.New("too many Answers to pack (>65535)");R=A.New("too many Authorities to pack (>65535)");S=A.New("too many Additionals to pack (>65535)");T=A.New("name is not in canonical format (it must end with a .)");U=A.New("character string exceeds maximum length (255)");V=A.New("compressed name in SRV resource data");}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["math/rand"]=(function(){var $pkg={},$init,B,A,J,K,M,AG,AI,AM,AN,AO,AP,AQ,AR,AT,AU,AV,C,D,E,G,H,I,P,AH,F,L,N,O,AJ;B=$packages["github.com/gopherjs/gopherjs/nosync"];A=$packages["math"];J=$pkg.Source=$newType(8,$kindInterface,"rand.Source",true,"math/rand",true,null);K=$pkg.Source64=$newType(8,$kindInterface,"rand.Source64",true,"math/rand",true,null);M=$pkg.Rand=$newType(0,$kindStruct,"rand.Rand",true,"math/rand",true,function(src_,s64_,readVal_,readPos_){this.$val=this;if(arguments.length===0){this.src=$ifaceNil;this.s64=$ifaceNil;this.readVal=new $Int64(0,0);this.readPos=0;return;}this.src=src_;this.s64=s64_;this.readVal=readVal_;this.readPos=readPos_;});AG=$pkg.lockedSource=$newType(0,$kindStruct,"rand.lockedSource",true,"math/rand",false,function(lk_,src_){this.$val=this;if(arguments.length===0){this.lk=new B.Mutex.ptr(false);this.src=$ifaceNil;return;}this.lk=lk_;this.src=src_;});AI=$pkg.rngSource=$newType(0,$kindStruct,"rand.rngSource",true,"math/rand",false,function(tap_,feed_,vec_){this.$val=this;if(arguments.length===0){this.tap=0;this.feed=0;this.vec=AM.zero();return;}this.tap=tap_;this.feed=feed_;this.vec=vec_;});AM=$arrayType($Int64,607);AN=$ptrType(AG);AO=$ptrType($Int8);AP=$sliceType($Int);AQ=$ptrType($Int64);AR=$ptrType(M);AT=$funcType([$Int,$Int],[],false);AU=$sliceType($Uint8);AV=$ptrType(AI);M.ptr.prototype.ExpFloat64=function(){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;case 1:b=a.Uint32();$s=3;case 3:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;d=(c&255)>>>0;e=(c)*(((d<0||d>=D.length)?($throwRuntimeError("index out of range"),undefined):D[d]));if(c<((d<0||d>=C.length)?($throwRuntimeError("index out of range"),undefined):C[d])){$s=-1;return e;}if(d===0){$s=4;continue;}$s=5;continue;case 4:f=a.Float64();$s=6;case 6:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=A.Log(f);$s=7;case 7:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}$s=-1;return 7.69711747013105-g;case 5:h=a.Float64();$s=10;case 10:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}if($fround(((d<0||d>=E.length)?($throwRuntimeError("index out of range"),undefined):E[d])+$fround(($fround(h))*($fround((i=d-1>>>0,((i<0||i>=E.length)?($throwRuntimeError("index out of range"),undefined):E[i]))-((d<0||d>=E.length)?($throwRuntimeError("index out of range"),undefined):E[d])))))<($fround(A.Exp(-e)))){$s=8;continue;}$s=9;continue;case 8:$s=-1;return e;case 9:$s=1;continue;case 2:$s=-1;return 0;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.ExpFloat64};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.ExpFloat64=function(){return this.$val.ExpFloat64();};F=function(a){var a;if(a<0){return((-a>>>0));}return((a>>>0));};M.ptr.prototype.NormFloat64=function(){var a,b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;case 1:b=a.Uint32();$s=3;case 3:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=((b>>0));d=c&127;e=(c)*(((d<0||d>=H.length)?($throwRuntimeError("index out of range"),undefined):H[d]));if(F(c)<((d<0||d>=G.length)?($throwRuntimeError("index out of range"),undefined):G[d])){$s=-1;return e;}if(d===0){$s=4;continue;}$s=5;continue;case 4:case 6:f=a.Float64();$s=8;case 8:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=A.Log(f);$s=9;case 9:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}e=-g*0.29047645161474317;h=a.Float64();$s=10;case 10:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}i=A.Log(h);$s=11;case 11:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}j=-i;if(j+j>=e*e){$s=7;continue;}$s=6;continue;case 7:if(c>0){$s=-1;return 3.442619855899+e;}$s=-1;return-3.442619855899-e;case 5:k=a.Float64();$s=14;case 14:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}if($fround(((d<0||d>=I.length)?($throwRuntimeError("index out of range"),undefined):I[d])+$fround(($fround(k))*($fround((l=d-1>>0,((l<0||l>=I.length)?($throwRuntimeError("index out of range"),undefined):I[l]))-((d<0||d>=I.length)?($throwRuntimeError("index out of range"),undefined):I[d])))))<($fround(A.Exp(-0.5*e*e)))){$s=12;continue;}$s=13;continue;case 12:$s=-1;return e;case 13:$s=1;continue;case 2:$s=-1;return 0;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.NormFloat64};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.NormFloat64=function(){return this.$val.NormFloat64();};L=function(a){var a,b;b=new AI.ptr(0,0,AM.zero());b.Seed(a);return b;};$pkg.NewSource=L;N=function(a){var a,b,c;b=$assertType(a,K,true);c=b[0];return new M.ptr(a,c,new $Int64(0,0),0);};$pkg.New=N;M.ptr.prototype.Seed=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=$assertType(b.src,AN,true);d=c[0];e=c[1];if(e){$s=1;continue;}$s=2;continue;case 1:$r=d.seedPos(a,(b.$ptr_readPos||(b.$ptr_readPos=new AO(function(){return this.$target.readPos;},function($v){this.$target.readPos=$v;},b))));$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 2:$r=b.src.Seed(a);$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}b.readPos=0;$s=-1;return;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Seed};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Seed=function(a){return this.$val.Seed(a);};M.ptr.prototype.Int63=function(){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.src.Int63();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return b;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Int63};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Int63=function(){return this.$val.Int63();};M.ptr.prototype.Uint32=function(){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.Int63();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return(($shiftRightInt64(b,31).$low>>>0));}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Uint32};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Uint32=function(){return this.$val.Uint32();};M.ptr.prototype.Uint64=function(){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;if(!($interfaceIsEqual(a.s64,$ifaceNil))){$s=1;continue;}$s=2;continue;case 1:b=a.s64.Uint64();$s=3;case 3:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return b;case 2:e=a.Int63();$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}h=a.Int63();$s=5;case 5:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}$s=-1;return(c=$shiftRightUint64(((d=e,new $Uint64(d.$high,d.$low))),31),f=$shiftLeft64(((g=h,new $Uint64(g.$high,g.$low))),32),new $Uint64(c.$high|f.$high,(c.$low|f.$low)>>>0));}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Uint64};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Uint64=function(){return this.$val.Uint64();};M.ptr.prototype.Int31=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;c=a.Int63();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return(((b=$shiftRightInt64(c,32),b.$low+((b.$high>>31)*4294967296))>>0));}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Int31};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Int31=function(){return this.$val.Int31();};M.ptr.prototype.Int=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.Int63();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=((b.$low>>>0));$s=-1;return((((c<<1>>>0)>>>1>>>0)>>0));}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Int};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Int=function(){return this.$val.Int();};M.ptr.prototype.Int63n=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;if((a.$high<0||(a.$high===0&&a.$low<=0))){$panic(new $String("invalid argument to Int63n"));}if((c=(d=new $Int64(a.$high-0,a.$low-1),new $Int64(a.$high&d.$high,(a.$low&d.$low)>>>0)),(c.$high===0&&c.$low===0))){$s=1;continue;}$s=2;continue;case 1:f=b.Int63();$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$s=-1;return(e=f,g=new $Int64(a.$high-0,a.$low-1),new $Int64(e.$high&g.$high,(e.$low&g.$low)>>>0));case 2:j=((h=(i=$div64(new $Uint64(2147483648,0),(new $Uint64(a.$high,a.$low)),true),new $Uint64(2147483647-i.$high,4294967295-i.$low)),new $Int64(h.$high,h.$low)));k=b.Int63();$s=4;case 4:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}l=k;case 5:if(!((l.$high>j.$high||(l.$high===j.$high&&l.$low>j.$low)))){$s=6;continue;}m=b.Int63();$s=7;case 7:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;$s=5;continue;case 6:$s=-1;return $div64(l,a,true);}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Int63n};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Int63n=function(a){return this.$val.Int63n(a);};M.ptr.prototype.Int31n=function(a){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;if(a<=0){$panic(new $String("invalid argument to Int31n"));}if((a&((a-1>>0)))===0){$s=1;continue;}$s=2;continue;case 1:c=b.Int31();$s=3;case 3:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return c&((a-1>>0));case 2:e=(((2147483647-(d=2147483648%((a>>>0)),d===d?d:$throwRuntimeError("integer divide by zero"))>>>0)>>0));f=b.Int31();$s=4;case 4:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=f;case 5:if(!(g>e)){$s=6;continue;}h=b.Int31();$s=7;case 7:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;$s=5;continue;case 6:$s=-1;return(i=g%a,i===i?i:$throwRuntimeError("integer divide by zero"));}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Int31n};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Int31n=function(a){return this.$val.Int31n(a);};M.ptr.prototype.int31n=function(a){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=b.Uint32();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;e=$mul64((new $Uint64(0,d)),(new $Uint64(0,a)));f=((e.$low>>>0));if(f<((a>>>0))){$s=2;continue;}$s=3;continue;case 2:h=(g=((-a>>>0))%((a>>>0)),g===g?g:$throwRuntimeError("integer divide by zero"));case 4:if(!(f>>0));$s=4;continue;case 5:case 3:$s=-1;return(($shiftRightUint64(e,32).$low>>0));}return;}if($f===undefined){$f={$blk:M.ptr.prototype.int31n};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.int31n=function(a){return this.$val.int31n(a);};M.ptr.prototype.Intn=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;if(a<=0){$panic(new $String("invalid argument to Intn"));}if(a<=2147483647){$s=1;continue;}$s=2;continue;case 1:c=b.Int31n(((a>>0)));$s=3;case 3:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return((c>>0));case 2:e=b.Int63n((new $Int64(0,a)));$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return(((d=e,d.$low+((d.$high>>31)*4294967296))>>0));}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Intn};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Intn=function(a){return this.$val.Intn(a);};M.ptr.prototype.Float64=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;case 1:b=a.Int63();$s=2;case 2:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=($flatten64(b))/9.223372036854776e+18;if(c===1){$s=3;continue;}$s=4;continue;case 3:$s=1;continue;case 4:$s=-1;return c;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Float64};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Float64=function(){return this.$val.Float64();};M.ptr.prototype.Float32=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;case 1:b=a.Float64();$s=2;case 2:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=($fround(b));if(c===1){$s=3;continue;}$s=4;continue;case 3:$s=1;continue;case 4:$s=-1;return c;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Float32};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Float32=function(){return this.$val.Float32();};M.ptr.prototype.Perm=function(a){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=$makeSlice(AP,a);d=0;case 1:if(!(d>0);$s=3;case 3:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=e;((d<0||d>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+d]=((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]));((f<0||f>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+f]=d);d=d+(1)>>0;$s=1;continue;case 2:$s=-1;return c;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Perm};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Perm=function(a){return this.$val.Perm(a);};M.ptr.prototype.Shuffle=function(a,b){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;if(a<0){$panic(new $String("invalid argument to Shuffle"));}d=a-1>>0;case 1:if(!(d>2147483646)){$s=2;continue;}f=c.Int63n((new $Int64(0,(d+1>>0))));$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=(((e=f,e.$low+((e.$high>>31)*4294967296))>>0));$r=b(d,g);$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d=d-(1)>>0;$s=1;continue;case 2:case 5:if(!(d>0)){$s=6;continue;}h=c.int31n((((d+1>>0)>>0)));$s=7;case 7:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}i=((h>>0));$r=b(d,i);$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d=d-(1)>>0;$s=5;continue;case 6:$s=-1;return;}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Shuffle};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Shuffle=function(a,b){return this.$val.Shuffle(a,b);};M.ptr.prototype.Read=function(a){var a,b,c,d,e,f,g,h,i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;d=this;e=$assertType(d.src,AN,true);f=e[0];g=e[1];if(g){$s=1;continue;}$s=2;continue;case 1:i=f.read(a,(d.$ptr_readVal||(d.$ptr_readVal=new AQ(function(){return this.$target.readVal;},function($v){this.$target.readVal=$v;},d))),(d.$ptr_readPos||(d.$ptr_readPos=new AO(function(){return this.$target.readPos;},function($v){this.$target.readPos=$v;},d))));$s=3;case 3:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;b=h[0];c=h[1];$s=-1;return[b,c];case 2:k=O(a,$methodVal(d,"Int63"),(d.$ptr_readVal||(d.$ptr_readVal=new AQ(function(){return this.$target.readVal;},function($v){this.$target.readVal=$v;},d))),(d.$ptr_readPos||(d.$ptr_readPos=new AO(function(){return this.$target.readPos;},function($v){this.$target.readPos=$v;},d))));$s=4;case 4:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;b=j[0];c=j[1];$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:M.ptr.prototype.Read};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};M.prototype.Read=function(a){return this.$val.Read(a);};O=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=0;f=$ifaceNil;g=d.$get();h=c.$get();e=0;case 1:if(!(e=a.$length)?($throwRuntimeError("index out of range"),undefined):a.$array[a.$offset+e]=((h.$low<<24>>>24)));h=$shiftRightInt64(h,(8));g=g-(1)<<24>>24;e=e+(1)>>0;$s=1;continue;case 2:d.$set(g);c.$set(h);$s=-1;return[e,f];}return;}if($f===undefined){$f={$blk:O};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};AG.ptr.prototype.Int63=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=new $Int64(0,0);b=this;b.lk.Lock();c=b.src.Int63();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}a=c;b.lk.Unlock();$s=-1;return a;}return;}if($f===undefined){$f={$blk:AG.ptr.prototype.Int63};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};AG.prototype.Int63=function(){return this.$val.Int63();};AG.ptr.prototype.Uint64=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=new $Uint64(0,0);b=this;b.lk.Lock();c=b.src.Uint64();$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}a=c;b.lk.Unlock();$s=-1;return a;}return;}if($f===undefined){$f={$blk:AG.ptr.prototype.Uint64};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};AG.prototype.Uint64=function(){return this.$val.Uint64();};AG.ptr.prototype.Seed=function(a){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;b.lk.Lock();$r=b.src.Seed(a);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}b.lk.Unlock();$s=-1;return;}return;}if($f===undefined){$f={$blk:AG.ptr.prototype.Seed};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};AG.prototype.Seed=function(a){return this.$val.Seed(a);};AG.ptr.prototype.seedPos=function(a,b){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;c.lk.Lock();$r=c.src.Seed(a);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}b.$set(0);c.lk.Unlock();$s=-1;return;}return;}if($f===undefined){$f={$blk:AG.ptr.prototype.seedPos};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};AG.prototype.seedPos=function(a,b){return this.$val.seedPos(a,b);};AG.ptr.prototype.read=function(a,b,c){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=0;e=$ifaceNil;f=this;f.lk.Lock();h=O(a,$methodVal(f.src,"Int63"),b,c);$s=1;case 1:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;d=g[0];e=g[1];f.lk.Unlock();$s=-1;return[d,e];}return;}if($f===undefined){$f={$blk:AG.ptr.prototype.read};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};AG.prototype.read=function(a,b,c){return this.$val.read(a,b,c);};AJ=function(a){var a,b,c,d,e;c=(b=a/44488,(b===b&&b!==1/0&&b!==-1/0)?b>>0:$throwRuntimeError("integer divide by zero"));e=(d=a%44488,d===d?d:$throwRuntimeError("integer divide by zero"));a=($imul(48271,e))-($imul(3399,c))>>0;if(a<0){a=a+(2147483647)>>0;}return a;};AI.ptr.prototype.Seed=function(a){var a,b,c,d,e,f,g,h,i,j;b=this;b.tap=0;b.feed=334;a=$div64(a,new $Int64(0,2147483647),true);if((a.$high<0||(a.$high===0&&a.$low<0))){a=(c=new $Int64(0,2147483647),new $Int64(a.$high+c.$high,a.$low+c.$low));}if((a.$high===0&&a.$low===0)){a=new $Int64(0,89482311);}d=(((a.$low+((a.$high>>31)*4294967296))>>0));e=-20;while(true){if(!(e<607)){break;}d=AJ(d);if(e>=0){f=new $Int64(0,0);f=$shiftLeft64((new $Int64(0,d)),40);d=AJ(d);f=(g=$shiftLeft64((new $Int64(0,d)),20),new $Int64(f.$high^g.$high,(f.$low^g.$low)>>>0));d=AJ(d);f=(h=(new $Int64(0,d)),new $Int64(f.$high^h.$high,(f.$low^h.$low)>>>0));f=(i=((e<0||e>=AH.length)?($throwRuntimeError("index out of range"),undefined):AH[e]),new $Int64(f.$high^i.$high,(f.$low^i.$low)>>>0));(j=b.vec,((e<0||e>=j.length)?($throwRuntimeError("index out of range"),undefined):j[e]=f));}e=e+(1)>>0;}};AI.prototype.Seed=function(a){return this.$val.Seed(a);};AI.ptr.prototype.Int63=function(){var a,b,c;a=this;return((b=(c=a.Uint64(),new $Uint64(c.$high&2147483647,(c.$low&4294967295)>>>0)),new $Int64(b.$high,b.$low)));};AI.prototype.Int63=function(){return this.$val.Int63();};AI.ptr.prototype.Uint64=function(){var a,b,c,d,e,f,g,h,i,j;a=this;a.tap=a.tap-(1)>>0;if(a.tap<0){a.tap=a.tap+(607)>>0;}a.feed=a.feed-(1)>>0;if(a.feed<0){a.feed=a.feed+(607)>>0;}h=(b=(c=a.vec,d=a.feed,((d<0||d>=c.length)?($throwRuntimeError("index out of range"),undefined):c[d])),e=(f=a.vec,g=a.tap,((g<0||g>=f.length)?($throwRuntimeError("index out of range"),undefined):f[g])),new $Int64(b.$high+e.$high,b.$low+e.$low));(i=a.vec,j=a.feed,((j<0||j>=i.length)?($throwRuntimeError("index out of range"),undefined):i[j]=h));return(new $Uint64(h.$high,h.$low));};AI.prototype.Uint64=function(){return this.$val.Uint64();};AR.methods=[{prop:"ExpFloat64",name:"ExpFloat64",pkg:"",typ:$funcType([],[$Float64],false)},{prop:"NormFloat64",name:"NormFloat64",pkg:"",typ:$funcType([],[$Float64],false)},{prop:"Seed",name:"Seed",pkg:"",typ:$funcType([$Int64],[],false)},{prop:"Int63",name:"Int63",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Uint32",name:"Uint32",pkg:"",typ:$funcType([],[$Uint32],false)},{prop:"Uint64",name:"Uint64",pkg:"",typ:$funcType([],[$Uint64],false)},{prop:"Int31",name:"Int31",pkg:"",typ:$funcType([],[$Int32],false)},{prop:"Int",name:"Int",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Int63n",name:"Int63n",pkg:"",typ:$funcType([$Int64],[$Int64],false)},{prop:"Int31n",name:"Int31n",pkg:"",typ:$funcType([$Int32],[$Int32],false)},{prop:"int31n",name:"int31n",pkg:"math/rand",typ:$funcType([$Int32],[$Int32],false)},{prop:"Intn",name:"Intn",pkg:"",typ:$funcType([$Int],[$Int],false)},{prop:"Float64",name:"Float64",pkg:"",typ:$funcType([],[$Float64],false)},{prop:"Float32",name:"Float32",pkg:"",typ:$funcType([],[$Float32],false)},{prop:"Perm",name:"Perm",pkg:"",typ:$funcType([$Int],[AP],false)},{prop:"Shuffle",name:"Shuffle",pkg:"",typ:$funcType([$Int,AT],[],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([AU],[$Int,$error],false)}];AN.methods=[{prop:"Int63",name:"Int63",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Uint64",name:"Uint64",pkg:"",typ:$funcType([],[$Uint64],false)},{prop:"Seed",name:"Seed",pkg:"",typ:$funcType([$Int64],[],false)},{prop:"seedPos",name:"seedPos",pkg:"math/rand",typ:$funcType([$Int64,AO],[],false)},{prop:"read",name:"read",pkg:"math/rand",typ:$funcType([AU,AQ,AO],[$Int,$error],false)}];AV.methods=[{prop:"Seed",name:"Seed",pkg:"",typ:$funcType([$Int64],[],false)},{prop:"Int63",name:"Int63",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Uint64",name:"Uint64",pkg:"",typ:$funcType([],[$Uint64],false)}];J.init([{prop:"Int63",name:"Int63",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Seed",name:"Seed",pkg:"",typ:$funcType([$Int64],[],false)}]);K.init([{prop:"Int63",name:"Int63",pkg:"",typ:$funcType([],[$Int64],false)},{prop:"Seed",name:"Seed",pkg:"",typ:$funcType([$Int64],[],false)},{prop:"Uint64",name:"Uint64",pkg:"",typ:$funcType([],[$Uint64],false)}]);M.init("math/rand",[{prop:"src",name:"src",embedded:false,exported:false,typ:J,tag:""},{prop:"s64",name:"s64",embedded:false,exported:false,typ:K,tag:""},{prop:"readVal",name:"readVal",embedded:false,exported:false,typ:$Int64,tag:""},{prop:"readPos",name:"readPos",embedded:false,exported:false,typ:$Int8,tag:""}]);AG.init("math/rand",[{prop:"lk",name:"lk",embedded:false,exported:false,typ:B.Mutex,tag:""},{prop:"src",name:"src",embedded:false,exported:false,typ:K,tag:""}]);AI.init("math/rand",[{prop:"tap",name:"tap",embedded:false,exported:false,typ:$Int,tag:""},{prop:"feed",name:"feed",embedded:false,exported:false,typ:$Int,tag:""},{prop:"vec",name:"vec",embedded:false,exported:false,typ:AM,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=B.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}C=$toNativeArray($kindUint32,[3801129273,0,2615860924,3279400049,3571300752,3733536696,3836274812,3906990442,3958562475,3997804264,4028649213,4053523342,4074002619,4091154507,4105727352,4118261130,4129155133,4138710916,4147160435,4154685009,4161428406,4167506077,4173011791,4178022498,4182601930,4186803325,4190671498,4194244443,4197554582,4200629752,4203493986,4206168142,4208670408,4211016720,4213221098,4215295924,4217252177,4219099625,4220846988,4222502074,4224071896,4225562770,4226980400,4228329951,4229616109,4230843138,4232014925,4233135020,4234206673,4235232866,4236216336,4237159604,4238064994,4238934652,4239770563,4240574564,4241348362,4242093539,4242811568,4243503822,4244171579,4244816032,4245438297,4246039419,4246620374,4247182079,4247725394,4248251127,4248760037,4249252839,4249730206,4250192773,4250641138,4251075867,4251497493,4251906522,4252303431,4252688672,4253062674,4253425844,4253778565,4254121205,4254454110,4254777611,4255092022,4255397640,4255694750,4255983622,4256264513,4256537670,4256803325,4257061702,4257313014,4257557464,4257795244,4258026541,4258251531,4258470383,4258683258,4258890309,4259091685,4259287526,4259477966,4259663135,4259843154,4260018142,4260188212,4260353470,4260514019,4260669958,4260821380,4260968374,4261111028,4261249421,4261383632,4261513736,4261639802,4261761900,4261880092,4261994441,4262105003,4262211835,4262314988,4262414513,4262510454,4262602857,4262691764,4262777212,4262859239,4262937878,4263013162,4263085118,4263153776,4263219158,4263281289,4263340187,4263395872,4263448358,4263497660,4263543789,4263586755,4263626565,4263663224,4263696735,4263727099,4263754314,4263778377,4263799282,4263817020,4263831582,4263842955,4263851124,4263856071,4263857776,4263856218,4263851370,4263843206,4263831695,4263816804,4263798497,4263776735,4263751476,4263722676,4263690284,4263654251,4263614520,4263571032,4263523724,4263472530,4263417377,4263358192,4263294892,4263227394,4263155608,4263079437,4262998781,4262913534,4262823581,4262728804,4262629075,4262524261,4262414220,4262298801,4262177846,4262051187,4261918645,4261780032,4261635148,4261483780,4261325704,4261160681,4260988457,4260808763,4260621313,4260425802,4260221905,4260009277,4259787550,4259556329,4259315195,4259063697,4258801357,4258527656,4258242044,4257943926,4257632664,4257307571,4256967906,4256612870,4256241598,4255853155,4255446525,4255020608,4254574202,4254106002,4253614578,4253098370,4252555662,4251984571,4251383021,4250748722,4250079132,4249371435,4248622490,4247828790,4246986404,4246090910,4245137315,4244119963,4243032411,4241867296,4240616155,4239269214,4237815118,4236240596,4234530035,4232664930,4230623176,4228378137,4225897409,4223141146,4220059768,4216590757,4212654085,4208145538,4202926710,4196809522,4189531420,4180713890,4169789475,4155865042,4137444620,4111806704,4073393724,4008685917,3873074895]);D=$toNativeArray($kindFloat32,[2.0249555365836613e-09,1.4866739783681027e-11,2.4409616689036184e-11,3.1968806074589295e-11,3.844677007314168e-11,4.42282044321729e-11,4.951644302919611e-11,5.443358958023836e-11,5.905943789574764e-11,6.34494193296753e-11,6.764381416113352e-11,7.167294535648239e-11,7.556032188826833e-11,7.932458162551725e-11,8.298078890689453e-11,8.654132271912474e-11,9.001651507523079e-11,9.341507428706208e-11,9.674443190998971e-11,1.0001099254308699e-10,1.0322031424037093e-10,1.0637725422757427e-10,1.0948611461891744e-10,1.1255067711157807e-10,1.1557434870246297e-10,1.1856014781042035e-10,1.2151082917633005e-10,1.2442885610752796e-10,1.2731647680563896e-10,1.3017574518325858e-10,1.330085347417409e-10,1.3581656632677408e-10,1.386014220061682e-10,1.413645728254309e-10,1.4410737880776736e-10,1.4683107507629245e-10,1.4953686899854546e-10,1.522258291641876e-10,1.5489899640730442e-10,1.575573282952547e-10,1.6020171300645814e-10,1.628330109637588e-10,1.6545202707884954e-10,1.68059510752272e-10,1.7065616975120435e-10,1.73242697965037e-10,1.758197337720091e-10,1.783878739169964e-10,1.8094774290045024e-10,1.834998542005195e-10,1.8604476292871652e-10,1.8858298256319017e-10,1.9111498494872592e-10,1.9364125580789704e-10,1.9616222535212557e-10,1.9867835154840918e-10,2.011900368525943e-10,2.0369768372052732e-10,2.062016807302669e-10,2.0870240258208383e-10,2.1120022397624894e-10,2.136955057352452e-10,2.1618855317040442e-10,2.1867974098199738e-10,2.2116936060356807e-10,2.2365774510202385e-10,2.2614519978869652e-10,2.2863201609713002e-10,2.3111849933865614e-10,2.3360494094681883e-10,2.3609159072179864e-10,2.3857874009713953e-10,2.4106666662859766e-10,2.4355562011635357e-10,2.460458781161634e-10,2.485376904282077e-10,2.5103127909709144e-10,2.5352694943414633e-10,2.560248957284017e-10,2.585253955356137e-10,2.610286709003873e-10,2.6353494386732734e-10,2.6604446423661443e-10,2.6855745405285347e-10,2.71074163116225e-10,2.7359478571575835e-10,2.7611959940720965e-10,2.786487707240326e-10,2.8118254946640775e-10,2.8372118543451563e-10,2.8626484516180994e-10,2.8881380620404684e-10,2.9136826285025563e-10,2.9392840938946563e-10,2.96494523377433e-10,2.990667713476114e-10,3.016454031001814e-10,3.042306406797479e-10,3.068226783753403e-10,3.09421765987139e-10,3.12028125559749e-10,3.1464195138219964e-10,3.17263521010247e-10,3.1989300097734485e-10,3.225306410836737e-10,3.2517669112941405e-10,3.2783134540359526e-10,3.3049485370639786e-10,3.3316743808242677e-10,3.3584937608743815e-10,3.385408342548857e-10,3.4124211789610115e-10,3.4395342130011386e-10,3.4667499426710435e-10,3.494071143528288e-10,3.521500313574677e-10,3.54903967325626e-10,3.576691720574843e-10,3.6044595086437425e-10,3.632345535464765e-10,3.660352021483959e-10,3.688482297370399e-10,3.716738583570134e-10,3.7451239331964814e-10,3.773641121807003e-10,3.802292924959261e-10,3.831082673322328e-10,3.8600128648980103e-10,3.8890865527996255e-10,3.9183070676962473e-10,3.9476774627011935e-10,3.977200790927782e-10,4.006880383045086e-10,4.0367195697221803e-10,4.066721681628138e-10,4.0968900494320337e-10,4.127228558914453e-10,4.15774054074447e-10,4.188429603146915e-10,4.2192993543466173e-10,4.25035395767992e-10,4.2815970213716525e-10,4.313032986313914e-10,4.3446651831757777e-10,4.376498607960855e-10,4.408536868893975e-10,4.4407846844229937e-10,4.4732464954400086e-10,4.5059267428371186e-10,4.538830145062178e-10,4.5719619756745544e-10,4.605326675566346e-10,4.638929240741163e-10,4.672775499869886e-10,4.706869893844612e-10,4.74121908400349e-10,4.775827511238617e-10,4.810701836888143e-10,4.845848167178701e-10,4.881271498113904e-10,4.916979601254923e-10,4.952977472605369e-10,4.989272883726414e-10,5.025872495956207e-10,5.062783525744408e-10,5.100013189540675e-10,5.13756870379467e-10,5.175458395179078e-10,5.21369003525507e-10,5.252272505806843e-10,5.29121357839557e-10,5.330522134805449e-10,5.3702081670437e-10,5.41028055689452e-10,5.450749851476644e-10,5.491624932574268e-10,5.532918012640664e-10,5.574638528571541e-10,5.616799247931681e-10,5.659410717839819e-10,5.702485705860738e-10,5.746036979559221e-10,5.790077306500052e-10,5.83462111958255e-10,5.879682296594524e-10,5.925275825546805e-10,5.971417249561739e-10,6.01812211176167e-10,6.065408175714992e-10,6.113292094767075e-10,6.16179329782085e-10,6.21092954844471e-10,6.260721940876124e-10,6.311191569352559e-10,6.362359528111483e-10,6.414249686947926e-10,6.466885360545405e-10,6.520292639144998e-10,6.574497612987784e-10,6.629528592760892e-10,6.685415554485985e-10,6.742187919073217e-10,6.799880103436351e-10,6.858525969377638e-10,6.918161599145378e-10,6.978825850545434e-10,7.040559801829716e-10,7.103406751696184e-10,7.167412219288849e-10,7.232625609532306e-10,7.2990985477972e-10,7.366885990123251e-10,7.436047333442275e-10,7.506645305355164e-10,7.57874762946642e-10,7.652426470272644e-10,7.727759543385559e-10,7.804830115532013e-10,7.883728114777e-10,7.964550685635174e-10,8.047402189070851e-10,8.132396422944055e-10,8.219657177122031e-10,8.309318788590758e-10,8.401527806789488e-10,8.496445214056791e-10,8.594246980742071e-10,8.695127395874636e-10,8.799300732498239e-10,8.90700457834015e-10,9.01850316648023e-10,9.134091816243028e-10,9.254100818978372e-10,9.37890431984556e-10,9.508922538259412e-10,9.64463842123564e-10,9.78660263939446e-10,9.935448019859905e-10,1.0091912860943353e-09,1.0256859805934937e-09,1.0431305819125214e-09,1.0616465484503124e-09,1.0813799855569073e-09,1.1025096391392708e-09,1.1252564435793033e-09,1.149898620766976e-09,1.176793218427008e-09,1.2064089727203964e-09,1.2393785997488749e-09,1.2765849488616254e-09,1.319313880365769e-09,1.36954347862428e-09,1.4305497897382224e-09,1.5083649884672923e-09,1.6160853766322703e-09,1.7921247819074893e-09]);E=$toNativeArray($kindFloat32,[1,0.9381436705589294,0.900469958782196,0.8717043399810791,0.847785472869873,0.8269932866096497,0.8084216713905334,0.7915276288986206,0.7759568691253662,0.7614634037017822,0.7478685975074768,0.7350381016731262,0.7228676676750183,0.7112747430801392,0.7001926302909851,0.6895664930343628,0.6793505549430847,0.669506311416626,0.6600008606910706,0.6508058309555054,0.6418967247009277,0.633251965045929,0.62485271692276,0.6166821718215942,0.608725368976593,0.6009689569473267,0.5934008955955505,0.5860103368759155,0.5787873864173889,0.5717230439186096,0.5648092031478882,0.5580382943153381,0.5514034032821655,0.5448982119560242,0.5385168790817261,0.5322538614273071,0.526104211807251,0.5200631618499756,0.5141264200210571,0.5082897543907166,0.5025495290756226,0.4969019889831543,0.4913438558578491,0.4858720004558563,0.48048335313796997,0.4751752018928528,0.4699448347091675,0.4647897481918335,0.4597076177597046,0.4546961486339569,0.4497532546520233,0.44487687945365906,0.4400651156902313,0.4353161156177521,0.4306281507015228,0.42599955201148987,0.42142874002456665,0.4169141948223114,0.4124544560909271,0.40804818272590637,0.4036940038204193,0.39939069747924805,0.3951369822025299,0.39093172550201416,0.38677382469177246,0.38266217708587646,0.378595769405365,0.37457355856895447,0.37059465050697327,0.366658091545105,0.362762987613678,0.358908474445343,0.35509374737739563,0.35131800174713135,0.3475804924964905,0.34388044476509094,0.34021714329719543,0.33658990263938904,0.3329980671405792,0.3294409513473511,0.32591795921325684,0.32242849469184875,0.3189719021320343,0.3155476748943329,0.31215524673461914,0.3087940812110901,0.30546361207962036,0.30216339230537415,0.29889291524887085,0.29565170407295227,0.2924392819404602,0.2892552316188812,0.28609907627105713,0.2829704284667969,0.27986884117126465,0.2767939269542694,0.2737452983856201,0.2707225978374481,0.26772540807724,0.26475343108177185,0.2618062496185303,0.258883535861969,0.2559850215911865,0.25311028957366943,0.25025907158851624,0.24743106961250305,0.2446259707212448,0.24184346199035645,0.23908329010009766,0.23634515702724457,0.2336287796497345,0.23093391954898834,0.22826029360294342,0.22560766339302063,0.22297576069831848,0.22036437690258026,0.21777324378490448,0.21520215272903442,0.212650865316391,0.21011915802955627,0.20760682225227356,0.20511364936828613,0.20263944566249847,0.20018397271633148,0.19774706661701202,0.1953285187482834,0.19292815029621124,0.19054576754570007,0.18818120658397675,0.18583425879478455,0.18350479006767273,0.18119260668754578,0.17889754474163055,0.17661945521831512,0.17435817420482635,0.1721135377883911,0.16988539695739746,0.16767361760139465,0.16547803580760956,0.16329853236675262,0.16113494336605072,0.1589871346950531,0.15685498714447021,0.15473836660385132,0.15263713896274567,0.1505511850118637,0.1484803706407547,0.14642459154129028,0.1443837285041809,0.14235764741897583,0.1403462439775467,0.13834942877292633,0.136367067694664,0.13439907133579254,0.1324453204870224,0.1305057406425476,0.12858019769191742,0.12666863203048706,0.12477091699838638,0.12288697808980942,0.1210167184472084,0.11916005611419678,0.11731690168380737,0.11548716574907303,0.11367076635360718,0.11186762899160385,0.11007767915725708,0.1083008274435997,0.10653700679540634,0.10478614270687103,0.1030481606721878,0.10132300108671188,0.0996105819940567,0.09791085124015808,0.09622374176979065,0.09454918652772903,0.09288713335990906,0.09123751521110535,0.08960027992725372,0.08797537535429001,0.08636274188756943,0.0847623273730278,0.08317409455776215,0.08159798383712769,0.08003395050764084,0.07848194986581802,0.07694194465875626,0.07541389018297195,0.07389774918556213,0.07239348441362381,0.070901058614254,0.06942043453454971,0.06795158982276917,0.06649449467658997,0.06504911929368973,0.06361543387174606,0.06219341605901718,0.06078304722905159,0.0593843050301075,0.05799717456102371,0.05662164092063904,0.05525768920779228,0.05390531197190285,0.05256449431180954,0.05123523622751236,0.04991753399372101,0.04861138388514519,0.047316793352365494,0.04603376239538193,0.044762298464775085,0.04350241273641586,0.04225412383675575,0.04101744294166565,0.039792392402887344,0.03857899457216263,0.03737728297710419,0.03618728369474411,0.03500903770327568,0.03384258225560188,0.0326879620552063,0.031545232981443405,0.030414443463087082,0.0292956605553627,0.028188949450850487,0.027094384655356407,0.02601204626262188,0.024942025542259216,0.023884421214461327,0.022839335724711418,0.021806888282299042,0.020787203684449196,0.019780423492193222,0.018786700442433357,0.017806200310587883,0.016839107498526573,0.015885621309280396,0.014945968054234982,0.01402039173990488,0.013109165243804455,0.012212592177093029,0.011331013403832912,0.010464809834957123,0.009614413604140282,0.008780314587056637,0.007963077165186405,0.007163353264331818,0.0063819061033427715,0.005619642324745655,0.004877655766904354,0.004157294984906912,0.003460264764726162,0.0027887988835573196,0.0021459676790982485,0.001536299823783338,0.0009672692976891994,0.0004541343660093844]);G=$toNativeArray($kindUint32,[1991057938,0,1611602771,1826899878,1918584482,1969227037,2001281515,2023368125,2039498179,2051788381,2061460127,2069267110,2075699398,2081089314,2085670119,2089610331,2093034710,2096037586,2098691595,2101053571,2103168620,2105072996,2106796166,2108362327,2109791536,2111100552,2112303493,2113412330,2114437283,2115387130,2116269447,2117090813,2117856962,2118572919,2119243101,2119871411,2120461303,2121015852,2121537798,2122029592,2122493434,2122931299,2123344971,2123736059,2124106020,2124456175,2124787725,2125101763,2125399283,2125681194,2125948325,2126201433,2126441213,2126668298,2126883268,2127086657,2127278949,2127460589,2127631985,2127793506,2127945490,2128088244,2128222044,2128347141,2128463758,2128572095,2128672327,2128764606,2128849065,2128925811,2128994934,2129056501,2129110560,2129157136,2129196237,2129227847,2129251929,2129268426,2129277255,2129278312,2129271467,2129256561,2129233410,2129201800,2129161480,2129112170,2129053545,2128985244,2128906855,2128817916,2128717911,2128606255,2128482298,2128345305,2128194452,2128028813,2127847342,2127648860,2127432031,2127195339,2126937058,2126655214,2126347546,2126011445,2125643893,2125241376,2124799783,2124314271,2123779094,2123187386,2122530867,2121799464,2120980787,2120059418,2119015917,2117825402,2116455471,2114863093,2112989789,2110753906,2108037662,2104664315,2100355223,2094642347,2086670106,2074676188,2054300022,2010539237]);H=$toNativeArray($kindFloat32,[1.7290404663583558e-09,1.2680928529462676e-10,1.689751810696194e-10,1.9862687883343e-10,2.223243117382978e-10,2.4244936613904144e-10,2.601613091623989e-10,2.761198769629658e-10,2.9073962681813725e-10,3.042996965518796e-10,3.169979556627567e-10,3.289802041894774e-10,3.4035738116777736e-10,3.5121602848242617e-10,3.61625090983253e-10,3.7164057942185025e-10,3.813085680537398e-10,3.906675816178762e-10,3.997501218933053e-10,4.0858399996679395e-10,4.1719308563337165e-10,4.255982233303257e-10,4.3381759295968436e-10,4.4186720948857783e-10,4.497613115272969e-10,4.57512583373898e-10,4.6513240481438345e-10,4.726310454117311e-10,4.800177477726209e-10,4.873009773476156e-10,4.944885056978876e-10,5.015873272284921e-10,5.086040477664255e-10,5.155446070048697e-10,5.224146670812502e-10,5.292193350214802e-10,5.359634958068682e-10,5.426517013518151e-10,5.492881705038144e-10,5.558769555769061e-10,5.624218868405251e-10,5.689264614971989e-10,5.75394121238304e-10,5.818281967329142e-10,5.882316855831959e-10,5.946076964136182e-10,6.009590047817426e-10,6.072883862451306e-10,6.135985053390414e-10,6.19892026598734e-10,6.261713370037114e-10,6.324390455780815e-10,6.386973727678935e-10,6.449488165749528e-10,6.511955974453087e-10,6.574400468473129e-10,6.636843297158634e-10,6.699307220081607e-10,6.761814441702541e-10,6.824387166481927e-10,6.887046488657234e-10,6.949815167800466e-10,7.012714853260604e-10,7.075767749498141e-10,7.13899661608508e-10,7.202424212593428e-10,7.266072743483676e-10,7.329966078550854e-10,7.394128087589991e-10,7.458582640396116e-10,7.523354716987285e-10,7.588469852493063e-10,7.653954137154528e-10,7.719834771435785e-10,7.786139510912449e-10,7.852897221383159e-10,7.920137878869582e-10,7.987892014504894e-10,8.056192379868321e-10,8.125072836762115e-10,8.194568912323064e-10,8.264716688799467e-10,8.3355555791087e-10,8.407127216614185e-10,8.479473234679347e-10,8.552640262671218e-10,8.626675485068347e-10,8.701631637464402e-10,8.777562010564566e-10,8.854524335966119e-10,8.932581896381464e-10,9.011799639857543e-10,9.092249730890956e-10,9.174008219758889e-10,9.25715837318819e-10,9.341788453909317e-10,9.42799727177146e-10,9.515889187738935e-10,9.605578554783278e-10,9.697193048552322e-10,9.790869226478094e-10,9.886760299337993e-10,9.985036131254788e-10,1.008588212947359e-09,1.0189509236369076e-09,1.0296150598776421e-09,1.040606933955246e-09,1.0519566329136865e-09,1.0636980185552147e-09,1.0758701707302976e-09,1.0885182755160372e-09,1.101694735439196e-09,1.115461056855338e-09,1.1298901814171813e-09,1.1450695946990663e-09,1.1611052119775422e-09,1.178127595480305e-09,1.1962995039027646e-09,1.2158286599728285e-09,1.2369856250415978e-09,1.2601323318151003e-09,1.2857697129220469e-09,1.3146201904845611e-09,1.3477839955200466e-09,1.3870635751089821e-09,1.43574030442295e-09,1.5008658760251592e-09,1.6030947680434338e-09]);I=$toNativeArray($kindFloat32,[1,0.963599681854248,0.9362826943397522,0.9130436182022095,0.8922816514968872,0.8732430338859558,0.8555005788803101,0.8387836217880249,0.8229072093963623,0.8077383041381836,0.7931770086288452,0.7791460752487183,0.7655841708183289,0.7524415850639343,0.7396772503852844,0.7272568941116333,0.7151514887809753,0.7033361196517944,0.6917891502380371,0.6804918646812439,0.6694276928901672,0.6585819721221924,0.6479418277740479,0.6374954581260681,0.6272324919700623,0.6171433925628662,0.6072195172309875,0.5974531769752502,0.5878370404243469,0.5783646702766418,0.5690299868583679,0.5598273873329163,0.550751805305481,0.5417983531951904,0.5329626798629761,0.5242405533790588,0.5156282186508179,0.5071220397949219,0.49871864914894104,0.4904148280620575,0.48220765590667725,0.47409430146217346,0.466072142124176,0.45813870429992676,0.45029163360595703,0.44252872467041016,0.4348478317260742,0.42724698781967163,0.41972434520721436,0.41227802634239197,0.40490642189979553,0.39760786294937134,0.3903807997703552,0.3832238018512726,0.3761354684829712,0.3691144585609436,0.36215949058532715,0.3552693724632263,0.3484429717063904,0.3416791558265686,0.33497685194015503,0.32833510637283325,0.3217529058456421,0.3152293860912323,0.30876362323760986,0.3023548424243927,0.2960021495819092,0.2897048592567444,0.28346219658851624,0.2772735059261322,0.271138072013855,0.2650552988052368,0.25902456045150757,0.25304529070854187,0.24711695313453674,0.24123899638652802,0.23541094362735748,0.22963231801986694,0.22390270233154297,0.21822164952754974,0.21258877217769623,0.20700371265411377,0.20146611332893372,0.1959756463766098,0.19053204357624054,0.18513499200344086,0.17978426814079285,0.1744796335697174,0.16922089457511902,0.16400785744190216,0.1588403731584549,0.15371830761432648,0.14864157140254974,0.14361007511615753,0.13862377405166626,0.13368265330791473,0.12878671288490295,0.12393598258495331,0.11913054436445236,0.11437050998210907,0.10965602099895477,0.1049872562289238,0.10036443918943405,0.09578784555196762,0.09125780314207077,0.08677466958761215,0.08233889937400818,0.07795098423957825,0.07361150532960892,0.06932111829519272,0.06508058309555054,0.06089077144861221,0.05675266310572624,0.05266740173101425,0.048636294901371,0.044660862535238266,0.040742866694927216,0.03688438981771469,0.03308788686990738,0.029356317594647408,0.025693291798233986,0.02210330404341221,0.018592102453112602,0.015167297795414925,0.011839478276669979,0.0086244847625494,0.005548994988203049,0.0026696291752159595]);AH=$toNativeArray($kindInt64,[new $Int64(-973649357,3952672746),new $Int64(-1065661887,3130416987),new $Int64(324977939,3414273807),new $Int64(1241840476,2806224363),new $Int64(-1477934308,1997590414),new $Int64(2103305448,2402795971),new $Int64(1663160183,1140819369),new $Int64(1120601685,1788868961),new $Int64(1848035537,1089001426),new $Int64(1235702047,873593504),new $Int64(1911387977,581324885),new $Int64(-1654874170,1609182556),new $Int64(1069394745,1241596776),new $Int64(1895445337,1771189259),new $Int64(-1374618802,3467012610),new $Int64(-140526423,2344407434),new $Int64(-1745367887,782467244),new $Int64(26335124,3404933915),new $Int64(1063924276,618867887),new $Int64(-968700782,520164395),new $Int64(-1591572833,1341358184),new $Int64(-1515085039,665794848),new $Int64(1527227641,3183648150),new $Int64(1781176124,696329606),new $Int64(1789146075,4151988961),new $Int64(-2087444114,998951326),new $Int64(-612324923,1364957564),new $Int64(63173359,4090230633),new $Int64(-1498029007,4009697548),new $Int64(248009524,2569622517),new $Int64(778703922,3742421481),new $Int64(-1109106023,1506914633),new $Int64(1738099768,1983412561),new $Int64(236311649,1436266083),new $Int64(-1111517500,3922894967),new $Int64(-1336974714,1792680179),new $Int64(563141142,1188796351),new $Int64(1349617468,405968250),new $Int64(1044074554,433754187),new $Int64(870549669,4073162024),new $Int64(-1094251604,433121399),new $Int64(2451824,4162580594),new $Int64(-137262572,4132415622),new $Int64(-1536231048,3033822028),new $Int64(2016407895,824682382),new $Int64(2366218,3583765414),new $Int64(-624604839,535386927),new $Int64(1637219058,2286693689),new $Int64(1453075389,2968466525),new $Int64(193683513,1351410206),new $Int64(-283806096,1412813499),new $Int64(492736522,4126267639),new $Int64(512765208,2105529399),new $Int64(2132966268,2413882233),new $Int64(947457634,32226200),new $Int64(1149341356,2032329073),new $Int64(106485445,1356518208),new $Int64(-2067810156,3430061722),new $Int64(-1484435135,3820169661),new $Int64(-1665985194,2981816134),new $Int64(1017155588,4184371017),new $Int64(206574701,2119206761),new $Int64(-852109057,2472200560),new $Int64(-560457548,2853524696),new $Int64(1307803389,1681119904),new $Int64(-174986835,95608918),new $Int64(392686347,3690479145),new $Int64(-1205570926,1397922290),new $Int64(-1159314025,1516129515),new $Int64(-320178155,1547420459),new $Int64(1311333971,1470949486),new $Int64(-1953469798,1336785672),new $Int64(-45086614,4131677129),new $Int64(-1392278100,4246329084),new $Int64(-1142500187,3788585631),new $Int64(-66478285,3080389532),new $Int64(-646438364,2215402037),new $Int64(391002300,1171593935),new $Int64(1408774047,1423855166),new $Int64(-519177718,2276716302),new $Int64(-368453140,2068027241),new $Int64(1369359303,3427553297),new $Int64(189241615,3289637845),new $Int64(1057480830,3486407650),new $Int64(-1512910664,3071877822),new $Int64(1159653919,3363620705),new $Int64(-934256930,4159821533),new $Int64(-76621938,1894661),new $Int64(-674493898,1156868282),new $Int64(348271067,776219088),new $Int64(-501428838,2425634259),new $Int64(1716021749,680510161),new $Int64(-574263456,1310101429),new $Int64(1095885995,2964454134),new $Int64(-325695512,3467098407),new $Int64(1990672920,2109628894),new $Int64(-2139648704,1232604732),new $Int64(-1838070714,3261916179),new $Int64(1699175360,434597899),new $Int64(235436061,1624796439),new $Int64(-1626402839,3589632480),new $Int64(1198416575,864579159),new $Int64(-1938748161,1380889830),new $Int64(619206309,2654509477),new $Int64(1419738251,1468209306),new $Int64(-1744284772,100794388),new $Int64(-1191421458,2991674471),new $Int64(-208666741,2224662036),new $Int64(-173659161,977097250),new $Int64(1351320195,726419512),new $Int64(-183459897,1747974366),new $Int64(-753095183,1556430604),new $Int64(-1049492215,1080776742),new $Int64(-385846958,280794874),new $Int64(117767733,919835643),new $Int64(-967009426,3434019658),new $Int64(-1951414480,2461941785),new $Int64(133215641,3615001066),new $Int64(417204809,3103414427),new $Int64(790056561,3380809712),new $Int64(-1267681408,2724693469),new $Int64(547796833,598827710),new $Int64(-1846559452,3452273442),new $Int64(-75778224,649274915),new $Int64(-801301329,2585724112),new $Int64(-1510934263,3165579553),new $Int64(1185578221,2635894283),new $Int64(-52910178,2053289721),new $Int64(985976581,3169337108),new $Int64(1170569632,144717764),new $Int64(1079216270,1383666384),new $Int64(-124804942,681540375),new $Int64(1375448925,537050586),new $Int64(-1964768344,315246468),new $Int64(226402871,849323088),new $Int64(-885062465,45543944),new $Int64(-946445250,2319052083),new $Int64(-40708194,3613090841),new $Int64(560472520,2992171180),new $Int64(-381863169,2068244785),new $Int64(917538188,4239862634),new $Int64(-1369555809,3892253031),new $Int64(720683925,958186149),new $Int64(-423297785,1877702262),new $Int64(1357886971,837674867),new $Int64(1837048883,1507589294),new $Int64(1905518400,873336795),new $Int64(-1879761037,2764496274),new $Int64(-1806480530,4196182374),new $Int64(-1066765755,550964545),new $Int64(818747069,420611474),new $Int64(-1924830376,204265180),new $Int64(1549974541,1787046383),new $Int64(1215581865,3102292318),new $Int64(418321538,1552199393),new $Int64(1243493047,980542004),new $Int64(267284263,3293718720),new $Int64(1179528763,3771917473),new $Int64(599484404,2195808264),new $Int64(252818753,3894702887),new $Int64(-1367475956,2099949527),new $Int64(1424094358,338442522),new $Int64(490737398,637158004),new $Int64(-1727621530,281976339),new $Int64(574970164,3619802330),new $Int64(-431930823,3084554784),new $Int64(-1264611183,4129772886),new $Int64(-2104399043,1680378557),new $Int64(-1621962591,3339087776),new $Int64(1680500332,4220317857),new $Int64(-1935828963,2959322499),new $Int64(1675600481,1488354890),new $Int64(-834863562,3958162143),new $Int64(-1226511573,2773705983),new $Int64(1876039582,225908689),new $Int64(-1183735113,908216283),new $Int64(-605696219,3574646075),new $Int64(-1827723091,1936937569),new $Int64(1519770881,75492235),new $Int64(816689472,1935193178),new $Int64(2142521206,2018250883),new $Int64(455141620,3943126022),new $Int64(-601399488,3066544345),new $Int64(1932392669,2793082663),new $Int64(-1239009361,3297036421),new $Int64(1640597065,2206987825),new $Int64(-553246738,807894872),new $Int64(-1781325307,766252117),new $Int64(2060649606,3833114345),new $Int64(845619743,1255067973),new $Int64(1201145605,741697208),new $Int64(-1476242608,2810093753),new $Int64(1109032642,4229340371),new $Int64(1462188720,1361684224),new $Int64(-1159399429,1906263026),new $Int64(475781207,3904421704),new $Int64(-623537128,1769075545),new $Int64(1062308525,2621599764),new $Int64(1279509432,3431891480),new $Int64(-1742751146,1871896503),new $Int64(128756421,1412808876),new $Int64(1605404688,952876175),new $Int64(-230443691,1824438899),new $Int64(1662295856,1005035476),new $Int64(-156574141,527508597),new $Int64(1288873303,3066806859),new $Int64(565995893,3244940914),new $Int64(-889746188,209092916),new $Int64(-247669406,1242699167),new $Int64(-713830396,456723774),new $Int64(1776978905,1001252870),new $Int64(1468772157,2026725874),new $Int64(857254202,2137562569),new $Int64(765939740,3183366709),new $Int64(1533887628,2612072960),new $Int64(56977098,1727148468),new $Int64(-1197583895,3803658212),new $Int64(1883670356,479946959),new $Int64(685713571,1562982345),new $Int64(-1946242443,1766109365),new $Int64(700596547,3257093788),new $Int64(-184714929,2365720207),new $Int64(93384808,3742754173),new $Int64(-458385235,2878193673),new $Int64(1096135042,2174002182),new $Int64(-834260953,3573511231),new $Int64(-754572527,1760299077),new $Int64(-1375627191,2260779833),new $Int64(-866019274,1452805722),new $Int64(-1229671918,2940011802),new $Int64(1890251082,1886183802),new $Int64(893897673,2514369088),new $Int64(1644345561,3924317791),new $Int64(-1974867432,500935732),new $Int64(1403501753,676580929),new $Int64(-1565912283,1184984890),new $Int64(-691968413,1271474274),new $Int64(-1828754738,3163791473),new $Int64(2051027584,2842487377),new $Int64(1511537551,2170968612),new $Int64(573262976,3535856740),new $Int64(-2053227187,1488599718),new $Int64(-1180531831,3408913763),new $Int64(-2086531912,2501050084),new $Int64(-875130448,1639124157),new $Int64(-2009482504,4088176393),new $Int64(1574896563,3989947576),new $Int64(-165243708,3414355209),new $Int64(-792329287,2275136352),new $Int64(-2057774345,2151835223),new $Int64(-931144933,1654534827),new $Int64(-679921451,377892833),new $Int64(-482716010,660204544),new $Int64(85706799,390828249),new $Int64(-1422172693,3402783878),new $Int64(-1468634160,3717936603),new $Int64(1113532086,2211058823),new $Int64(1564224320,2692150867),new $Int64(1952770442,1928910388),new $Int64(788716862,3931011137),new $Int64(1083670504,1112701047),new $Int64(-68150572,2452299106),new $Int64(-896164822,2337204777),new $Int64(1774877857,273889282),new $Int64(1798719843,1462008793),new $Int64(2138834788,1554494002),new $Int64(-1194967131,182675323),new $Int64(-1598554764,1882802136),new $Int64(589279648,3700220025),new $Int64(381039426,3083431543),new $Int64(-851859191,3622207527),new $Int64(338126939,432729309),new $Int64(-1667470126,2391914317),new $Int64(-1849558151,235747924),new $Int64(2120733629,3088823825),new $Int64(-745079795,2314658321),new $Int64(1165929723,2957634338),new $Int64(501323675,4117056981),new $Int64(1564699815,1482500298),new $Int64(-740826490,840489337),new $Int64(799522364,3483178565),new $Int64(532129761,2074004656),new $Int64(724246478,3643392642),new $Int64(-665153481,1583624461),new $Int64(-885822954,287473085),new $Int64(1667835381,3136843981),new $Int64(1138806821,1266970974),new $Int64(135185781,1998688839),new $Int64(392094735,1492900209),new $Int64(1031326774,1538112737),new $Int64(-2070568842,2207265429),new $Int64(-1886797613,963263315),new $Int64(1671145500,2295892134),new $Int64(1068469660,2002560897),new $Int64(-356250305,1369254035),new $Int64(33436120,3353312708),new $Int64(57507843,947771099),new $Int64(-1945755145,1747061399),new $Int64(1507240140,2047354631),new $Int64(720000810,4165367136),new $Int64(479265078,3388864963),new $Int64(-952181250,286492130),new $Int64(2045622690,2795735007),new $Int64(-715730566,3703961339),new $Int64(-148436487,1797825479),new $Int64(1429039600,1116589674),new $Int64(-1665420098,2593309206),new $Int64(1329049334,3404995677),new $Int64(-750579440,3453462936),new $Int64(1014767077,3016498634),new $Int64(75698599,1650371545),new $Int64(1592007860,212344364),new $Int64(1127766888,3843932156),new $Int64(-748019856,3573129983),new $Int64(-890581831,665897820),new $Int64(1071492673,1675628772),new $Int64(243225682,2831752928),new $Int64(2120298836,1486294219),new $Int64(-1954407413,268782709),new $Int64(-1002123503,4186179080),new $Int64(624342951,1613720397),new $Int64(857179861,2703686015),new $Int64(-911618704,2205342611),new $Int64(-672703993,1411666394),new $Int64(-1528454899,677744900),new $Int64(-1876628533,4172867247),new $Int64(135494707,2163418403),new $Int64(849547544,2841526879),new $Int64(-1117516959,1082141470),new $Int64(-1770111792,4046134367),new $Int64(51415528,2142943655),new $Int64(-249824333,3124627521),new $Int64(998228909,219992939),new $Int64(-1078790951,1756846531),new $Int64(1283749206,1225118210),new $Int64(-525858006,1647770243),new $Int64(-2035959705,444807907),new $Int64(2036369448,3952076173),new $Int64(53201823,1461839639),new $Int64(315761893,3699250910),new $Int64(702974850,1373688981),new $Int64(734022261,147523747),new $Int64(-2047330906,1211276581),new $Int64(1294440951,2548832680),new $Int64(1144696256,1995631888),new $Int64(-1992983070,2011457303),new $Int64(-1351022674,3057425772),new $Int64(667839456,81484597),new $Int64(-1681980888,3646681560),new $Int64(-1372462725,635548515),new $Int64(602489502,2508044581),new $Int64(-1794220117,1014917157),new $Int64(719992433,3214891315),new $Int64(-1294799037,959582252),new $Int64(226415134,3347040449),new $Int64(-362868096,4102971975),new $Int64(397887437,4078022210),new $Int64(-536803826,2851767182),new $Int64(-1398321012,1540160644),new $Int64(-1549098876,1057290595),new $Int64(-112592988,3907769253),new $Int64(579300318,4248952684),new $Int64(-1054576049,132554364),new $Int64(-1085862414,1029351092),new $Int64(697840928,2583007416),new $Int64(298619124,1486185789),new $Int64(55905697,2871589073),new $Int64(2017643612,723203291),new $Int64(146250550,2494333952),new $Int64(-1082993397,2230939180),new $Int64(-1804568072,3943232912),new $Int64(1768732449,2181367922),new $Int64(-729261111,2889274791),new $Int64(1824032949,2046728161),new $Int64(1653899792,1376052477),new $Int64(1022327048,381236993),new $Int64(-1113097690,3188942166),new $Int64(-74480109,350070824),new $Int64(144881592,61758415),new $Int64(-741824226,3492950336),new $Int64(-2030042720,3093818430),new $Int64(-453590535,2962480613),new $Int64(-1912050708,3154871160),new $Int64(-1636478569,3228564679),new $Int64(610731502,888276216),new $Int64(-946702974,3574998604),new $Int64(-1277068380,1967526716),new $Int64(-1556147941,1554691298),new $Int64(-1573024234,339944798),new $Int64(1223764147,1154515356),new $Int64(1825645307,967516237),new $Int64(1546195135,596588202),new $Int64(-1867600880,3764362170),new $Int64(-1655392592,266611402),new $Int64(-393255880,2047856075),new $Int64(-1000726433,21444105),new $Int64(-949424754,3065563181),new $Int64(-232418803,1140663212),new $Int64(633187674,2323741028),new $Int64(2126290159,3103873707),new $Int64(1008658319,2766828349),new $Int64(-485587503,1970872996),new $Int64(1628585413,3766615585),new $Int64(-595148528,2036813414),new $Int64(-1994877121,3105536507),new $Int64(13954645,3396176938),new $Int64(-721402003,1377154485),new $Int64(-61839181,3807014186),new $Int64(543009040,3710110597),new $Int64(-1751425519,916420443),new $Int64(734556788,2103831255),new $Int64(-1766161494,717331943),new $Int64(-1574598896,3550505941),new $Int64(45939673,378749927),new $Int64(-1997615719,611017331),new $Int64(592130075,758907650),new $Int64(1012992349,154266815),new $Int64(-1040454942,1407468696),new $Int64(-1678191250,970098704),new $Int64(-285057486,1971660656),new $Int64(998365243,3332747885),new $Int64(1947089649,1935189867),new $Int64(1510248801,203520055),new $Int64(-1305165746,3916463034),new $Int64(-388598655,3474113316),new $Int64(1036101639,316544223),new $Int64(-1773744891,1650844677),new $Int64(-907191419,4267565603),new $Int64(-1070275024,2501167616),new $Int64(-1520651863,3929401789),new $Int64(-2091360852,337170252),new $Int64(-960502090,2061966842),new $Int64(-304190848,2508461464),new $Int64(-1941471116,2791377107),new $Int64(1240791848,1227227588),new $Int64(1813978778,1709681848),new $Int64(1153692192,3768820575),new $Int64(-1002297449,2887126398),new $Int64(-1447111334,296561685),new $Int64(700300844,3729960077),new $Int64(-1572311344,372833036),new $Int64(2078875613,2409779288),new $Int64(1829161290,555274064),new $Int64(-1105595719,4239804901),new $Int64(1839403216,3723486978),new $Int64(-1649093095,2145871984),new $Int64(-1582765715,3565480803),new $Int64(-1568653827,2197313814),new $Int64(974785092,3613674566),new $Int64(438638731,3042093666),new $Int64(-96556264,3324034321),new $Int64(869420878,3708873369),new $Int64(946682149,1698090092),new $Int64(1618900382,4213940712),new $Int64(-1843479747,2087477361),new $Int64(-1766167800,2407950639),new $Int64(-1296225558,3942568569),new $Int64(-1223900450,4088074412),new $Int64(723260036,2964773675),new $Int64(-673921829,1539178386),new $Int64(1062961552,2694849566),new $Int64(460977733,2120273838),new $Int64(-1604570740,2484608657),new $Int64(880846449,2956190677),new $Int64(1970902366,4223313749),new $Int64(662161910,3502682327),new $Int64(705634754,4133891139),new $Int64(-1031359300,1166449596),new $Int64(1038247601,3362705993),new $Int64(93734798,3892921029),new $Int64(1876124043,786869787),new $Int64(1057490746,1046342263),new $Int64(242763728,493777327),new $Int64(-853573201,3304827646),new $Int64(616460742,125356352),new $Int64(499300063,74094113),new $Int64(-795586925,2500816079),new $Int64(-490248444,514015239),new $Int64(1377565129,543520454),new $Int64(-2039776725,3614531153),new $Int64(2056746300,2356753985),new $Int64(1390062617,2018141668),new $Int64(131272971,2087974891),new $Int64(-1502927041,3166972343),new $Int64(372256200,1517638666),new $Int64(-935275664,173466846),new $Int64(-695774461,4241513471),new $Int64(-1413550842,2783126920),new $Int64(1972004134,4167264826),new $Int64(29260506,3907395640),new $Int64(-910901561,1539634186),new $Int64(-595957298,178241987),new $Int64(-113277636,182168164),new $Int64(-1102530459,2386154934),new $Int64(1379126408,4077374341),new $Int64(-2114679722,1732699140),new $Int64(-421057745,1041306002),new $Int64(1860414813,2068001749),new $Int64(1005320202,3208962910),new $Int64(844054010,697710380),new $Int64(-1509359403,2228431183),new $Int64(-810313977,3554678728),new $Int64(-750989047,173470263),new $Int64(-85886265,3848297795),new $Int64(-926936977,246236185),new $Int64(-1984190461,2066374846),new $Int64(1771673660,312890749),new $Int64(703378057,3573310289),new $Int64(-598851901,143166754),new $Int64(613554316,2081511079),new $Int64(1197802104,486038032),new $Int64(-1906483789,2982218564),new $Int64(364901986,1000939191),new $Int64(1902782651,2750454885),new $Int64(-671844857,3375313137),new $Int64(-1643868040,881302957),new $Int64(-1508784745,2514186393),new $Int64(-1703622845,360024739),new $Int64(1399671872,292500025),new $Int64(1381210821,2276300752),new $Int64(521803381,4069087683),new $Int64(-1938982667,1637778212),new $Int64(720490469,1676670893),new $Int64(1067262482,3855174429),new $Int64(2114075974,2067248671),new $Int64(-89426259,2884561259),new $Int64(-805741095,2456511185),new $Int64(983726246,561175414),new $Int64(-1719489563,432588903),new $Int64(885133709,4059399550),new $Int64(-93096266,1075014784),new $Int64(-1733832628,2728058415),new $Int64(1839142064,1299703678),new $Int64(1262333188,2347583393),new $Int64(1285481956,2468164145),new $Int64(-1158354011,1140014346),new $Int64(2033889184,1936972070),new $Int64(-1737578993,3870530098),new $Int64(-484494257,1717789158),new $Int64(-232997156,1153452491),new $Int64(-990424416,3948827651),new $Int64(-1357145630,2101413152),new $Int64(1495744672,3854091229),new $Int64(83644069,4215565463),new $Int64(-1385277313,1202710438),new $Int64(-564909037,2072216740),new $Int64(705690639,2066751068),new $Int64(-2113583312,173902580),new $Int64(-741983806,142459001),new $Int64(172391592,1889151926),new $Int64(-498943125,3034199774),new $Int64(1618587731,516490102),new $Int64(93114264,3692577783),new $Int64(-2078821353,2953948865),new $Int64(-320938673,4041040923),new $Int64(-1942517976,592046130),new $Int64(-705643640,384297211),new $Int64(-2051649464,265863924),new $Int64(2101717619,1333136237),new $Int64(1499611781,1406273556),new $Int64(1074670496,426305476),new $Int64(125704633,2750898176),new $Int64(488068495,1633944332),new $Int64(2037723464,3236349343),new $Int64(-1703423246,4013676611),new $Int64(1718532237,2265047407),new $Int64(1433593806,875071080),new $Int64(-343047503,1418843655),new $Int64(2009228711,451657300),new $Int64(1229446621,1866374663),new $Int64(1653472867,1551455622),new $Int64(577191481,3560962459),new $Int64(1669204077,3347903778),new $Int64(-298327194,2675874918),new $Int64(-1831355577,2762991672),new $Int64(530492383,3689068477),new $Int64(844089962,4071997905),new $Int64(1508155730,1381702441),new $Int64(2089931018,2373284878),new $Int64(-864267462,2143983064),new $Int64(308739063,1938207195),new $Int64(1754949306,1188152253),new $Int64(1272345009,615870490),new $Int64(742653194,2662252621),new $Int64(1477718295,3839976789),new $Int64(-2091334213,306752547),new $Int64(-1426688067,2162363077),new $Int64(-57052633,2767224719),new $Int64(-1471624099,2628837712),new $Int64(1678405918,2967771969),new $Int64(1694285728,499792248),new $Int64(-1744131281,4285253508),new $Int64(962357072,2856511070),new $Int64(679471692,2526409716),new $Int64(-1793706473,1240875658),new $Int64(-914893422,2577342868),new $Int64(-1001298215,4136853496),new $Int64(-1477114974,2403540137),new $Int64(1372824515,1371410668),new $Int64(-176562048,371758825),new $Int64(-441063112,1528834084),new $Int64(-71688630,1504757260),new $Int64(-1461820072,699052551),new $Int64(-505543539,3347789870),new $Int64(1951619734,3430604759),new $Int64(2119672219,1935601723),new $Int64(966789690,834676166)]);P=N(new AG.ptr(new B.Mutex.ptr(false),$assertType(L(new $Int64(0,1)),K)));}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["net"]=(function(){var $pkg={},$init,E,A,C,F,J,K,Q,N,O,M,G,H,D,I,P,B,L,AG,AH,AK,EE,EF,EM,EX,EY,EZ,FU,HS,HW,II,IM,IN,IO,IP,JJ,LC,NE,NG,NM,NP,NX,OE,OM,OQ,OR,OS,OT,OU,OV,OW,PM,PN,PP,QL,QM,QN,QO,QT,AI,BX,BY,BZ,CA,CB,CC,CD,DM,DZ,EA,EB,EC,ED,EG,EN,FB,FF,FG,FH,HU,IE,IF,IG,IJ,IT,c,d,e,f,g,h,i,j,k,AJ,AL,AP,CY,EK,EO,EP,EQ,ER,ES,ET,EU,EV,EW,FA,FC,FD,FE,FI,FJ,FK,FL,FM,FN,FO,FQ,FR,FT,JK,JM,JN,JP,JQ,JR,JT,JU;E=$packages["context"];A=$packages["errors"];C=$packages["github.com/gopherjs/gopherjs/js"];F=$packages["internal/bytealg"];J=$packages["internal/nettrace"];K=$packages["internal/poll"];Q=$packages["internal/singleflight"];N=$packages["internal/x/net/dns/dnsmessage"];O=$packages["io"];M=$packages["math/rand"];G=$packages["os"];H=$packages["runtime"];D=$packages["sort"];I=$packages["sync"];P=$packages["sync/atomic"];B=$packages["syscall"];L=$packages["time"];AG=$pkg.policyTableEntry=$newType(0,$kindStruct,"net.policyTableEntry",true,"net",false,function(Prefix_,Precedence_,Label_){this.$val=this;if(arguments.length===0){this.Prefix=NP.nil;this.Precedence=0;this.Label=0;return;}this.Prefix=Prefix_;this.Precedence=Precedence_;this.Label=Label_;});AH=$pkg.policyTable=$newType(12,$kindSlice,"net.policyTable",true,"net",false,null);AK=$pkg.byMaskLength=$newType(12,$kindSlice,"net.byMaskLength",true,"net",false,null);EE=$pkg.Interface=$newType(0,$kindStruct,"net.Interface",true,"net",true,function(Index_,MTU_,Name_,HardwareAddr_,Flags_){this.$val=this;if(arguments.length===0){this.Index=0;this.MTU=0;this.Name="";this.HardwareAddr=HS.nil;this.Flags=0;return;}this.Index=Index_;this.MTU=MTU_;this.Name=Name_;this.HardwareAddr=HardwareAddr_;this.Flags=Flags_;});EF=$pkg.Flags=$newType(4,$kindUint,"net.Flags",true,"net",true,null);EM=$pkg.ipv6ZoneCache=$newType(0,$kindStruct,"net.ipv6ZoneCache",true,"net",false,function(RWMutex_,lastFetched_,toIndex_,toName_){this.$val=this;if(arguments.length===0){this.RWMutex=new I.RWMutex.ptr(new I.Mutex.ptr(0,0),0,0,0,0);this.lastFetched=new L.Time.ptr(new $Uint64(0,0),new $Int64(0,0),NE.nil);this.toIndex=false;this.toName=false;return;}this.RWMutex=RWMutex_;this.lastFetched=lastFetched_;this.toIndex=toIndex_;this.toName=toName_;});EX=$pkg.IP=$newType(12,$kindSlice,"net.IP",true,"net",true,null);EY=$pkg.IPMask=$newType(12,$kindSlice,"net.IPMask",true,"net",true,null);EZ=$pkg.IPNet=$newType(0,$kindStruct,"net.IPNet",true,"net",true,function(IP_,Mask_){this.$val=this;if(arguments.length===0){this.IP=EX.nil;this.Mask=EY.nil;return;}this.IP=IP_;this.Mask=Mask_;});FU=$pkg.IPAddr=$newType(0,$kindStruct,"net.IPAddr",true,"net",true,function(IP_,Zone_){this.$val=this;if(arguments.length===0){this.IP=EX.nil;this.Zone="";return;}this.IP=IP_;this.Zone=Zone_;});HS=$pkg.HardwareAddr=$newType(12,$kindSlice,"net.HardwareAddr",true,"net",true,null);HW=$pkg.Addr=$newType(8,$kindInterface,"net.Addr",true,"net",true,null);II=$pkg.OpError=$newType(0,$kindStruct,"net.OpError",true,"net",true,function(Op_,Net_,Source_,Addr_,Err_){this.$val=this;if(arguments.length===0){this.Op="";this.Net="";this.Source=$ifaceNil;this.Addr=$ifaceNil;this.Err=$ifaceNil;return;}this.Op=Op_;this.Net=Net_;this.Source=Source_;this.Addr=Addr_;this.Err=Err_;});IM=$pkg.timeout=$newType(8,$kindInterface,"net.timeout",true,"net",false,null);IN=$pkg.temporary=$newType(8,$kindInterface,"net.temporary",true,"net",false,null);IO=$pkg.ParseError=$newType(0,$kindStruct,"net.ParseError",true,"net",true,function(Type_,Text_){this.$val=this;if(arguments.length===0){this.Type="";this.Text="";return;}this.Type=Type_;this.Text=Text_;});IP=$pkg.AddrError=$newType(0,$kindStruct,"net.AddrError",true,"net",true,function(Err_,Addr_){this.$val=this;if(arguments.length===0){this.Err="";this.Addr="";return;}this.Err=Err_;this.Addr=Addr_;});JJ=$pkg.file=$newType(0,$kindStruct,"net.file",true,"net",false,function(file_,data_,atEOF_){this.$val=this;if(arguments.length===0){this.file=OM.nil;this.data=NM.nil;this.atEOF=false;return;}this.file=file_;this.data=data_;this.atEOF=atEOF_;});LC=$pkg.sockaddr=$newType(8,$kindInterface,"net.sockaddr",true,"net",false,null);NE=$ptrType(L.Location);NG=$sliceType($String);NM=$sliceType($Uint8);NP=$ptrType(EZ);NX=$ptrType(FU);OE=$ptrType(II);OM=$ptrType(G.File);OQ=$ptrType(JJ);OR=$ptrType(EE);OS=$sliceType(HW);OT=$sliceType(EE);OU=$ptrType(B.IfInfomsg);OV=$ptrType(B.IfAddrmsg);OW=$arrayType($Uint8,4);PM=$ptrType(G.SyscallError);PN=$ptrType(IP);PP=$arrayType($Uint8,20);QL=$ptrType(EM);QM=$mapType($String,$Int);QN=$mapType($Int,$String);QO=$ptrType(EX);QT=$ptrType(IO);AJ=function(){var $s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=D.Sort(D.Reverse(($subslice(new AK(AI.$array),AI.$offset,AI.$offset+AI.$length))));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AJ};}$f.$s=$s;$f.$r=$r;return $f;};AK.prototype.Len=function(){var l;l=this;return l.$length;};$ptrType(AK).prototype.Len=function(){return this.$get().Len();};AK.prototype.Swap=function(l,m){var l,m,n,o,p;n=this;o=$clone(((m<0||m>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+m]),AG);p=$clone(((l<0||l>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+l]),AG);AG.copy(((l<0||l>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+l]),o);AG.copy(((m<0||m>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+m]),p);};$ptrType(AK).prototype.Swap=function(l,m){return this.$get().Swap(l,m);};AK.prototype.Less=function(l,m){var l,m,n,o,p,q,r;n=this;o=((l<0||l>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+l]).Prefix.Mask.Size();p=o[0];q=((m<0||m>=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+m]).Prefix.Mask.Size();r=q[0];return p=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]),AG);if(p.Prefix.Contains(l)){return p;}o++;}return new AG.ptr(NP.nil,0,0);};$ptrType(AH).prototype.Classify=function(l){return this.$get().Classify(l);};AP=function(){HU=true;};CY=function(l){var l,m,n,o;m=$assertType(l,B.Errno,true);n=m[0];o=m[1];if(o){return(n===104)||(n===103);}return false;};EF.prototype.String=function(){var l,m,n,o,p,q,r;l=this.$val;m="";n=EG;o=0;while(true){if(!(o=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]);if(!((((l&(((r=((p>>>0)),r<32?(1<>>0)))>>>0)===0))){if(!(m==="")){m=m+("|");}m=m+(q);}o++;}if(m===""){m="0";}return m;};$ptrType(EF).prototype.String=function(){return new EF(this.$get()).String();};EE.ptr.prototype.Addrs=function(){var l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(l===OR.nil){$s=-1;return[OS.nil,new II.ptr("route","ip+net",$ifaceNil,$ifaceNil,DZ)];}n=ER(l);$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m[0];p=m[1];if(!($interfaceIsEqual(p,$ifaceNil))){p=new II.ptr("route","ip+net",$ifaceNil,$ifaceNil,p);}$s=-1;return[o,p];}return;}if($f===undefined){$f={$blk:EE.ptr.prototype.Addrs};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};EE.prototype.Addrs=function(){return this.$val.Addrs();};EE.ptr.prototype.MulticastAddrs=function(){var l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(l===OR.nil){$s=-1;return[OS.nil,new II.ptr("route","ip+net",$ifaceNil,$ifaceNil,DZ)];}n=EU(l);$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m[0];p=m[1];if(!($interfaceIsEqual(p,$ifaceNil))){p=new II.ptr("route","ip+net",$ifaceNil,$ifaceNil,p);}$s=-1;return[o,p];}return;}if($f===undefined){$f={$blk:EE.ptr.prototype.MulticastAddrs};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};EE.prototype.MulticastAddrs=function(){return this.$val.MulticastAddrs();};EK=function(l,m){var l,m,n,o,p;n=l;o=0;while(true){if(!(o=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]),EE);if(m===p.Index){return[p,$ifaceNil];}o++;}return[OR.nil,EC];};EO=function(l){var aa,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:m=[m];o=B.NetlinkRIB(18,0);$s=1;case 1:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;p=n[0];q=n[1];if(!($interfaceIsEqual(q,$ifaceNil))){$s=-1;return[OT.nil,G.NewSyscallError("netlinkrib",q)];}r=B.ParseNetlinkMessage(p);s=r[0];q=r[1];if(!($interfaceIsEqual(q,$ifaceNil))){$s=-1;return[OT.nil,G.NewSyscallError("parsenetlinkmessage",q)];}t=OT.nil;u=s;v=0;loop:while(true){if(!(v=u.$length)?($throwRuntimeError("index out of range"),undefined):u.$array[u.$offset+v]),B.NetlinkMessage);w=m[0].Header.Type;if(w===(3)){break loop;}else if(w===(16)){x=($pointerOfStructConversion(($sliceToArray(m[0].Data)),OU));if((l===0)||(l===((x.Index>>0)))){y=B.ParseNetlinkRouteAttr(m[0]);z=y[0];aa=y[1];if(!($interfaceIsEqual(aa,$ifaceNil))){$s=-1;return[OT.nil,G.NewSyscallError("parsenetlinkrouteattr",aa)];}t=$append(t,EP(x,z));if(l===((x.Index>>0))){break loop;}}}v++;}$s=-1;return[t,$ifaceNil];}return;}if($f===undefined){$f={$blk:EO};}$f.aa=aa;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};EP=function(l,m){var aa,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;n=new EE.ptr(((l.Index>>0)),0,"",HS.nil,EQ(l.Flags));o=m;p=0;while(true){if(!(p=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]),B.NetlinkRouteAttr);r=q.Attr.Type;if(r===(1)){s=q.Value.$length;if(s===(4)){t=l.Type;if((t===(768))||(t===(778))||(t===(776))){p++;continue;}}else if(s===(16)){u=l.Type;if((u===(769))||(u===(823))){p++;continue;}}v=false;w=q.Value;x=0;while(true){if(!(x=w.$length)?($throwRuntimeError("index out of range"),undefined):w.$array[w.$offset+x]);if(!((y===0))){v=true;break;}x++;}if(v){n.HardwareAddr=(z=q.Value,$subslice(new HS(z.$array),z.$offset,z.$offset+z.$length));}}else if(r===(3)){n.Name=($bytesToString($subslice(q.Value,0,(q.Value.$length-1>>0))));}else if(r===(4)){n.MTU=(((aa=$subslice(q.Value,0,4),(0>=aa.$length?($throwRuntimeError("index out of range"),undefined):aa.$array[aa.$offset+0]))>>0));}p++;}return n;};EQ=function(l){var l,m;m=0;if(!((((l&1)>>>0)===0))){m=(m|(1))>>>0;}if(!((((l&2)>>>0)===0))){m=(m|(2))>>>0;}if(!((((l&8)>>>0)===0))){m=(m|(4))>>>0;}if(!((((l&16)>>>0)===0))){m=(m|(8))>>>0;}if(!((((l&4096)>>>0)===0))){m=(m|(16))>>>0;}return m;};ER=function(l){var l,m,n,o,p,q,r,s,t,u,v,w,x,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:n=B.NetlinkRIB(22,0);$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m[0];p=m[1];if(!($interfaceIsEqual(p,$ifaceNil))){$s=-1;return[OS.nil,G.NewSyscallError("netlinkrib",p)];}q=B.ParseNetlinkMessage(o);r=q[0];p=q[1];if(!($interfaceIsEqual(p,$ifaceNil))){$s=-1;return[OS.nil,G.NewSyscallError("parsenetlinkmessage",p)];}s=OT.nil;if(l===OR.nil){$s=2;continue;}$s=3;continue;case 2:t=$ifaceNil;v=EO(0);$s=4;case 4:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}u=v;s=u[0];t=u[1];if(!($interfaceIsEqual(t,$ifaceNil))){$s=-1;return[OS.nil,t];}case 3:w=ES(s,l,r);x=w[0];p=w[1];if(!($interfaceIsEqual(p,$ifaceNil))){$s=-1;return[OS.nil,p];}$s=-1;return[x,$ifaceNil];}return;}if($f===undefined){$f={$blk:ER};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.$s=$s;$f.$r=$r;return $f;};ES=function(l,m,n){var l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;o=OS.nil;p=n;q=0;loop:while(true){if(!(q=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+q]),B.NetlinkMessage);s=r.Header.Type;if(s===(3)){break loop;}else if(s===(20)){t=($pointerOfStructConversion(($sliceToArray(r.Data)),OV));if(!((l.$length===0))||(m.Index===((t.Index>>0)))){if(!((l.$length===0))){u=$ifaceNil;v=EK(l,((t.Index>>0)));m=v[0];u=v[1];if(!($interfaceIsEqual(u,$ifaceNil))){return[OS.nil,u];}}w=B.ParseNetlinkRouteAttr(r);x=w[0];y=w[1];if(!($interfaceIsEqual(y,$ifaceNil))){return[OS.nil,G.NewSyscallError("parsenetlinkrouteattr",y)];}z=ET(t,x);if(!($interfaceIsEqual(z,$ifaceNil))){o=$append(o,z);}}}q++;}return[o,$ifaceNil];};ET=function(l,m){var l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;n=false;o=m;p=0;while(true){if(!(p=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]),B.NetlinkRouteAttr);if(q.Attr.Type===2){n=true;break;}p++;}r=m;s=0;while(true){if(!(s=r.$length)?($throwRuntimeError("index out of range"),undefined):r.$array[r.$offset+s]),B.NetlinkRouteAttr);if(n&&(t.Attr.Type===1)){s++;continue;}u=l.Family;if(u===(2)){return new EZ.ptr(FA((v=t.Value,(0>=v.$length?($throwRuntimeError("index out of range"),undefined):v.$array[v.$offset+0])),(w=t.Value,(1>=w.$length?($throwRuntimeError("index out of range"),undefined):w.$array[w.$offset+1])),(x=t.Value,(2>=x.$length?($throwRuntimeError("index out of range"),undefined):x.$array[x.$offset+2])),(y=t.Value,(3>=y.$length?($throwRuntimeError("index out of range"),undefined):y.$array[y.$offset+3]))),FD(((l.Prefixlen>>0)),32));}else if(u===(10)){z=new EZ.ptr($makeSlice(EX,16),FD(((l.Prefixlen>>0)),128));$copySlice(z.IP,t.Value);return z;}s++;}return $ifaceNil;};EU=function(l){var l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:m=EV("/proc/net/igmp",l);$s=1;case 1:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}n=m;o=EW("/proc/net/igmp6",l);$s=2;case 2:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}p=o;$s=-1;return[$appendSlice(n,p),$ifaceNil];}return;}if($f===undefined){$f={$blk:EU};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};EV=function(l,m){var aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);o=JK(l);$s=1;case 1:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;p=n[0];q=n[1];if(!($interfaceIsEqual(q,$ifaceNil))){$s=-1;return OS.nil;}$deferred.push([$methodVal(p,"close"),[]]);r=OS.nil;s="";t=p.readLine();$s=2;case 2:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}t;u=$makeSlice(NM,4);w=p.readLine();$s=3;case 3:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}v=w;x=v[0];y=v[1];case 4:if(!(y)){$s=5;continue;}z=JN(x," :\r\t\n");if(z.$length<4){$s=6;continue;}$s=7;continue;case 6:ab=p.readLine();$s=8;case 8:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}aa=ab;x=aa[0];y=aa[1];$s=4;continue;case 7:if(!((x.charCodeAt(0)===32))&&!((x.charCodeAt(0)===9))){s=(1>=z.$length?($throwRuntimeError("index out of range"),undefined):z.$array[z.$offset+1]);}else if(((0>=z.$length?($throwRuntimeError("index out of range"),undefined):z.$array[z.$offset+0]).length===8)){if(m===OR.nil||s===m.Name){ac=0;while(true){if(!((ac+1>>0)<(0>=z.$length?($throwRuntimeError("index out of range"),undefined):z.$array[z.$offset+0]).length)){break;}ad=JR($substring((0>=z.$length?($throwRuntimeError("index out of range"),undefined):z.$array[z.$offset+0]),ac,(ac+2>>0)),0);(ae=(af=ac/2,(af===af&&af!==1/0&&af!==-1/0)?af>>0:$throwRuntimeError("integer divide by zero")),((ae<0||ae>=u.$length)?($throwRuntimeError("index out of range"),undefined):u.$array[u.$offset+ae]=ad[0]));ac=ac+(2)>>0;}ah=(ag=$subslice(u,0,4),(0>=ag.$length?($throwRuntimeError("index out of range"),undefined):ag.$array[ag.$offset+0]));ai=new FU.ptr(FA((((ah>>>24>>>0)<<24>>>24)),(((ah>>>16>>>0)<<24>>>24)),(((ah>>>8>>>0)<<24>>>24)),((ah<<24>>>24))),"");r=$append(r,ai);}}ak=p.readLine();$s=9;case 9:if($c){$c=false;ak=ak.$blk();}if(ak&&ak.$blk!==undefined){break s;}aj=ak;x=aj[0];y=aj[1];$s=4;continue;case 5:$s=-1;return r;}return;}}catch(err){$err=err;$s=-1;return OS.nil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:EV};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};EW=function(l,m){var aa,ab,ac,ad,ae,af,ag,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);o=JK(l);$s=1;case 1:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;p=n[0];q=n[1];if(!($interfaceIsEqual(q,$ifaceNil))){$s=-1;return OS.nil;}$deferred.push([$methodVal(p,"close"),[]]);r=OS.nil;s=$makeSlice(NM,16);u=p.readLine();$s=2;case 2:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}t=u;v=t[0];w=t[1];case 3:if(!(w)){$s=4;continue;}x=JN(v," \r\t\n");if(x.$length<6){$s=5;continue;}$s=6;continue;case 5:z=p.readLine();$s=7;case 7:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}y=z;v=y[0];w=y[1];$s=3;continue;case 6:if(m===OR.nil||(1>=x.$length?($throwRuntimeError("index out of range"),undefined):x.$array[x.$offset+1])===m.Name){aa=0;while(true){if(!((aa+1>>0)<(2>=x.$length?($throwRuntimeError("index out of range"),undefined):x.$array[x.$offset+2]).length)){break;}ab=JR($substring((2>=x.$length?($throwRuntimeError("index out of range"),undefined):x.$array[x.$offset+2]),aa,(aa+2>>0)),0);(ac=(ad=aa/2,(ad===ad&&ad!==1/0&&ad!==-1/0)?ad>>0:$throwRuntimeError("integer divide by zero")),((ac<0||ac>=s.$length)?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+ac]=ab[0]));aa=aa+(2)>>0;}ae=new FU.ptr(new EX([(0>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+0]),(1>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+1]),(2>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+2]),(3>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+3]),(4>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+4]),(5>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+5]),(6>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+6]),(7>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+7]),(8>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+8]),(9>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+9]),(10>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+10]),(11>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+11]),(12>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+12]),(13>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+13]),(14>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+14]),(15>=s.$length?($throwRuntimeError("index out of range"),undefined):s.$array[s.$offset+15])]),"");r=$append(r,ae);}ag=p.readLine();$s=8;case 8:if($c){$c=false;ag=ag.$blk();}if(ag&&ag.$blk!==undefined){break s;}af=ag;v=af[0];w=af[1];$s=3;continue;case 4:$s=-1;return r;}return;}}catch(err){$err=err;$s=-1;return OS.nil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:EW};}$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};FA=function(l,m,n,o){var l,m,n,o,p;p=$makeSlice(EX,16);$copySlice(p,FB);(12>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+12]=l);(13>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+13]=m);(14>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+14]=n);(15>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+15]=o);return p;};$pkg.IPv4=FA;FC=function(l,m,n,o){var l,m,n,o,p;p=$makeSlice(EY,4);(0>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+0]=l);(1>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+1]=m);(2>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+2]=n);(3>=p.$length?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+3]=o);return p;};$pkg.IPv4Mask=FC;FD=function(l,m){var l,m,n,o,p,q,r,s;if(!((m===32))&&!((m===128))){return EY.nil;}if(l<0||l>m){return EY.nil;}o=(n=m/8,(n===n&&n!==1/0&&n!==-1/0)?n>>0:$throwRuntimeError("integer divide by zero"));p=$makeSlice(EY,o);q=((l>>>0));r=0;while(true){if(!(r=8){((r<0||r>=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+r]=255);q=q-(8)>>>0;r=r+(1)>>0;continue;}((r<0||r>=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+r]=(~(((s=q,s<32?(255>>>s):0)<<24>>>24))<<24>>>24));q=0;r=r+(1)>>0;}return p;};$pkg.CIDRMask=FD;EX.prototype.IsUnspecified=function(){var l;l=this;return l.Equal($pkg.IPv4zero)||l.Equal($pkg.IPv6unspecified);};$ptrType(EX).prototype.IsUnspecified=function(){return this.$get().IsUnspecified();};EX.prototype.IsLoopback=function(){var l,m;l=this;m=l.To4();if(!(m===EX.nil)){return(0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0])===127;}return l.Equal($pkg.IPv6loopback);};$ptrType(EX).prototype.IsLoopback=function(){return this.$get().IsLoopback();};EX.prototype.IsMulticast=function(){var l,m;l=this;m=l.To4();if(!(m===EX.nil)){return(((0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0])&240)>>>0)===224;}return(l.$length===16)&&((0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])===255);};$ptrType(EX).prototype.IsMulticast=function(){return this.$get().IsMulticast();};EX.prototype.IsInterfaceLocalMulticast=function(){var l;l=this;return(l.$length===16)&&((0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])===255)&&((((1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1])&15)>>>0)===1);};$ptrType(EX).prototype.IsInterfaceLocalMulticast=function(){return this.$get().IsInterfaceLocalMulticast();};EX.prototype.IsLinkLocalMulticast=function(){var l,m;l=this;m=l.To4();if(!(m===EX.nil)){return((0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0])===224)&&((1>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+1])===0)&&((2>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+2])===0);}return(l.$length===16)&&((0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])===255)&&((((1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1])&15)>>>0)===2);};$ptrType(EX).prototype.IsLinkLocalMulticast=function(){return this.$get().IsLinkLocalMulticast();};EX.prototype.IsLinkLocalUnicast=function(){var l,m;l=this;m=l.To4();if(!(m===EX.nil)){return((0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0])===169)&&((1>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+1])===254);}return(l.$length===16)&&((0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])===254)&&((((1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1])&192)>>>0)===128);};$ptrType(EX).prototype.IsLinkLocalUnicast=function(){return this.$get().IsLinkLocalUnicast();};EX.prototype.IsGlobalUnicast=function(){var l;l=this;return((l.$length===4)||(l.$length===16))&&!l.Equal($pkg.IPv4bcast)&&!l.IsUnspecified()&&!l.IsLoopback()&&!l.IsMulticast()&&!l.IsLinkLocalUnicast();};$ptrType(EX).prototype.IsGlobalUnicast=function(){return this.$get().IsGlobalUnicast();};FE=function(l){var l,m;m=0;while(true){if(!(m=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+m])===0))){return false;}m=m+(1)>>0;}return true;};EX.prototype.To4=function(){var l;l=this;if(l.$length===4){return l;}if((l.$length===16)&&FE($subslice(l,0,10))&&((10>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+10])===255)&&((11>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+11])===255)){return $subslice(l,12,16);}return EX.nil;};$ptrType(EX).prototype.To4=function(){return this.$get().To4();};EX.prototype.To16=function(){var l;l=this;if(l.$length===4){return FA((0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0]),(1>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+1]),(2>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+2]),(3>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+3]));}if(l.$length===16){return l;}return EX.nil;};$ptrType(EX).prototype.To16=function(){return this.$get().To16();};EX.prototype.DefaultMask=function(){var l;l=this;l=l.To4();if(l===EX.nil){return EY.nil;}if((0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])<128){return FF;}else if((0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])<192){return FG;}else{return FH;}};$ptrType(EX).prototype.DefaultMask=function(){return this.$get().DefaultMask();};FI=function(l){var l,m,n,o;m=l;n=0;while(true){if(!(n=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+n]);if(!((o===255))){return false;}n++;}return true;};EX.prototype.Mask=function(l){var l,m,n,o,p,q,r;m=this;if((l.$length===16)&&(m.$length===4)&&FI((n=$subslice(l,0,12),$subslice(new NM(n.$array),n.$offset,n.$offset+n.$length)))){l=$subslice(l,12);}if((l.$length===4)&&(m.$length===16)&&F.Equal((o=$subslice(m,0,12),$subslice(new NM(o.$array),o.$offset,o.$offset+o.$length)),FB)){m=$subslice(m,12);}p=m.$length;if(!((p===l.$length))){return EX.nil;}q=$makeSlice(EX,p);r=0;while(true){if(!(r=q.$length)?($throwRuntimeError("index out of range"),undefined):q.$array[q.$offset+r]=((((r<0||r>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+r])&((r<0||r>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+r]))>>>0));r=r+(1)>>0;}return q;};$ptrType(EX).prototype.Mask=function(l){return this.$get().Mask(l);};FJ=function(l,m,n){var l,m,n,o,p,q,r,s,t,u,v,w;if(n<10){((m<0||m>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+m]=(n+48<<24>>>24));return 1;}else if(n<100){(p=m+1>>0,((p<0||p>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+p]=((o=n%10,o===o?o:$throwRuntimeError("integer divide by zero"))+48<<24>>>24)));((m<0||m>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+m]=((q=n/10,(q===q&&q!==1/0&&q!==-1/0)?q>>>0:$throwRuntimeError("integer divide by zero"))+48<<24>>>24));return 2;}(s=m+2>>0,((s<0||s>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+s]=((r=n%10,r===r?r:$throwRuntimeError("integer divide by zero"))+48<<24>>>24)));(v=m+1>>0,((v<0||v>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+v]=((t=((u=n/10,(u===u&&u!==1/0&&u!==-1/0)?u>>>0:$throwRuntimeError("integer divide by zero")))%10,t===t?t:$throwRuntimeError("integer divide by zero"))+48<<24>>>24)));((m<0||m>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+m]=((w=n/100,(w===w&&w!==1/0&&w!==-1/0)?w>>>0:$throwRuntimeError("integer divide by zero"))+48<<24>>>24));return 3;};EX.prototype.String=function(){var l,m,n,o,p,q,r,s,t,u,v,w,x;l=this;m=l;if(l.$length===0){return"";}n=m.To4();if(n.$length===4){o=$makeSlice(NM,15);p=FJ(o,0,(0>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+0]));((p<0||p>=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]=46);p=p+(1)>>0;p=p+(FJ(o,p,(1>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+1])))>>0;((p<0||p>=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]=46);p=p+(1)>>0;p=p+(FJ(o,p,(2>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+2])))>>0;((p<0||p>=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]=46);p=p+(1)>>0;p=p+(FJ(o,p,(3>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+3])))>>0;return($bytesToString($subslice(o,0,p)));}if(!((m.$length===16))){return"?"+FK($subslice(new NM(l.$array),l.$offset,l.$offset+l.$length));}q=-1;r=-1;s=0;while(true){if(!(s<16)){break;}t=s;while(true){if(!(t<16&&(((t<0||t>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+t])===0)&&((u=t+1>>0,((u<0||u>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+u]))===0))){break;}t=t+(2)>>0;}if(t>s&&(t-s>>0)>(r-q>>0)){q=s;r=t;s=t;}s=s+(2)>>0;}if((r-q>>0)<=2){q=-1;r=-1;}v=$makeSlice(NM,0,39);w=0;while(true){if(!(w<16)){break;}if(w===q){v=$append(v,58,58);w=r;if(w>=16){break;}}else if(w>0){v=$append(v,58);}v=JU(v,(((((((w<0||w>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+w])>>>0))<<8>>>0))|(((x=w+1>>0,((x<0||x>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+x]))>>>0)))>>>0);w=w+(2)>>0;}return($bytesToString(v));};$ptrType(EX).prototype.String=function(){return this.$get().String();};FK=function(l){var l,m,n,o,p,q,r,s,t,u;m=$makeSlice(NM,($imul(l.$length,2)));n=l;o=0;while(true){if(!(o=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]);r="0123456789abcdef".charCodeAt((q>>>4<<24>>>24));s="0123456789abcdef".charCodeAt(((q&15)>>>0));(t=$imul(p,2),((t<0||t>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+t]=r));(u=($imul(p,2))+1>>0,((u<0||u>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+u]=s));o++;}return($bytesToString(m));};FL=function(l){var l;if(l.$length===0){return"";}return l.String();};EX.prototype.MarshalText=function(){var l;l=this;if(l.$length===0){return[(new NM($stringToBytes(""))),$ifaceNil];}if(!((l.$length===4))&&!((l.$length===16))){return[NM.nil,new IP.ptr("invalid IP address",FK($subslice(new NM(l.$array),l.$offset,l.$offset+l.$length)))];}return[(new NM($stringToBytes(l.String()))),$ifaceNil];};$ptrType(EX).prototype.MarshalText=function(){return this.$get().MarshalText();};$ptrType(EX).prototype.UnmarshalText=function(l){var l,m,n,o;m=this;if(l.$length===0){m.$set(EX.nil);return $ifaceNil;}n=($bytesToString(l));o=FR(n);if(o===EX.nil){return new IO.ptr("IP address",n);}m.$set(o);return $ifaceNil;};EX.prototype.Equal=function(l){var l,m,n,o,p,q;m=this;if(m.$length===l.$length){return F.Equal($subslice(new NM(m.$array),m.$offset,m.$offset+m.$length),$subslice(new NM(l.$array),l.$offset,l.$offset+l.$length));}if((m.$length===4)&&(l.$length===16)){return F.Equal((n=$subslice(l,0,12),$subslice(new NM(n.$array),n.$offset,n.$offset+n.$length)),FB)&&F.Equal($subslice(new NM(m.$array),m.$offset,m.$offset+m.$length),(o=$subslice(l,12),$subslice(new NM(o.$array),o.$offset,o.$offset+o.$length)));}if((m.$length===16)&&(l.$length===4)){return F.Equal((p=$subslice(m,0,12),$subslice(new NM(p.$array),p.$offset,p.$offset+p.$length)),FB)&&F.Equal((q=$subslice(m,12),$subslice(new NM(q.$array),q.$offset,q.$offset+q.$length)),$subslice(new NM(l.$array),l.$offset,l.$offset+l.$length));}return false;};$ptrType(EX).prototype.Equal=function(l){return this.$get().Equal(l);};FM=function(l){var l,m,n,o,p,q,r;m=0;n=l;o=0;while(true){if(!(o=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]);if(q===255){m=m+(8)>>0;o++;continue;}while(true){if(!(!((((q&128)>>>0)===0)))){break;}m=m+(1)>>0;q=(r=(1),r<32?(q<>>24;}if(!((q===0))){return-1;}p=p+(1)>>0;while(true){if(!(p=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+p])===0))){return-1;}p=p+(1)>>0;}break;}return m;};EY.prototype.Size=function(){var l,m,n,o,p,q,r;l=0;m=0;n=this;o=FM(n);p=$imul(n.$length,8);l=o;m=p;if(l===-1){q=0;r=0;l=q;m=r;return[l,m];}return[l,m];};$ptrType(EY).prototype.Size=function(){return this.$get().Size();};EY.prototype.String=function(){var l;l=this;if(l.$length===0){return"";}return FK($subslice(new NM(l.$array),l.$offset,l.$offset+l.$length));};$ptrType(EY).prototype.String=function(){return this.$get().String();};FN=function(l){var l,m,n,o,p,q,r,s,t,u;m=EX.nil;n=EY.nil;m=l.IP.To4();if(m===EX.nil){m=l.IP;if(!((m.$length===16))){o=EX.nil;p=EY.nil;m=o;n=p;return[m,n];}}n=l.Mask;q=n.$length;if(q===(4)){if(!((m.$length===4))){r=EX.nil;s=EY.nil;m=r;n=s;return[m,n];}}else if(q===(16)){if(m.$length===4){n=$subslice(n,12);}}else{t=EX.nil;u=EY.nil;m=t;n=u;return[m,n];}return[m,n];};EZ.ptr.prototype.Contains=function(l){var l,m,n,o,p,q,r,s;m=this;n=FN(m);o=n[0];p=n[1];q=l.To4();if(!(q===EX.nil)){l=q;}r=l.$length;if(!((r===o.$length))){return false;}s=0;while(true){if(!(s=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+s])&((s<0||s>=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+s]))>>>0)===((((s<0||s>=l.$length)?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+s])&((s<0||s>=p.$length)?($throwRuntimeError("index out of range"),undefined):p.$array[p.$offset+s]))>>>0)))){return false;}s=s+(1)>>0;}return true;};EZ.prototype.Contains=function(l){return this.$val.Contains(l);};EZ.ptr.prototype.Network=function(){var l;l=this;return"ip+net";};EZ.prototype.Network=function(){return this.$val.Network();};EZ.ptr.prototype.String=function(){var l,m,n,o,p;l=this;m=FN(l);n=m[0];o=m[1];if(n===EX.nil||o===EY.nil){return"";}p=FM(o);if(p===-1){return n.String()+"/"+o.String();}return n.String()+"/"+JT(((p>>>0)));};EZ.prototype.String=function(){return this.$val.String();};FO=function(l){var l,m,n,o,p,q,r;m=OW.zero();n=0;while(true){if(!(n<4)){break;}if(l.length===0){return EX.nil;}if(n>0){if(!((l.charCodeAt(0)===46))){return EX.nil;}l=$substring(l,1);}o=JP(l);p=o[0];q=o[1];r=o[2];if(!r||p>255){return EX.nil;}l=$substring(l,q);((n<0||n>=m.length)?($throwRuntimeError("index out of range"),undefined):m[n]=((p<<24>>>24)));n=n+(1)>>0;}if(!((l.length===0))){return EX.nil;}return FA(m[0],m[1],m[2],m[3]);};FQ=function(l){var aa,ab,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;m=EX.nil;m=$makeSlice(EX,16);n=-1;if(l.length>=2&&(l.charCodeAt(0)===58)&&(l.charCodeAt(1)===58)){n=0;l=$substring(l,2);if(l.length===0){m=m;return m;}}o=0;while(true){if(!(o<16)){break;}p=JQ(l);q=p[0];r=p[1];s=p[2];if(!s||q>65535){m=EX.nil;return m;}if(r>0)>16){m=EX.nil;return m;}t=FO(l);if(t===EX.nil){m=EX.nil;return m;}((o<0||o>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+o]=(12>=t.$length?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+12]));(u=o+1>>0,((u<0||u>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+u]=(13>=t.$length?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+13])));(v=o+2>>0,((v<0||v>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+v]=(14>=t.$length?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+14])));(w=o+3>>0,((w<0||w>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+w]=(15>=t.$length?($throwRuntimeError("index out of range"),undefined):t.$array[t.$offset+15])));l="";o=o+(4)>>0;break;}((o<0||o>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+o]=(((q>>8>>0)<<24>>>24)));(x=o+1>>0,((x<0||x>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+x]=((q<<24>>>24))));o=o+(2)>>0;l=$substring(l,r);if(l.length===0){break;}if(!((l.charCodeAt(0)===58))||(l.length===1)){m=EX.nil;return m;}l=$substring(l,1);if(l.charCodeAt(0)===58){if(n>=0){m=EX.nil;return m;}n=o;l=$substring(l,1);if(l.length===0){break;}}}if(!((l.length===0))){m=EX.nil;return m;}if(o<16){if(n<0){m=EX.nil;return m;}y=16-o>>0;z=o-1>>0;while(true){if(!(z>=n)){break;}(aa=z+y>>0,((aa<0||aa>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+aa]=((z<0||z>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+z])));z=z-(1)>>0;}ab=(n+y>>0)-1>>0;while(true){if(!(ab>=n)){break;}((ab<0||ab>=m.$length)?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+ab]=0);ab=ab-(1)>>0;}}else if(n>=0){m=EX.nil;return m;}m=m;return m;};FR=function(l){var l,m,n;m=0;while(true){if(!(m>0;}return EX.nil;};$pkg.ParseIP=FR;FT=function(l){var l,m,n,o,p,q,r,s,t,u,v,w;m=F.IndexByteString(l,47);if(m<0){return[EX.nil,NP.nil,new IO.ptr("CIDR address",l)];}n=$substring(l,0,m);o=$substring(l,(m+1>>0));p=n;q=o;r=4;s=FO(p);if(s===EX.nil){r=16;s=FQ(p);}t=JP(q);u=t[0];m=t[1];v=t[2];if(s===EX.nil||!v||!((m===q.length))||u<0||u>($imul(8,r))){return[EX.nil,NP.nil,new IO.ptr("CIDR address",l)];}w=FD(u,$imul(8,r));return[s,new EZ.ptr(s.Mask(w),w),$ifaceNil];};$pkg.ParseCIDR=FT;FU.ptr.prototype.Network=function(){var l;l=this;return"ip";};FU.prototype.Network=function(){return this.$val.Network();};FU.ptr.prototype.String=function(){var l,m;l=this;if(l===NX.nil){return"";}m=FL(l.IP);if(!(l.Zone==="")){return m+"%"+l.Zone;}return m;};FU.prototype.String=function(){return this.$val.String();};HS.prototype.String=function(){var l,m,n,o,p,q;l=this;if(l.$length===0){return"";}m=$makeSlice(NM,0,(($imul(l.$length,3))-1>>0));n=l;o=0;while(true){if(!(o=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]);if(p>0){m=$append(m,58);}m=$append(m,"0123456789abcdef".charCodeAt((q>>>4<<24>>>24)));m=$append(m,"0123456789abcdef".charCodeAt(((q&15)>>>0)));o++;}return($bytesToString(m));};$ptrType(HS).prototype.String=function(){return this.$get().String();};II.ptr.prototype.Error=function(){var l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(l===OE.nil){$s=-1;return"";}m=l.Op;if(!(l.Net==="")){m=m+(" "+l.Net);}if(!($interfaceIsEqual(l.Source,$ifaceNil))){$s=1;continue;}$s=2;continue;case 1:n=l.Source.String();$s=3;case 3:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=m+(" "+n);case 2:if(!($interfaceIsEqual(l.Addr,$ifaceNil))){$s=4;continue;}$s=5;continue;case 4:if(!($interfaceIsEqual(l.Source,$ifaceNil))){m=m+("->");}else{m=m+(" ");}o=l.Addr.String();$s=6;case 6:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}m=m+(o);case 5:p=l.Err.Error();$s=7;case 7:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}m=m+(": "+p);$s=-1;return m;}return;}if($f===undefined){$f={$blk:II.ptr.prototype.Error};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};II.prototype.Error=function(){return this.$val.Error();};II.ptr.prototype.Timeout=function(){var l,m,n,o,p,q,r,s,t,u,v,w,x,y,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;m=$assertType(l.Err,PM,true);n=m[0];o=m[1];if(o){$s=1;continue;}$s=2;continue;case 1:p=$assertType(n.Err,IM,true);q=p[0];r=p[1];if(!(r)){s=false;$s=3;continue s;}t=q.Timeout();$s=4;case 4:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}s=t;case 3:$s=-1;return s;case 2:u=$assertType(l.Err,IM,true);v=u[0];w=u[1];if(!(w)){x=false;$s=5;continue s;}y=v.Timeout();$s=6;case 6:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}x=y;case 5:$s=-1;return x;}return;}if($f===undefined){$f={$blk:II.ptr.prototype.Timeout};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.$s=$s;$f.$r=$r;return $f;};II.prototype.Timeout=function(){return this.$val.Timeout();};II.ptr.prototype.Temporary=function(){var l,m,n,o,p,q,r,s,t,u,v,w,x,y,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;if(l.Op==="accept"&&CY(l.Err)){$s=-1;return true;}m=$assertType(l.Err,PM,true);n=m[0];o=m[1];if(o){$s=1;continue;}$s=2;continue;case 1:p=$assertType(n.Err,IN,true);q=p[0];r=p[1];if(!(r)){s=false;$s=3;continue s;}t=q.Temporary();$s=4;case 4:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}s=t;case 3:$s=-1;return s;case 2:u=$assertType(l.Err,IN,true);v=u[0];w=u[1];if(!(w)){x=false;$s=5;continue s;}y=v.Temporary();$s=6;case 6:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}x=y;case 5:$s=-1;return x;}return;}if($f===undefined){$f={$blk:II.ptr.prototype.Temporary};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.$s=$s;$f.$r=$r;return $f;};II.prototype.Temporary=function(){return this.$val.Temporary();};IO.ptr.prototype.Error=function(){var l;l=this;return"invalid "+l.Type+": "+l.Text;};IO.prototype.Error=function(){return this.$val.Error();};IP.ptr.prototype.Error=function(){var l,m;l=this;if(l===PN.nil){return"";}m=l.Err;if(!(l.Addr==="")){m="address "+l.Addr+": "+m;}return m;};IP.prototype.Error=function(){return this.$val.Error();};IP.ptr.prototype.Timeout=function(){var l;l=this;return false;};IP.prototype.Timeout=function(){return this.$val.Timeout();};IP.ptr.prototype.Temporary=function(){var l;l=this;return false;};IP.prototype.Temporary=function(){return this.$val.Temporary();};JJ.ptr.prototype.close=function(){var l,m,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l=this;m=l.file.Close();$s=1;case 1:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}m;$s=-1;return;}return;}if($f===undefined){$f={$blk:JJ.ptr.prototype.close};}$f.l=l;$f.m=m;$f.$s=$s;$f.$r=$r;return $f;};JJ.prototype.close=function(){return this.$val.close();};JJ.ptr.prototype.getLineFromData=function(){var l,m,n,o,p,q;l="";m=false;n=this;o=n.data;p=0;p=0;while(true){if(!(p=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p])===10){l=($bytesToString($subslice(o,0,p)));m=true;p=p+(1)>>0;q=o.$length-p>>0;$copySlice($subslice(o,0),$subslice(o,p));n.data=$subslice(o,0,q);return[l,m];}p=p+(1)>>0;}if(n.atEOF&&n.data.$length>0){l=($bytesToString(o));n.data=$subslice(n.data,0,0);m=true;}return[l,m];};JJ.prototype.getLineFromData=function(){return this.$val.getLineFromData();};JJ.ptr.prototype.readLine=function(){var l,m,n,o,p,q,r,s,t,u,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:l="";m=false;n=this;o=n.getLineFromData();l=o[0];m=o[1];if(m){$s=-1;return[l,m];}if(n.data.$length=0){n.data=$subslice(n.data,0,(p+s>>0));}if($interfaceIsEqual(t,O.EOF)||$interfaceIsEqual(t,O.ErrUnexpectedEOF)){n.atEOF=true;}case 2:u=n.getLineFromData();l=u[0];m=u[1];$s=-1;return[l,m];}return;}if($f===undefined){$f={$blk:JJ.ptr.prototype.readLine};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.$s=$s;$f.$r=$r;return $f;};JJ.prototype.readLine=function(){return this.$val.readLine();};JK=function(l){var l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:n=G.Open(l);$s=1;case 1:if($c){$c=false;n=n.$blk();}if(n&&n.$blk!==undefined){break s;}m=n;o=m[0];p=m[1];if(!($interfaceIsEqual(p,$ifaceNil))){$s=-1;return[OQ.nil,p];}$s=-1;return[new JJ.ptr(o,$makeSlice(NM,0,65536),false),$ifaceNil];}return;}if($f===undefined){$f={$blk:JK};}$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};JM=function(l,m){var l,m,n,o;n=0;o=0;while(true){if(!(o=0){n=n+(1)>>0;}o=o+(1)>>0;}return n;};JN=function(l,m){var l,m,n,o,p,q;n=$makeSlice(NG,(1+JM(l,m)>>0));o=0;p=0;q=0;while(true){if(!(q=0){if(p=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]=$substring(l,p,q));o=o+(1)>>0;}p=q+1>>0;}q=q+(1)>>0;}if(p=n.$length)?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+o]=$substring(l,p));o=o+(1)>>0;}return $subslice(n,0,o);};JP=function(l){var l,m,n,o,p,q,r,s,t,u,v,w,x;m=0;n=0;o=false;m=0;n=0;while(true){if(!(n>>24)>>0))>>0;if(m>=16777215){p=16777215;q=n;r=false;m=p;n=q;o=r;return[m,n,o];}n=n+(1)>>0;}if(n===0){s=0;t=0;u=false;m=s;n=t;o=u;return[m,n,o];}v=m;w=n;x=true;m=v;n=w;o=x;return[m,n,o];};JQ=function(l){var l,m,n,o,p,q,r,s,t,u,v,w,x;m=0;n=0;o=false;m=0;n=0;while(true){if(!(n>>24)>>0)))>>0;}else if(97<=l.charCodeAt(n)&&l.charCodeAt(n)<=102){m=$imul(m,(16));m=m+(((((l.charCodeAt(n)-97<<24>>>24)>>0))+10>>0))>>0;}else if(65<=l.charCodeAt(n)&&l.charCodeAt(n)<=70){m=$imul(m,(16));m=m+(((((l.charCodeAt(n)-65<<24>>>24)>>0))+10>>0))>>0;}else{break;}if(m>=16777215){p=0;q=n;r=false;m=p;n=q;o=r;return[m,n,o];}n=n+(1)>>0;}if(n===0){s=0;t=n;u=false;m=s;n=t;o=u;return[m,n,o];}v=m;w=n;x=true;m=v;n=w;o=x;return[m,n,o];};JR=function(l,m){var l,m,n,o,p,q;if(l.length>2&&!((l.charCodeAt(2)===m))){return[0,false];}n=JQ($substring(l,0,2));o=n[0];p=n[1];q=n[2];return[((o<<24>>>24)),q&&(p===2)];};JT=function(l){var l,m,n,o,p;if(l===0){return"0";}m=PP.zero();n=19;while(true){if(!(l>=10)){break;}p=(o=l/10,(o===o&&o!==1/0&&o!==-1/0)?o>>>0:$throwRuntimeError("integer divide by zero"));((n<0||n>=m.length)?($throwRuntimeError("index out of range"),undefined):m[n]=((((48+l>>>0)-(p*10>>>0)>>>0)<<24>>>24)));n=n-(1)>>0;l=p;}((n<0||n>=m.length)?($throwRuntimeError("index out of range"),undefined):m[n]=(((48+l>>>0)<<24>>>24)));return($bytesToString($subslice(new NM(m),n)));};JU=function(l,m){var l,m,n,o,p;if(m===0){return $append(l,48);}n=7;while(true){if(!(n>=0)){break;}p=(o=((($imul(n,4))>>>0)),o<32?(m>>>o):0)>>>0;if(p>0){l=$append(l,"0123456789abcdef".charCodeAt(((p&15)>>>0)));}n=n-(1)>>0;}return l;};AH.methods=[{prop:"Classify",name:"Classify",pkg:"",typ:$funcType([EX],[AG],false)}];AK.methods=[{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Swap",name:"Swap",pkg:"",typ:$funcType([$Int,$Int],[],false)},{prop:"Less",name:"Less",pkg:"",typ:$funcType([$Int,$Int],[$Bool],false)}];OR.methods=[{prop:"Addrs",name:"Addrs",pkg:"",typ:$funcType([],[OS,$error],false)},{prop:"MulticastAddrs",name:"MulticastAddrs",pkg:"",typ:$funcType([],[OS,$error],false)}];EF.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];QL.methods=[{prop:"update",name:"update",pkg:"net",typ:$funcType([OT,$Bool],[$Bool],false)},{prop:"name",name:"name",pkg:"net",typ:$funcType([$Int],[$String],false)},{prop:"index",name:"index",pkg:"net",typ:$funcType([$String],[$Int],false)}];EX.methods=[{prop:"IsUnspecified",name:"IsUnspecified",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"IsLoopback",name:"IsLoopback",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"IsMulticast",name:"IsMulticast",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"IsInterfaceLocalMulticast",name:"IsInterfaceLocalMulticast",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"IsLinkLocalMulticast",name:"IsLinkLocalMulticast",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"IsLinkLocalUnicast",name:"IsLinkLocalUnicast",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"IsGlobalUnicast",name:"IsGlobalUnicast",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"To4",name:"To4",pkg:"",typ:$funcType([],[EX],false)},{prop:"To16",name:"To16",pkg:"",typ:$funcType([],[EX],false)},{prop:"DefaultMask",name:"DefaultMask",pkg:"",typ:$funcType([],[EY],false)},{prop:"Mask",name:"Mask",pkg:"",typ:$funcType([EY],[EX],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"MarshalText",name:"MarshalText",pkg:"",typ:$funcType([],[NM,$error],false)},{prop:"Equal",name:"Equal",pkg:"",typ:$funcType([EX],[$Bool],false)},{prop:"matchAddrFamily",name:"matchAddrFamily",pkg:"net",typ:$funcType([EX],[$Bool],false)}];QO.methods=[{prop:"UnmarshalText",name:"UnmarshalText",pkg:"",typ:$funcType([NM],[$error],false)}];EY.methods=[{prop:"Size",name:"Size",pkg:"",typ:$funcType([],[$Int,$Int],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];NP.methods=[{prop:"Contains",name:"Contains",pkg:"",typ:$funcType([EX],[$Bool],false)},{prop:"Network",name:"Network",pkg:"",typ:$funcType([],[$String],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];NX.methods=[{prop:"Network",name:"Network",pkg:"",typ:$funcType([],[$String],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"isWildcard",name:"isWildcard",pkg:"net",typ:$funcType([],[$Bool],false)},{prop:"opAddr",name:"opAddr",pkg:"net",typ:$funcType([],[HW],false)},{prop:"family",name:"family",pkg:"net",typ:$funcType([],[$Int],false)},{prop:"sockaddr",name:"sockaddr",pkg:"net",typ:$funcType([$Int],[B.Sockaddr,$error],false)},{prop:"toLocal",name:"toLocal",pkg:"net",typ:$funcType([$String],[LC],false)}];HS.methods=[{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];OE.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Temporary",name:"Temporary",pkg:"",typ:$funcType([],[$Bool],false)}];QT.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];PN.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Temporary",name:"Temporary",pkg:"",typ:$funcType([],[$Bool],false)}];OQ.methods=[{prop:"close",name:"close",pkg:"net",typ:$funcType([],[],false)},{prop:"getLineFromData",name:"getLineFromData",pkg:"net",typ:$funcType([],[$String,$Bool],false)},{prop:"readLine",name:"readLine",pkg:"net",typ:$funcType([],[$String,$Bool],false)}];AG.init("",[{prop:"Prefix",name:"Prefix",embedded:false,exported:true,typ:NP,tag:""},{prop:"Precedence",name:"Precedence",embedded:false,exported:true,typ:$Uint8,tag:""},{prop:"Label",name:"Label",embedded:false,exported:true,typ:$Uint8,tag:""}]);AH.init(AG);AK.init(AG);EE.init("",[{prop:"Index",name:"Index",embedded:false,exported:true,typ:$Int,tag:""},{prop:"MTU",name:"MTU",embedded:false,exported:true,typ:$Int,tag:""},{prop:"Name",name:"Name",embedded:false,exported:true,typ:$String,tag:""},{prop:"HardwareAddr",name:"HardwareAddr",embedded:false,exported:true,typ:HS,tag:""},{prop:"Flags",name:"Flags",embedded:false,exported:true,typ:EF,tag:""}]);EM.init("net",[{prop:"RWMutex",name:"RWMutex",embedded:true,exported:true,typ:I.RWMutex,tag:""},{prop:"lastFetched",name:"lastFetched",embedded:false,exported:false,typ:L.Time,tag:""},{prop:"toIndex",name:"toIndex",embedded:false,exported:false,typ:QM,tag:""},{prop:"toName",name:"toName",embedded:false,exported:false,typ:QN,tag:""}]);EX.init($Uint8);EY.init($Uint8);EZ.init("",[{prop:"IP",name:"IP",embedded:false,exported:true,typ:EX,tag:""},{prop:"Mask",name:"Mask",embedded:false,exported:true,typ:EY,tag:""}]);FU.init("",[{prop:"IP",name:"IP",embedded:false,exported:true,typ:EX,tag:""},{prop:"Zone",name:"Zone",embedded:false,exported:true,typ:$String,tag:""}]);HS.init($Uint8);HW.init([{prop:"Network",name:"Network",pkg:"",typ:$funcType([],[$String],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}]);II.init("",[{prop:"Op",name:"Op",embedded:false,exported:true,typ:$String,tag:""},{prop:"Net",name:"Net",embedded:false,exported:true,typ:$String,tag:""},{prop:"Source",name:"Source",embedded:false,exported:true,typ:HW,tag:""},{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:HW,tag:""},{prop:"Err",name:"Err",embedded:false,exported:true,typ:$error,tag:""}]);IM.init([{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)}]);IN.init([{prop:"Temporary",name:"Temporary",pkg:"",typ:$funcType([],[$Bool],false)}]);IO.init("",[{prop:"Type",name:"Type",embedded:false,exported:true,typ:$String,tag:""},{prop:"Text",name:"Text",embedded:false,exported:true,typ:$String,tag:""}]);IP.init("",[{prop:"Err",name:"Err",embedded:false,exported:true,typ:$String,tag:""},{prop:"Addr",name:"Addr",embedded:false,exported:true,typ:$String,tag:""}]);JJ.init("net",[{prop:"file",name:"file",embedded:false,exported:false,typ:OM,tag:""},{prop:"data",name:"data",embedded:false,exported:false,typ:NM,tag:""},{prop:"atEOF",name:"atEOF",embedded:false,exported:false,typ:$Bool,tag:""}]);LC.init([{prop:"Network",name:"Network",pkg:"",typ:$funcType([],[$String],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"family",name:"family",pkg:"net",typ:$funcType([],[$Int],false)},{prop:"isWildcard",name:"isWildcard",pkg:"net",typ:$funcType([],[$Bool],false)},{prop:"sockaddr",name:"sockaddr",pkg:"net",typ:$funcType([$Int],[B.Sockaddr,$error],false)},{prop:"toLocal",name:"toLocal",pkg:"net",typ:$funcType([$String],[LC],false)}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=E.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=J.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=K.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=Q.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=N.$init();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=O.$init();$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=M.$init();$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=G.$init();$s=11;case 11:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=H.$init();$s=12;case 12:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=13;case 13:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=I.$init();$s=14;case 14:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=P.$init();$s=15;case 15:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=16;case 16:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=L.$init();$s=17;case 17:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}HU=false;BX=A.New("lame referral");BY=A.New("cannot unmarshal DNS message");BZ=A.New("cannot marshal DNS message");CA=A.New("server misbehaving");CB=A.New("invalid DNS response");CC=A.New("no answer from DNS server");CD=A.New("server misbehaving");DM=(function $b(c,d,e,f){var c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:g=d(c,e,f);$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}$s=-1;return g;}return;}if($f===undefined){$f={$blk:$b};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;});DZ=A.New("invalid network interface");EA=A.New("invalid network interface index");EB=A.New("invalid network interface name");EC=A.New("no such network interface");ED=A.New("no such multicast network interface");EG=new NG(["up","broadcast","loopback","pointtopoint","multicast"]);EN=new EM.ptr(new I.RWMutex.ptr(new I.Mutex.ptr(0,0),0,0,0,0),new L.Time.ptr(new $Uint64(0,0),new $Int64(0,0),NE.nil),{},{});FB=new NM([0,0,0,0,0,0,0,0,0,0,255,255]);$pkg.IPv4bcast=FA(255,255,255,255);$pkg.IPv4allsys=FA(224,0,0,1);$pkg.IPv4allrouter=FA(224,0,0,2);$pkg.IPv4zero=FA(0,0,0,0);$pkg.IPv6unspecified=new EX([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);$pkg.IPv6loopback=new EX([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]);FF=FC(255,0,0,0);FG=FC(255,255,0,0);FH=FC(255,255,255,0);IE=A.New("no suitable address found");IF=A.New("missing address");IG=A.New("operation was canceled");$pkg.ErrWriteToConnected=A.New("use of WriteTo with pre-connected connection");IJ=$clone(L.Unix(new $Int64(0,1),new $Int64(0,0)),L.Time);IT=A.New("no such host");c=AL("::1/128");$s=18;case 18:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=AL("::/0");$s=19;case 19:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=AL("::ffff:0:0/96");$s=20;case 20:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}f=AL("2002::/16");$s=21;case 21:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}g=AL("2001::/32");$s=22;case 22:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=AL("fc00::/7");$s=23;case 23:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}i=AL("::/96");$s=24;case 24:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}j=AL("fec0::/10");$s=25;case 25:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}k=AL("3ffe::/16");$s=26;case 26:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}AI=new AH([new AG.ptr(c,50,0),new AG.ptr(d,40,1),new AG.ptr(e,35,4),new AG.ptr(f,30,2),new AG.ptr(g,5,5),new AG.ptr(h,3,13),new AG.ptr(i,1,3),new AG.ptr(j,1,11),new AG.ptr(k,1,12)]);$r=AJ();$s=27;case 27:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}AP();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["net/url"]=(function(){var $pkg={},$init,A,B,C,D,E,F,G,H,L,M,U,X,AH,AO,AP,AQ,AR,AS,AT,AU,AV,I,J,N,O,Q,R,T,V,W,Y,Z,AA,AC,AD,AE,AF,AG,AI,AJ,AK,AL,AM,AN;A=$packages["errors"];B=$packages["fmt"];C=$packages["sort"];D=$packages["strconv"];E=$packages["strings"];F=$pkg.Error=$newType(0,$kindStruct,"url.Error",true,"net/url",true,function(Op_,URL_,Err_){this.$val=this;if(arguments.length===0){this.Op="";this.URL="";this.Err=$ifaceNil;return;}this.Op=Op_;this.URL=URL_;this.Err=Err_;});G=$pkg.timeout=$newType(8,$kindInterface,"url.timeout",true,"net/url",false,null);H=$pkg.temporary=$newType(8,$kindInterface,"url.temporary",true,"net/url",false,null);L=$pkg.EscapeError=$newType(8,$kindString,"url.EscapeError",true,"net/url",true,null);M=$pkg.InvalidHostError=$newType(8,$kindString,"url.InvalidHostError",true,"net/url",true,null);U=$pkg.URL=$newType(0,$kindStruct,"url.URL",true,"net/url",true,function(Scheme_,Opaque_,User_,Host_,Path_,RawPath_,ForceQuery_,RawQuery_,Fragment_){this.$val=this;if(arguments.length===0){this.Scheme="";this.Opaque="";this.User=AQ.nil;this.Host="";this.Path="";this.RawPath="";this.ForceQuery=false;this.RawQuery="";this.Fragment="";return;}this.Scheme=Scheme_;this.Opaque=Opaque_;this.User=User_;this.Host=Host_;this.Path=Path_;this.RawPath=RawPath_;this.ForceQuery=ForceQuery_;this.RawQuery=RawQuery_;this.Fragment=Fragment_;});X=$pkg.Userinfo=$newType(0,$kindStruct,"url.Userinfo",true,"net/url",true,function(username_,password_,passwordSet_){this.$val=this;if(arguments.length===0){this.username="";this.password="";this.passwordSet=false;return;}this.username=username_;this.password=password_;this.passwordSet=passwordSet_;});AH=$pkg.Values=$newType(4,$kindMap,"url.Values",true,"net/url",true,null);AO=$sliceType($Uint8);AP=$arrayType($Uint8,64);AQ=$ptrType(X);AR=$ptrType(U);AS=$sliceType($emptyInterface);AT=$ptrType(E.Builder);AU=$sliceType($String);AV=$ptrType(F);F.ptr.prototype.Error=function(){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.Err.Error();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return a.Op+" "+a.URL+": "+b;}return;}if($f===undefined){$f={$blk:F.ptr.prototype.Error};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};F.prototype.Error=function(){return this.$val.Error();};F.ptr.prototype.Timeout=function(){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=$assertType(a.Err,G,true);c=b[0];d=b[1];if(!(d)){e=false;$s=1;continue s;}f=c.Timeout();$s=2;case 2:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;case 1:$s=-1;return e;}return;}if($f===undefined){$f={$blk:F.ptr.prototype.Timeout};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};F.prototype.Timeout=function(){return this.$val.Timeout();};F.ptr.prototype.Temporary=function(){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=$assertType(a.Err,H,true);c=b[0];d=b[1];if(!(d)){e=false;$s=1;continue s;}f=c.Temporary();$s=2;case 2:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;case 1:$s=-1;return e;}return;}if($f===undefined){$f={$blk:F.ptr.prototype.Temporary};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};F.prototype.Temporary=function(){return this.$val.Temporary();};I=function(a){var a;if(48<=a&&a<=57){return true;}else if(97<=a&&a<=102){return true;}else if(65<=a&&a<=70){return true;}return false;};J=function(a){var a;if(48<=a&&a<=57){return a-48<<24>>>24;}else if(97<=a&&a<=102){return(a-97<<24>>>24)+10<<24>>>24;}else if(65<=a&&a<=70){return(a-65<<24>>>24)+10<<24>>>24;}return 0;};L.prototype.Error=function(){var a;a=this.$val;return"invalid URL escape "+D.Quote((a));};$ptrType(L).prototype.Error=function(){return new L(this.$get()).Error();};M.prototype.Error=function(){var a;a=this.$val;return"invalid character "+D.Quote((a))+" in host name";};$ptrType(M).prototype.Error=function(){return new M(this.$get()).Error();};N=function(a,b){var a,b,c,d,e,f;if(65<=a&&a<=90||97<=a&&a<=122||48<=a&&a<=57){return false;}if((b===3)||(b===4)){c=a;if((c===(33))||(c===(36))||(c===(38))||(c===(39))||(c===(40))||(c===(41))||(c===(42))||(c===(43))||(c===(44))||(c===(59))||(c===(61))||(c===(58))||(c===(91))||(c===(93))||(c===(60))||(c===(62))||(c===(34))){return false;}}d=a;if((d===(45))||(d===(95))||(d===(46))||(d===(126))){return false;}else if((d===(36))||(d===(38))||(d===(43))||(d===(44))||(d===(47))||(d===(58))||(d===(59))||(d===(61))||(d===(63))||(d===(64))){e=b;if(e===(1)){return a===63;}else if(e===(2)){return(a===47)||(a===59)||(a===44)||(a===63);}else if(e===(5)){return(a===64)||(a===47)||(a===63)||(a===58);}else if(e===(6)){return true;}else if(e===(7)){return false;}}if(b===7){f=a;if((f===(33))||(f===(40))||(f===(41))||(f===(42))){return false;}}return true;};O=function(a){var a;return Q(a,6);};$pkg.QueryUnescape=O;Q=function(a,b){var a,b,c,d,e,f,g,h,i,j,k;c=0;d=false;e=0;while(true){if(!(e>0;if((e+2>>0)>=a.length||!I(a.charCodeAt((e+1>>0)))||!I(a.charCodeAt((e+2>>0)))){a=$substring(a,e);if(a.length>3){a=$substring(a,0,3);}return["",new L((a))];}if((b===3)&&J(a.charCodeAt((e+1>>0)))<8&&!($substring(a,e,(e+3>>0))==="%25")){return["",new L(($substring(a,e,(e+3>>0))))];}if(b===4){g=((J(a.charCodeAt((e+1>>0)))<<4<<24>>>24)|J(a.charCodeAt((e+2>>0))))>>>0;if(!($substring(a,e,(e+3>>0))==="%25")&&!((g===32))&&N(g,3)){return["",new L(($substring(a,e,(e+3>>0))))];}}e=e+(3)>>0;}else if(f===(43)){d=b===6;e=e+(1)>>0;}else{if(((b===3)||(b===4))&&a.charCodeAt(e)<128&&N(a.charCodeAt(e),b)){return["",new M(($substring(a,e,(e+1>>0))))];}e=e+(1)>>0;}}if((c===0)&&!d){return[a,$ifaceNil];}h=$makeSlice(AO,(a.length-($imul(2,c))>>0));i=0;j=0;while(true){if(!(j=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]=(((J(a.charCodeAt((j+1>>0)))<<4<<24>>>24)|J(a.charCodeAt((j+2>>0))))>>>0));i=i+(1)>>0;j=j+(3)>>0;}else if(k===(43)){if(b===6){((i<0||i>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]=32);}else{((i<0||i>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]=43);}i=i+(1)>>0;j=j+(1)>>0;}else{((i<0||i>=h.$length)?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+i]=a.charCodeAt(j));i=i+(1)>>0;j=j+(1)>>0;}}return[($bytesToString(h)),$ifaceNil];};R=function(a){var a;return T(a,6);};$pkg.QueryEscape=R;T=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;c=0;d=0;e=c;f=d;g=0;while(true){if(!(g>0;}else{f=f+(1)>>0;}}g=g+(1)>>0;}if((e===0)&&(f===0)){return a;}i=AP.zero();j=AO.nil;k=a.length+($imul(2,f))>>0;if(k<=64){j=$subslice(new AO(i),0,k);}else{j=$makeSlice(AO,k);}if(f===0){$copyString(j,a);l=0;while(true){if(!(l=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+l]=43);}l=l+(1)>>0;}return($bytesToString(j));}m=0;n=0;while(true){if(!(n=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+m]=43);m=m+(1)>>0;}else if(N(o,b)){((m<0||m>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+m]=37);(p=m+1>>0,((p<0||p>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+p]="0123456789ABCDEF".charCodeAt((o>>>4<<24>>>24))));(q=m+2>>0,((q<0||q>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+q]="0123456789ABCDEF".charCodeAt(((o&15)>>>0))));m=m+(3)>>0;}else{((m<0||m>=j.$length)?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+m]=a.charCodeAt(n));m=m+(1)>>0;}n=n+(1)>>0;}return($bytesToString(j));};V=function(a){var a;return new X.ptr(a,"",false);};$pkg.User=V;W=function(a,b){var a,b;return new X.ptr(a,b,true);};$pkg.UserPassword=W;X.ptr.prototype.Username=function(){var a;a=this;if(a===AQ.nil){return"";}return a.username;};X.prototype.Username=function(){return this.$val.Username();};X.ptr.prototype.Password=function(){var a;a=this;if(a===AQ.nil){return["",false];}return[a.password,a.passwordSet];};X.prototype.Password=function(){return this.$val.Password();};X.ptr.prototype.String=function(){var a,b;a=this;if(a===AQ.nil){return"";}b=T(a.username,5);if(a.passwordSet){b=b+(":"+T(a.password,5));}return b;};X.prototype.String=function(){return this.$val.String();};Y=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;b="";c="";d=$ifaceNil;e=0;while(true){if(!(e>0));o=$ifaceNil;b=m;c=n;d=o;return[b,c,d];}else{p="";q=a;r=$ifaceNil;b=p;c=q;d=r;return[b,c,d];}e=e+(1)>>0;}s="";t=a;u=$ifaceNil;b=s;c=t;d=u;return[b,c,d];};Z=function(a,b,c){var a,b,c,d;d=E.Index(a,b);if(d<0){return[a,""];}if(c){return[$substring(a,0,d),$substring(a,(d+b.length>>0))];}return[$substring(a,0,d),$substring(a,d)];};AA=function(a){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=Z(a,"#",true);c=b[0];d=b[1];f=AC(c,false);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;g=e[0];h=e[1];if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return[AR.nil,new F.ptr("parse",c,h)];}if(d===""){$s=-1;return[g,$ifaceNil];}i=Q(d,7);g.Fragment=i[0];h=i[1];if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return[AR.nil,new F.ptr("parse",a,h)];}$s=-1;return[g,$ifaceNil];}return;}if($f===undefined){$f={$blk:AA};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Parse=AA;AC=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c="";d=$ifaceNil;if(AN(a)){$s=-1;return[AR.nil,A.New("net/url: invalid control character in URL")];}if(a===""&&b){$s=-1;return[AR.nil,A.New("empty url")];}e=new U.ptr("","",AQ.nil,"","","",false,"","");if(a==="*"){e.Path="*";$s=-1;return[e,$ifaceNil];}f=Y(a);e.Scheme=f[0];c=f[1];d=f[2];if(!($interfaceIsEqual(d,$ifaceNil))){$s=-1;return[AR.nil,d];}g=E.ToLower(e.Scheme);$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}e.Scheme=g;if(E.HasSuffix(c,"?")&&(E.Count(c,"?")===1)){e.ForceQuery=true;c=$substring(c,0,(c.length-1>>0));}else{h=Z(c,"?",true);c=h[0];e.RawQuery=h[1];}if(!E.HasPrefix(c,"/")){if(!(e.Scheme==="")){e.Opaque=c;$s=-1;return[e,$ifaceNil];}if(b){$s=-1;return[AR.nil,A.New("invalid URI for request")];}i=E.Index(c,":");j=E.Index(c,"/");if(i>=0&&(j<0||i>0)));$s=5;case 5:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;c=h[0];d=h[1];case 3:if(!($interfaceIsEqual(d,$ifaceNil))){j=AQ.nil;k="";l=d;b=j;c=k;d=l;$s=-1;return[b,c,d];}if(e<0){m=AQ.nil;n=c;o=$ifaceNil;b=m;c=n;d=o;$s=-1;return[b,c,d];}p=$substring(a,0,e);if(!AM(p)){q=AQ.nil;r="";s=A.New("net/url: invalid userinfo");b=q;c=r;d=s;$s=-1;return[b,c,d];}if(!E.Contains(p,":")){t=Q(p,5);p=t[0];d=t[1];if(!($interfaceIsEqual(d,$ifaceNil))){u=AQ.nil;v="";w=d;b=u;c=v;d=w;$s=-1;return[b,c,d];}b=V(p);}else{x=Z(p,":",true);y=x[0];z=x[1];aa=Q(y,5);y=aa[0];d=aa[1];if(!($interfaceIsEqual(d,$ifaceNil))){ab=AQ.nil;ac="";ad=d;b=ab;c=ac;d=ad;$s=-1;return[b,c,d];}ae=Q(z,5);z=ae[0];d=ae[1];if(!($interfaceIsEqual(d,$ifaceNil))){af=AQ.nil;ag="";ah=d;b=af;c=ag;d=ah;$s=-1;return[b,c,d];}b=W(y,z);}ai=b;aj=c;ak=$ifaceNil;b=ai;c=aj;d=ak;$s=-1;return[b,c,d];}return;}if($f===undefined){$f={$blk:AD};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};AE=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(E.HasPrefix(a,"[")){$s=1;continue;}$s=2;continue;case 1:b=E.LastIndex(a,"]");if(b<0){$s=-1;return["",A.New("missing ']' in host")];}c=$substring(a,(b+1>>0));if(!AG(c)){$s=4;continue;}$s=5;continue;case 4:d=B.Errorf("invalid port %q after host",new AS([new $String(c)]));$s=6;case 6:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}$s=-1;return["",d];case 5:e=E.Index($substring(a,0,b),"%25");if(e>=0){f=Q($substring(a,0,e),3);g=f[0];h=f[1];if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return["",h];}i=Q($substring(a,e,b),4);j=i[0];h=i[1];if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return["",h];}k=Q($substring(a,b),3);l=k[0];h=k[1];if(!($interfaceIsEqual(h,$ifaceNil))){$s=-1;return["",h];}$s=-1;return[g+j+l,$ifaceNil];}$s=3;continue;case 2:m=E.LastIndex(a,":");if(!((m===-1))){$s=7;continue;}$s=8;continue;case 7:n=$substring(a,m);if(!AG(n)){$s=9;continue;}$s=10;continue;case 9:o=B.Errorf("invalid port %q after host",new AS([new $String(n)]));$s=11;case 11:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}$s=-1;return["",o];case 10:case 8:case 3:p=$ifaceNil;q=Q(a,3);a=q[0];p=q[1];if(!($interfaceIsEqual(p,$ifaceNil))){$s=-1;return["",p];}$s=-1;return[a,$ifaceNil];}return;}if($f===undefined){$f={$blk:AE};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.$s=$s;$f.$r=$r;return $f;};U.ptr.prototype.setPath=function(a){var a,b,c,d,e,f;b=this;c=Q(a,1);d=c[0];e=c[1];if(!($interfaceIsEqual(e,$ifaceNil))){return e;}b.Path=d;f=T(d,1);if(a===f){b.RawPath="";}else{b.RawPath=a;}return $ifaceNil;};U.prototype.setPath=function(a){return this.$val.setPath(a);};U.ptr.prototype.EscapedPath=function(){var a,b,c,d;a=this;if(!(a.RawPath==="")&&AF(a.RawPath)){b=Q(a.RawPath,1);c=b[0];d=b[1];if($interfaceIsEqual(d,$ifaceNil)&&c===a.Path){return a.RawPath;}}if(a.Path==="*"){return"*";}return T(a.Path,1);};U.prototype.EscapedPath=function(){return this.$val.EscapedPath();};AF=function(a){var a,b,c;b=0;while(true){if(!(b>0;}return true;};AG=function(a){var a,b,c,d,e;if(a===""){return true;}if(!((a.charCodeAt(0)===58))){return false;}b=$substring(a,1);c=0;while(true){if(!(c57){return false;}c+=d[1];}return true;};U.ptr.prototype.String=function(){var a,b,c,d,e,f;a=this;b=new E.Builder.ptr(AT.nil,AO.nil);if(!(a.Scheme==="")){b.WriteString(a.Scheme);b.WriteByte(58);}if(!(a.Opaque==="")){b.WriteString(a.Opaque);}else{if(!(a.Scheme==="")||!(a.Host==="")||!(a.User===AQ.nil)){if(!(a.Host==="")||!(a.Path==="")||!(a.User===AQ.nil)){b.WriteString("//");}c=a.User;if(!(c===AQ.nil)){b.WriteString(c.String());b.WriteByte(64);}d=a.Host;if(!(d==="")){b.WriteString(T(d,3));}}e=a.EscapedPath();if(!(e==="")&&!((e.charCodeAt(0)===47))&&!(a.Host==="")){b.WriteByte(47);}if(b.Len()===0){f=E.IndexByte(e,58);if(f>-1&&(E.IndexByte($substring(e,0,f),47)===-1)){b.WriteString("./");}}b.WriteString(e);}if(a.ForceQuery||!(a.RawQuery==="")){b.WriteByte(63);b.WriteString(a.RawQuery);}if(!(a.Fragment==="")){b.WriteByte(35);b.WriteString(T(a.Fragment,7));}return b.String();};U.prototype.String=function(){return this.$val.String();};AH.prototype.Get=function(a){var a,b,c,d;b=this.$val;if(b===false){return"";}d=(c=b[$String.keyFor(a)],c!==undefined?c.v:AU.nil);if(d.$length===0){return"";}return(0>=d.$length?($throwRuntimeError("index out of range"),undefined):d.$array[d.$offset+0]);};$ptrType(AH).prototype.Get=function(a){return new AH(this.$get()).Get(a);};AH.prototype.Set=function(a,b){var a,b,c,d;c=this.$val;d=a;(c||$throwRuntimeError("assignment to entry in nil map"))[$String.keyFor(d)]={k:d,v:new AU([b])};};$ptrType(AH).prototype.Set=function(a,b){return new AH(this.$get()).Set(a,b);};AH.prototype.Add=function(a,b){var a,b,c,d,e;c=this.$val;d=a;(c||$throwRuntimeError("assignment to entry in nil map"))[$String.keyFor(d)]={k:d,v:$append((e=c[$String.keyFor(a)],e!==undefined?e.v:AU.nil),b)};};$ptrType(AH).prototype.Add=function(a,b){return new AH(this.$get()).Add(a,b);};AH.prototype.Del=function(a){var a,b;b=this.$val;delete b[$String.keyFor(a)];};$ptrType(AH).prototype.Del=function(a){return new AH(this.$get()).Del(a);};AI=function(a){var a,b,c;b={};c=AJ(b,a);return[b,c];};$pkg.ParseQuery=AI;AJ=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;c=$ifaceNil;while(true){if(!(!(b===""))){break;}d=b;e=E.IndexAny(d,"&;");if(e>=0){f=$substring(d,0,e);g=$substring(d,(e+1>>0));d=f;b=g;}else{b="";}if(d===""){continue;}h="";i=E.Index(d,"=");if(i>=0){j=$substring(d,0,i);k=$substring(d,(i+1>>0));d=j;h=k;}l=O(d);d=l[0];m=l[1];if(!($interfaceIsEqual(m,$ifaceNil))){if($interfaceIsEqual(c,$ifaceNil)){c=m;}continue;}n=O(h);h=n[0];m=n[1];if(!($interfaceIsEqual(m,$ifaceNil))){if($interfaceIsEqual(c,$ifaceNil)){c=m;}continue;}o=d;(a||$throwRuntimeError("assignment to entry in nil map"))[$String.keyFor(o)]={k:o,v:$append((p=a[$String.keyFor(d)],p!==undefined?p.v:AU.nil),h)};}c=c;return c;};AH.prototype.Encode=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this.$val;if(a===false){$s=-1;return"";}b=new E.Builder.ptr(AT.nil,AO.nil);c=$makeSlice(AU,0,$keys(a).length);d=a;e=0;f=$keys(d);while(true){if(!(e=i.$length)?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+j]);m=(l=a[$String.keyFor(k)],l!==undefined?l.v:AU.nil);n=R(k);o=m;p=0;while(true){if(!(p=o.$length)?($throwRuntimeError("index out of range"),undefined):o.$array[o.$offset+p]);if(b.Len()>0){b.WriteByte(38);}b.WriteString(n);b.WriteByte(61);b.WriteString(R(q));p++;}j++;}$s=-1;return b.String();}return;}if($f===undefined){$f={$blk:AH.prototype.Encode};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(AH).prototype.Encode=function(){return new AH(this.$get()).Encode();};AK=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,l;c="";if(b===""){c=a;}else if(!((b.charCodeAt(0)===47))){d=E.LastIndex(a,"/");c=$substring(a,0,(d+1>>0))+b;}else{c=b;}if(c===""){return"";}e=AU.nil;f=E.Split(c,"/");g=f;h=0;while(true){if(!(h=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+h]);j=i;if(j===(".")){}else if(j===("..")){if(e.$length>0){e=$subslice(e,0,(e.$length-1>>0));}}else{e=$append(e,i);}h++;}l=(k=f.$length-1>>0,((k<0||k>=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+k]));if(l==="."||l===".."){e=$append(e,"");}return"/"+E.TrimPrefix(E.Join(e,"/"),"/");};U.ptr.prototype.IsAbs=function(){var a;a=this;return!(a.Scheme==="");};U.prototype.IsAbs=function(){return this.$val.IsAbs();};U.ptr.prototype.Parse=function(a){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;d=AA(a);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}c=d;e=c[0];f=c[1];if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return[AR.nil,f];}$s=-1;return[b.ResolveReference(e),$ifaceNil];}return;}if($f===undefined){$f={$blk:U.ptr.prototype.Parse};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};U.prototype.Parse=function(a){return this.$val.Parse(a);};U.ptr.prototype.ResolveReference=function(a){var a,b,c;b=this;c=$clone(a,U);if(a.Scheme===""){c.Scheme=b.Scheme;}if(!(a.Scheme==="")||!(a.Host==="")||!(a.User===AQ.nil)){c.setPath(AK(a.EscapedPath(),""));return c;}if(!(a.Opaque==="")){c.User=AQ.nil;c.Host="";c.Path="";return c;}if(a.Path===""&&a.RawQuery===""){c.RawQuery=b.RawQuery;if(a.Fragment===""){c.Fragment=b.Fragment;}}c.Host=b.Host;c.User=b.User;c.setPath(AK(b.EscapedPath(),a.EscapedPath()));return c;};U.prototype.ResolveReference=function(a){return this.$val.ResolveReference(a);};U.ptr.prototype.Query=function(){var a,b,c;a=this;b=AI(a.RawQuery);c=b[0];return c;};U.prototype.Query=function(){return this.$val.Query();};U.ptr.prototype.RequestURI=function(){var a,b;a=this;b=a.Opaque;if(b===""){b=a.EscapedPath();if(b===""){b="/";}}else{if(E.HasPrefix(b,"//")){b=a.Scheme+":"+b;}}if(a.ForceQuery||!(a.RawQuery==="")){b=b+("?"+a.RawQuery);}return b;};U.prototype.RequestURI=function(){return this.$val.RequestURI();};U.ptr.prototype.Hostname=function(){var a,b,c;a=this;b=AL(a.Host);c=b[0];return c;};U.prototype.Hostname=function(){return this.$val.Hostname();};U.ptr.prototype.Port=function(){var a,b,c;a=this;b=AL(a.Host);c=b[1];return c;};U.prototype.Port=function(){return this.$val.Port();};AL=function(a){var a,b,c,d,e,f;b="";c="";b=a;d=E.LastIndexByte(b,58);if(!((d===-1))&&AG($substring(b,d))){e=$substring(b,0,d);f=$substring(b,(d+1>>0));b=e;c=f;}if(E.HasPrefix(b,"[")&&E.HasSuffix(b,"]")){b=$substring(b,1,(b.length-1>>0));}return[b,c];};U.ptr.prototype.MarshalBinary=function(){var a,b,c,d,e;a=AO.nil;b=$ifaceNil;c=this;d=(new AO($stringToBytes(c.String())));e=$ifaceNil;a=d;b=e;return[a,b];};U.prototype.MarshalBinary=function(){return this.$val.MarshalBinary();};U.ptr.prototype.UnmarshalBinary=function(a){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;d=AA(($bytesToString(a)));$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}c=d;e=c[0];f=c[1];if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return f;}U.copy(b,e);$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:U.ptr.prototype.UnmarshalBinary};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};U.prototype.UnmarshalBinary=function(a){return this.$val.UnmarshalBinary(a);};AM=function(a){var a,b,c,d,e,f;b=a;c=0;while(true){if(!(c>0;}return false;};AV.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Temporary",name:"Temporary",pkg:"",typ:$funcType([],[$Bool],false)}];L.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];M.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];AR.methods=[{prop:"setPath",name:"setPath",pkg:"net/url",typ:$funcType([$String],[$error],false)},{prop:"EscapedPath",name:"EscapedPath",pkg:"",typ:$funcType([],[$String],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)},{prop:"IsAbs",name:"IsAbs",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Parse",name:"Parse",pkg:"",typ:$funcType([$String],[AR,$error],false)},{prop:"ResolveReference",name:"ResolveReference",pkg:"",typ:$funcType([AR],[AR],false)},{prop:"Query",name:"Query",pkg:"",typ:$funcType([],[AH],false)},{prop:"RequestURI",name:"RequestURI",pkg:"",typ:$funcType([],[$String],false)},{prop:"Hostname",name:"Hostname",pkg:"",typ:$funcType([],[$String],false)},{prop:"Port",name:"Port",pkg:"",typ:$funcType([],[$String],false)},{prop:"MarshalBinary",name:"MarshalBinary",pkg:"",typ:$funcType([],[AO,$error],false)},{prop:"UnmarshalBinary",name:"UnmarshalBinary",pkg:"",typ:$funcType([AO],[$error],false)}];AQ.methods=[{prop:"Username",name:"Username",pkg:"",typ:$funcType([],[$String],false)},{prop:"Password",name:"Password",pkg:"",typ:$funcType([],[$String,$Bool],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];AH.methods=[{prop:"Get",name:"Get",pkg:"",typ:$funcType([$String],[$String],false)},{prop:"Set",name:"Set",pkg:"",typ:$funcType([$String,$String],[],false)},{prop:"Add",name:"Add",pkg:"",typ:$funcType([$String,$String],[],false)},{prop:"Del",name:"Del",pkg:"",typ:$funcType([$String],[],false)},{prop:"Encode",name:"Encode",pkg:"",typ:$funcType([],[$String],false)}];F.init("",[{prop:"Op",name:"Op",embedded:false,exported:true,typ:$String,tag:""},{prop:"URL",name:"URL",embedded:false,exported:true,typ:$String,tag:""},{prop:"Err",name:"Err",embedded:false,exported:true,typ:$error,tag:""}]);G.init([{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)}]);H.init([{prop:"Temporary",name:"Temporary",pkg:"",typ:$funcType([],[$Bool],false)}]);U.init("",[{prop:"Scheme",name:"Scheme",embedded:false,exported:true,typ:$String,tag:""},{prop:"Opaque",name:"Opaque",embedded:false,exported:true,typ:$String,tag:""},{prop:"User",name:"User",embedded:false,exported:true,typ:AQ,tag:""},{prop:"Host",name:"Host",embedded:false,exported:true,typ:$String,tag:""},{prop:"Path",name:"Path",embedded:false,exported:true,typ:$String,tag:""},{prop:"RawPath",name:"RawPath",embedded:false,exported:true,typ:$String,tag:""},{prop:"ForceQuery",name:"ForceQuery",embedded:false,exported:true,typ:$Bool,tag:""},{prop:"RawQuery",name:"RawQuery",embedded:false,exported:true,typ:$String,tag:""},{prop:"Fragment",name:"Fragment",embedded:false,exported:true,typ:$String,tag:""}]);X.init("net/url",[{prop:"username",name:"username",embedded:false,exported:false,typ:$String,tag:""},{prop:"password",name:"password",embedded:false,exported:false,typ:$String,tag:""},{prop:"passwordSet",name:"passwordSet",embedded:false,exported:false,typ:$Bool,tag:""}]);AH.init($String,AU);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket"]=(function(){var $pkg={},$init,B,C,G,H,D,E,A,F,I,K,M,R,S,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,N,J,L,O,P,Q,T;B=$packages["bytes"];C=$packages["fmt"];G=$packages["github.com/gopherjs/gopherjs/js"];H=$packages["github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket/websocketjs"];D=$packages["io"];E=$packages["net"];A=$packages["net/url"];F=$packages["time"];I=$pkg.addr=$newType(0,$kindStruct,"websocket.addr",true,"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",false,function(URL_){this.$val=this;if(arguments.length===0){this.URL=AA.nil;return;}this.URL=URL_;});K=$pkg.closeError=$newType(0,$kindStruct,"websocket.closeError",true,"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",false,function(Object_,Code_,Reason_,WasClean_){this.$val=this;if(arguments.length===0){this.Object=null;this.Code=0;this.Reason="";this.WasClean=false;return;}this.Object=Object_;this.Code=Code_;this.Reason=Reason_;this.WasClean=WasClean_;});M=$pkg.deadlineErr=$newType(0,$kindStruct,"websocket.deadlineErr",true,"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",false,function(){this.$val=this;if(arguments.length===0){return;}});R=$pkg.conn=$newType(0,$kindStruct,"websocket.conn",true,"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",false,function(WebSocket_,ch_,readBuf_,readDeadline_){this.$val=this;if(arguments.length===0){this.WebSocket=V.nil;this.ch=$chanNil;this.readBuf=W.nil;this.readDeadline=new F.Time.ptr(new $Uint64(0,0),new $Int64(0,0),X.nil);return;}this.WebSocket=WebSocket_;this.ch=ch_;this.readBuf=readBuf_;this.readDeadline=readDeadline_;});S=$pkg.messageEvent=$newType(0,$kindStruct,"websocket.messageEvent",true,"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",false,function(Object_,Data_){this.$val=this;if(arguments.length===0){this.Object=null;this.Data=null;return;}this.Object=Object_;this.Data=Data_;});U=$sliceType($emptyInterface);V=$ptrType(H.WebSocket);W=$ptrType(B.Reader);X=$ptrType(F.Location);Y=$ptrType(S);Z=$sliceType($Uint8);AA=$ptrType(A.URL);AB=$ptrType(I);AC=$ptrType(K);AD=$ptrType(G.Object);AE=$ptrType(M);AF=$ptrType(R);AG=$chanType(Y,false,false);I.ptr.prototype.Network=function(){var a;a=this;return"websocket";};I.prototype.Network=function(){return this.$val.Network();};J=function(a,b){var a,b;return(function $b(c){var c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=b();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$close(a);$s=-1;return;}return;}if($f===undefined){$f={$blk:$b};}$f.c=c;$f.$s=$s;$f.$r=$r;return $f;});};K.ptr.prototype.Error=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b="";if(!!(a.Object.wasClean)){b="clean";}else{b="unclean";}c=C.Sprintf("CloseEvent: (%s) (%d) %s",new U([new $String(b),new $Int(($parseInt(a.Object.code)>>0)),new $String($internalize(a.Object.reason,$String))]));$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}$s=-1;return c;}return;}if($f===undefined){$f={$blk:K.ptr.prototype.Error};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};K.prototype.Error=function(){return this.$val.Error();};L=function(a,b){var a,b;return(function $b(c){var c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=[c];$r=b();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$go((function(c){return function $b(){var $s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=$send(a,new K.ptr(c[0],0,"",false));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$close(a);$s=-1;return;}return;}if($f===undefined){$f={$blk:$b};}$f.$s=$s;$f.$r=$r;return $f;};})(c),[]);$s=-1;return;}return;}if($f===undefined){$f={$blk:$b};}$f.c=c;$f.$s=$s;$f.$r=$r;return $f;});};M.ptr.prototype.Error=function(){var a;a=this;return"i/o timeout: deadline reached";};M.prototype.Error=function(){return this.$val.Error();};M.ptr.prototype.Timeout=function(){var a;a=this;return true;};M.prototype.Timeout=function(){return this.$val.Timeout();};M.ptr.prototype.Temporary=function(){var a;a=this;return true;};M.prototype.Temporary=function(){return this.$val.Temporary();};O=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=H.New(a);c=b[0];d=b[1];e=Q(d,c);$s=1;case 1:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;}return;}if($f===undefined){$f={$blk:O};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Dial=O;P=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=H.New2(a,b);d=c[0];e=c[1];f=Q(e,d);$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$s=-1;return f;}return;}if($f===undefined){$f={$blk:P};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Dial2=P;Q=function(a,b){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=[b];c=[c];d=[d];if(!($interfaceIsEqual(a,$ifaceNil))){$s=-1;return[$ifaceNil,a];}e=new R.ptr(b[0],new $Chan(Y,1),W.nil,new F.Time.ptr(new $Uint64(0,0),new $Int64(0,0),X.nil));e.initialize();f=new $Chan($error,1);d[0]=$throwNilPointerError;c[0]=$throwNilPointerError;g=(function(b,c,d){return function(){b[0].RemoveEventListener("open",false,d[0]);b[0].RemoveEventListener("close",false,c[0]);};})(b,c,d);d[0]=J(f,g);c[0]=L(f,g);b[0].AddEventListener("open",false,d[0]);b[0].AddEventListener("close",false,c[0]);i=$recv(f);$s=1;case 1:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;a=h[0];j=h[1];if(j&&!($interfaceIsEqual(a,$ifaceNil))){$s=-1;return[$ifaceNil,a];}$s=-1;return[e,$ifaceNil];}return;}if($f===undefined){$f={$blk:Q};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};R.ptr.prototype.onMessage=function(a){var a,b;b=this;$go((function $b(){var $s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=$send(b.ch,new S.ptr(a,null));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:$b};}$f.$s=$s;$f.$r=$r;return $f;}),[]);};R.prototype.onMessage=function(a){return this.$val.onMessage(a);};R.ptr.prototype.onClose=function(a){var a,b;b=this;$go((function $b(){var $s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=$send(b.ch,Y.nil);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:$b};}$f.$s=$s;$f.$r=$r;return $f;}),[]);};R.prototype.onClose=function(a){return this.$val.onClose(a);};R.ptr.prototype.initialize=function(){var a;a=this;a.WebSocket.Object.binaryType=$externalize("arraybuffer",$String);a.WebSocket.AddEventListener("message",false,$methodVal(a,"onMessage"));a.WebSocket.AddEventListener("close",false,$methodVal(a,"onClose"));};R.prototype.initialize=function(){return this.$val.initialize();};R.ptr.prototype.handleFrame=function(a,b){var a,b,c;c=this;if(!b){return[Y.nil,D.EOF];}else if(a===Y.nil){$close(c.ch);return[Y.nil,D.EOF];}return[a,$ifaceNil];};R.prototype.handleFrame=function(a,b){return this.$val.handleFrame(a,b);};R.ptr.prototype.receiveFrame=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=this;c=$chanNil;if(a&&!$clone(b.readDeadline,F.Time).IsZero()){$s=1;continue;}$s=2;continue;case 1:d=$clone(F.Now(),F.Time);if($clone(d,F.Time).After($clone(b.readDeadline,F.Time))){e=$select([[b.ch],[]]);if(e[0]===0){f=e[1];g=f[0];h=f[1];$s=-1;return b.handleFrame(g,h);}else if(e[0]===1){$s=-1;return[Y.nil,N];}}i=F.NewTimer($clone(b.readDeadline,F.Time).Sub($clone(d,F.Time)));$deferred.push([$methodVal(i,"Stop"),[]]);c=i.C;case 2:k=$select([[b.ch],[c]]);$s=3;case 3:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;if(j[0]===0){l=j[1];m=l[0];n=l[1];$s=-1;return b.handleFrame(m,n);}else if(j[0]===1){$s=-1;return[Y.nil,N];}$s=-1;return[Y.nil,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[Y.nil,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:R.ptr.prototype.receiveFrame};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};R.prototype.receiveFrame=function(a){return this.$val.receiveFrame(a);};T=function(a){var a,b,c;b=a.constructor;if(b===$global.ArrayBuffer){c=new($global.Uint8Array)(a);return $assertType($internalize(c,$emptyInterface),Z);}return(new Z($stringToBytes($internalize(a,$String))));};R.ptr.prototype.Read=function(a){var a,b,c,d,e,f,g,h,i,j,k,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;d=this;if(!(d.readBuf===W.nil)){e=d.readBuf.Read(a);b=e[0];c=e[1];if($interfaceIsEqual(c,D.EOF)){d.readBuf=W.nil;c=$ifaceNil;}if(b>0){$s=-1;return[b,c];}}g=d.receiveFrame(true);$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}f=g;h=f[0];c=f[1];if(!($interfaceIsEqual(c,$ifaceNil))){i=0;j=c;b=i;c=j;$s=-1;return[b,c];}k=T(h.Object.data);b=$copySlice(a,k);if(b>=k.$length){$s=-1;return[b,c];}d.readBuf=B.NewReader($subslice(k,b));$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:R.ptr.prototype.Read};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$r=$r;return $f;};R.prototype.Read=function(a){return this.$val.Read(a);};R.ptr.prototype.Write=function(a){var a,b,c,d,e,f,g,h;b=0;c=$ifaceNil;d=this;c=d.WebSocket.Send(a);if(!($interfaceIsEqual(c,$ifaceNil))){e=0;f=c;b=e;c=f;return[b,c];}g=a.$length;h=$ifaceNil;b=g;c=h;return[b,c];};R.prototype.Write=function(a){return this.$val.Write(a);};R.ptr.prototype.LocalAddr=function(){var a;a=this;$panic(new $String("we are unable to implement websocket.conn.LocalAddr() due to limitations in the underlying JavaScript API"));};R.prototype.LocalAddr=function(){return this.$val.LocalAddr();};R.ptr.prototype.RemoteAddr=function(){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;c=A.Parse($internalize(a.WebSocket.Object.url,$String));$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}b=c;d=b[0];e=b[1];if(!($interfaceIsEqual(e,$ifaceNil))){$panic(e);}$s=-1;return new I.ptr(d);}return;}if($f===undefined){$f={$blk:R.ptr.prototype.RemoteAddr};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};R.prototype.RemoteAddr=function(){return this.$val.RemoteAddr();};R.ptr.prototype.SetDeadline=function(a){var a,b;b=this;F.Time.copy(b.readDeadline,a);return $ifaceNil;};R.prototype.SetDeadline=function(a){return this.$val.SetDeadline(a);};R.ptr.prototype.SetReadDeadline=function(a){var a,b;b=this;F.Time.copy(b.readDeadline,a);return $ifaceNil;};R.prototype.SetReadDeadline=function(a){return this.$val.SetReadDeadline(a);};R.ptr.prototype.SetWriteDeadline=function(a){var a,b;b=this;return $ifaceNil;};R.prototype.SetWriteDeadline=function(a){return this.$val.SetWriteDeadline(a);};AB.methods=[{prop:"Network",name:"Network",pkg:"",typ:$funcType([],[$String],false)}];AC.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)}];AE.methods=[{prop:"Error",name:"Error",pkg:"",typ:$funcType([],[$String],false)},{prop:"Timeout",name:"Timeout",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"Temporary",name:"Temporary",pkg:"",typ:$funcType([],[$Bool],false)}];AF.methods=[{prop:"onMessage",name:"onMessage",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",typ:$funcType([AD],[],false)},{prop:"onClose",name:"onClose",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",typ:$funcType([AD],[],false)},{prop:"initialize",name:"initialize",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",typ:$funcType([],[],false)},{prop:"handleFrame",name:"handleFrame",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",typ:$funcType([Y,$Bool],[Y,$error],false)},{prop:"receiveFrame",name:"receiveFrame",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",typ:$funcType([$Bool],[Y,$error],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([Z],[$Int,$error],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([Z],[$Int,$error],false)},{prop:"LocalAddr",name:"LocalAddr",pkg:"",typ:$funcType([],[E.Addr],false)},{prop:"RemoteAddr",name:"RemoteAddr",pkg:"",typ:$funcType([],[E.Addr],false)},{prop:"SetDeadline",name:"SetDeadline",pkg:"",typ:$funcType([F.Time],[$error],false)},{prop:"SetReadDeadline",name:"SetReadDeadline",pkg:"",typ:$funcType([F.Time],[$error],false)},{prop:"SetWriteDeadline",name:"SetWriteDeadline",pkg:"",typ:$funcType([F.Time],[$error],false)}];I.init("",[{prop:"URL",name:"URL",embedded:true,exported:true,typ:AA,tag:""}]);K.init("",[{prop:"Object",name:"Object",embedded:true,exported:true,typ:AD,tag:""},{prop:"Code",name:"Code",embedded:false,exported:true,typ:$Int,tag:"js:\"code\""},{prop:"Reason",name:"Reason",embedded:false,exported:true,typ:$String,tag:"js:\"reason\""},{prop:"WasClean",name:"WasClean",embedded:false,exported:true,typ:$Bool,tag:"js:\"wasClean\""}]);M.init("",[]);R.init("github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket",[{prop:"WebSocket",name:"WebSocket",embedded:true,exported:true,typ:V,tag:""},{prop:"ch",name:"ch",embedded:false,exported:false,typ:AG,tag:""},{prop:"readBuf",name:"readBuf",embedded:false,exported:false,typ:W,tag:""},{prop:"readDeadline",name:"readDeadline",embedded:false,exported:false,typ:F.Time,tag:""}]);S.init("",[{prop:"Object",name:"Object",embedded:true,exported:true,typ:AD,tag:""},{prop:"Data",name:"Data",embedded:false,exported:true,typ:AD,tag:"js:\"data\""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=B.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=G.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=H.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}N=new M.ptr();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["container/heap"]=(function(){var $pkg={},$init,A,D,E,H,I;A=$packages["sort"];D=function(a,b){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=a.Push(b);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}c=a;d=a.Len();$s=2;case 2:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d-1>>0;$r=H(c,e);$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:D};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Push=D;E=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=a.Len();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b-1>>0;$r=a.Swap(0,c);$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d=I(a,0,c);$s=3;case 3:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}d;e=a.Pop();$s=4;case 4:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;}return;}if($f===undefined){$f={$blk:E};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Pop=E;H=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:case 1:d=(c=((b-1>>0))/2,(c===c&&c!==1/0&&c!==-1/0)?c>>0:$throwRuntimeError("integer divide by zero"));if(d===b){e=true;$s=5;continue s;}f=a.Less(b,d);$s=6;case 6:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=!f;case 5:if(e){$s=3;continue;}$s=4;continue;case 3:$s=2;continue;case 4:$r=a.Swap(d,b);$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}b=d;$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:H};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};I=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=b;case 1:e=($imul(2,d))+1>>0;if(e>=c||e<0){$s=2;continue;}f=e;g=e+1>>0;if(!(gb;}return;}if($f===undefined){$f={$blk:I};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux"]=(function(){var $pkg={},$init,H,C,A,D,E,F,I,B,J,G,N,Q,S,T,U,Z,AA,AB,AC,AE,AF,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC,BD,K,L,M,O,P,R,V,W,Y,AD,AG;H=$packages["container/heap"];C=$packages["encoding/binary"];A=$packages["errors"];D=$packages["fmt"];E=$packages["io"];F=$packages["math"];I=$packages["net"];B=$packages["sync"];J=$packages["sync/atomic"];G=$packages["time"];N=$pkg.Allocator=$newType(0,$kindStruct,"smux.Allocator",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",true,function(buffers_){this.$val=this;if(arguments.length===0){this.buffers=AI.nil;return;}this.buffers=buffers_;});Q=$pkg.Frame=$newType(0,$kindStruct,"smux.Frame",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",true,function(ver_,cmd_,sid_,data_){this.$val=this;if(arguments.length===0){this.ver=0;this.cmd=0;this.sid=0;this.data=AJ.nil;return;}this.ver=ver_;this.cmd=cmd_;this.sid=sid_;this.data=data_;});S=$pkg.rawHeader=$newType(8,$kindArray,"smux.rawHeader",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",false,null);T=$pkg.updHeader=$newType(8,$kindArray,"smux.updHeader",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",false,null);U=$pkg.Config=$newType(0,$kindStruct,"smux.Config",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",true,function(Version_,KeepAliveDisabled_,KeepAliveInterval_,KeepAliveTimeout_,MaxFrameSize_,MaxReceiveBuffer_,MaxStreamBuffer_){this.$val=this;if(arguments.length===0){this.Version=0;this.KeepAliveDisabled=false;this.KeepAliveInterval=new G.Duration(0,0);this.KeepAliveTimeout=new G.Duration(0,0);this.MaxFrameSize=0;this.MaxReceiveBuffer=0;this.MaxStreamBuffer=0;return;}this.Version=Version_;this.KeepAliveDisabled=KeepAliveDisabled_;this.KeepAliveInterval=KeepAliveInterval_;this.KeepAliveTimeout=KeepAliveTimeout_;this.MaxFrameSize=MaxFrameSize_;this.MaxReceiveBuffer=MaxReceiveBuffer_;this.MaxStreamBuffer=MaxStreamBuffer_;});Z=$pkg.writeRequest=$newType(0,$kindStruct,"smux.writeRequest",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",false,function(prio_,frame_,result_){this.$val=this;if(arguments.length===0){this.prio=new $Uint64(0,0);this.frame=new Q.ptr(0,0,0,AJ.nil);this.result=$chanNil;return;}this.prio=prio_;this.frame=frame_;this.result=result_;});AA=$pkg.writeResult=$newType(0,$kindStruct,"smux.writeResult",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",false,function(n_,err_){this.$val=this;if(arguments.length===0){this.n=0;this.err=$ifaceNil;return;}this.n=n_;this.err=err_;});AB=$pkg.buffersWriter=$newType(8,$kindInterface,"smux.buffersWriter",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",false,null);AC=$pkg.Session=$newType(0,$kindStruct,"smux.Session",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",true,function(conn_,config_,nextStreamID_,nextStreamIDLock_,bucket_,bucketNotify_,streams_,streamLock_,die_,dieOnce_,socketReadError_,socketWriteError_,chSocketReadError_,chSocketWriteError_,socketReadErrorOnce_,socketWriteErrorOnce_,protoError_,chProtoError_,protoErrorOnce_,chAccepts_,dataReady_,goAway_,deadline_,shaper_,writes_){this.$val=this;if(arguments.length===0){this.conn=$ifaceNil;this.config=AL.nil;this.nextStreamID=0;this.nextStreamIDLock=new B.Mutex.ptr(0,0);this.bucket=0;this.bucketNotify=$chanNil;this.streams=false;this.streamLock=new B.Mutex.ptr(0,0);this.die=$chanNil;this.dieOnce=new B.Once.ptr(new B.Mutex.ptr(0,0),0);this.socketReadError=new J.Value.ptr($ifaceNil);this.socketWriteError=new J.Value.ptr($ifaceNil);this.chSocketReadError=$chanNil;this.chSocketWriteError=$chanNil;this.socketReadErrorOnce=new B.Once.ptr(new B.Mutex.ptr(0,0),0);this.socketWriteErrorOnce=new B.Once.ptr(new B.Mutex.ptr(0,0),0);this.protoError=new J.Value.ptr($ifaceNil);this.chProtoError=$chanNil;this.protoErrorOnce=new B.Once.ptr(new B.Mutex.ptr(0,0),0);this.chAccepts=$chanNil;this.dataReady=0;this.goAway=0;this.deadline=new J.Value.ptr($ifaceNil);this.shaper=$chanNil;this.writes=$chanNil;return;}this.conn=conn_;this.config=config_;this.nextStreamID=nextStreamID_;this.nextStreamIDLock=nextStreamIDLock_;this.bucket=bucket_;this.bucketNotify=bucketNotify_;this.streams=streams_;this.streamLock=streamLock_;this.die=die_;this.dieOnce=dieOnce_;this.socketReadError=socketReadError_;this.socketWriteError=socketWriteError_;this.chSocketReadError=chSocketReadError_;this.chSocketWriteError=chSocketWriteError_;this.socketReadErrorOnce=socketReadErrorOnce_;this.socketWriteErrorOnce=socketWriteErrorOnce_;this.protoError=protoError_;this.chProtoError=chProtoError_;this.protoErrorOnce=protoErrorOnce_;this.chAccepts=chAccepts_;this.dataReady=dataReady_;this.goAway=goAway_;this.deadline=deadline_;this.shaper=shaper_;this.writes=writes_;});AE=$pkg.shaperHeap=$newType(12,$kindSlice,"smux.shaperHeap",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",false,null);AF=$pkg.Stream=$newType(0,$kindStruct,"smux.Stream",true,"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",true,function(id_,sess_,buffers_,heads_,bufferLock_,frameSize_,chReadEvent_,die_,dieOnce_,chFinEvent_,finEventOnce_,readDeadline_,writeDeadline_,numRead_,numWritten_,incr_,peerConsumed_,peerWindow_,chUpdate_){this.$val=this;if(arguments.length===0){this.id=0;this.sess=AM.nil;this.buffers=AU.nil;this.heads=AU.nil;this.bufferLock=new B.Mutex.ptr(0,0);this.frameSize=0;this.chReadEvent=$chanNil;this.die=$chanNil;this.dieOnce=new B.Once.ptr(new B.Mutex.ptr(0,0),0);this.chFinEvent=$chanNil;this.finEventOnce=new B.Once.ptr(new B.Mutex.ptr(0,0),0);this.readDeadline=new J.Value.ptr($ifaceNil);this.writeDeadline=new J.Value.ptr($ifaceNil);this.numRead=0;this.numWritten=0;this.incr=0;this.peerConsumed=0;this.peerWindow=0;this.chUpdate=$chanNil;return;}this.id=id_;this.sess=sess_;this.buffers=buffers_;this.heads=heads_;this.bufferLock=bufferLock_;this.frameSize=frameSize_;this.chReadEvent=chReadEvent_;this.die=die_;this.dieOnce=dieOnce_;this.chFinEvent=chFinEvent_;this.finEventOnce=finEventOnce_;this.readDeadline=readDeadline_;this.writeDeadline=writeDeadline_;this.numRead=numRead_;this.numWritten=numWritten_;this.incr=incr_;this.peerConsumed=peerConsumed_;this.peerWindow=peerWindow_;this.chUpdate=chUpdate_;});AH=$ptrType(N);AI=$sliceType(B.Pool);AJ=$sliceType($Uint8);AK=$sliceType($emptyInterface);AL=$ptrType(U);AM=$ptrType(AC);AN=$structType("",[]);AO=$ptrType(AF);AP=$interfaceType([{prop:"LocalAddr",name:"LocalAddr",pkg:"",typ:$funcType([],[I.Addr],false)}]);AQ=$interfaceType([{prop:"RemoteAddr",name:"RemoteAddr",pkg:"",typ:$funcType([],[I.Addr],false)}]);AR=$ptrType($Int32);AS=$arrayType($Uint8,8);AT=$ptrType(AE);AU=$sliceType(AJ);AV=$ptrType(G.Timer);AW=$ptrType($Uint32);AX=$chanType(AA,false,false);AY=$chanType(G.Time,false,true);AZ=$chanType(AN,false,false);BA=$mapType($Uint32,AO);BB=$chanType(AO,false,false);BC=$chanType(Z,false,false);BD=$chanType(AN,false,true);M=function(){K=O();};O=function(){var a,b,c,d,e,f;a=new N.ptr(AI.nil);a.buffers=$makeSlice(AI,17);b=a.buffers;c=0;while(true){if(!(c=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+e])).New=(function(d){return function(){var f;return $makeSlice(AJ,((f=((d[0]>>>0)),f<32?(1<>0));};})(d);c++;}return a;};$pkg.NewAllocator=O;N.ptr.prototype.Get=function(a){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;if(a<=0||a>65536){$s=-1;return AJ.nil;}c=P(a);if(a===((d=c,d<32?(1<>0)){$s=1;continue;}$s=2;continue;case 1:f=(e=b.buffers,((c<0||c>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+c])).Get();$s=4;case 4:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}$s=-1;return $subslice($assertType(f,AJ),0,a);case 2:i=(g=b.buffers,h=c+1<<24>>>24,((h<0||h>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+h])).Get();$s=5;case 5:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}$s=-1;return $subslice($assertType(i,AJ),0,a);case 3:$s=-1;return AJ.nil;}return;}if($f===undefined){$f={$blk:N.ptr.prototype.Get};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;};N.prototype.Get=function(a){return this.$val.Get(a);};N.ptr.prototype.Put=function(a){var a,b,c,d,e;b=this;c=P(a.$capacity);if((a.$capacity===0)||a.$capacity>65536||!((a.$capacity===((d=c,d<32?(1<>0)))){return A.New("allocator Put() incorrect buffer size");}(e=b.buffers,((c<0||c>=e.$length)?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+c])).Put(a);return $ifaceNil;};N.prototype.Put=function(a){return this.$val.Put(a);};P=function(a){var a,b,c;b=((a>>>0));b=(b|((b>>>1>>>0)))>>>0;b=(b|((b>>>2>>>0)))>>>0;b=(b|((b>>>4>>>0)))>>>0;b=(b|((b>>>8>>>0)))>>>0;b=(b|((b>>>16>>>0)))>>>0;return(c=(($imul(b,130329821)>>>0))>>>27>>>0,((c<0||c>=L.length)?($throwRuntimeError("index out of range"),undefined):L[c]));};R=function(a,b,c){var a,b,c;return new Q.ptr(a,b,c,AJ.nil);};S.prototype.Version=function(){var a;a=this.$val;return a[0];};$ptrType(S).prototype.Version=function(){return new S(this.$get()).Version();};S.prototype.Cmd=function(){var a;a=this.$val;return a[1];};$ptrType(S).prototype.Cmd=function(){return new S(this.$get()).Cmd();};S.prototype.Length=function(){var a;a=this.$val;return $clone(C.LittleEndian,C.littleEndian).Uint16($subslice(new AJ(a),2));};$ptrType(S).prototype.Length=function(){return new S(this.$get()).Length();};S.prototype.StreamID=function(){var a;a=this.$val;return $clone(C.LittleEndian,C.littleEndian).Uint32($subslice(new AJ(a),4));};$ptrType(S).prototype.StreamID=function(){return new S(this.$get()).StreamID();};S.prototype.String=function(){var a,b,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this.$val;b=D.Sprintf("Version:%d Cmd:%d StreamID:%d Length:%d",new AK([new $Uint8(new S($clone(a,S)).Version()),new $Uint8(new S($clone(a,S)).Cmd()),new $Uint32(new S($clone(a,S)).StreamID()),new $Uint16(new S($clone(a,S)).Length())]));$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}$s=-1;return b;}return;}if($f===undefined){$f={$blk:S.prototype.String};}$f.a=a;$f.b=b;$f.$s=$s;$f.$r=$r;return $f;};$ptrType(S).prototype.String=function(){return new S(this.$get()).String();};T.prototype.Consumed=function(){var a;a=this.$val;return $clone(C.LittleEndian,C.littleEndian).Uint32(new AJ(a));};$ptrType(T).prototype.Consumed=function(){return new T(this.$get()).Consumed();};T.prototype.Window=function(){var a;a=this.$val;return $clone(C.LittleEndian,C.littleEndian).Uint32($subslice(new AJ(a),4));};$ptrType(T).prototype.Window=function(){return new T(this.$get()).Window();};V=function(){return new U.ptr(1,false,new G.Duration(2,1410065408),new G.Duration(6,4230196224),32768,4194304,65536);};$pkg.DefaultConfig=V;W=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(!((a.Version===1)||(a.Version===2))){$s=-1;return A.New("unsupported protocol version");}if(!a.KeepAliveDisabled){$s=1;continue;}$s=2;continue;case 1:if((b=a.KeepAliveInterval,(b.$high===0&&b.$low===0))){$s=-1;return A.New("keep-alive interval must be positive");}if((c=a.KeepAliveTimeout,d=a.KeepAliveInterval,(c.$high65535){$s=-1;return A.New("max frame size must not be larger than 65535");}if(a.MaxReceiveBuffer<=0){$s=-1;return A.New("max receive buffer must be positive");}if(a.MaxStreamBuffer<=0){$s=-1;return A.New("max stream buffer must be positive");}if(a.MaxStreamBuffer>a.MaxReceiveBuffer){$s=-1;return A.New("max stream buffer must not be larger than max receive buffer");}if(a.MaxStreamBuffer>2147483647){$s=-1;return A.New("max stream buffer cannot be larger than 2147483647");}$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:W};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};$pkg.VerifyConfig=W;Y=function(a,b){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:if(b===AL.nil){b=V();}c=W(b);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;if(!($interfaceIsEqual(d,$ifaceNil))){$s=-1;return[AM.nil,d];}$s=-1;return[AD(b,a,true),$ifaceNil];}return;}if($f===undefined){$f={$blk:Y};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Client=Y;AD=function(a,b,c){var a,b,c,d;d=new AC.ptr($ifaceNil,AL.nil,0,new B.Mutex.ptr(0,0),0,$chanNil,false,new B.Mutex.ptr(0,0),$chanNil,new B.Once.ptr(new B.Mutex.ptr(0,0),0),new J.Value.ptr($ifaceNil),new J.Value.ptr($ifaceNil),$chanNil,$chanNil,new B.Once.ptr(new B.Mutex.ptr(0,0),0),new B.Once.ptr(new B.Mutex.ptr(0,0),0),new J.Value.ptr($ifaceNil),$chanNil,new B.Once.ptr(new B.Mutex.ptr(0,0),0),$chanNil,0,0,new J.Value.ptr($ifaceNil),$chanNil,$chanNil);d.die=new $Chan(AN,0);d.conn=b;d.config=a;d.streams={};d.chAccepts=new $Chan(AO,1024);d.bucket=((a.MaxReceiveBuffer>>0));d.bucketNotify=new $Chan(AN,1);d.shaper=new $Chan(Z,0);d.writes=new $Chan(Z,0);d.chSocketReadError=new $Chan(AN,0);d.chSocketWriteError=new $Chan(AN,0);d.chProtoError=new $Chan(AN,0);if(c){d.nextStreamID=1;}else{d.nextStreamID=0;}$go($methodVal(d,"shaperLoop"),[]);$go($methodVal(d,"recvLoop"),[]);$go($methodVal(d,"sendLoop"),[]);if(!a.KeepAliveDisabled){$go($methodVal(d,"keepalive"),[]);}return d;};AC.ptr.prototype.OpenStream=function(){var a,b,c,d,e,f,g,h,i,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);a=this;if(a.IsClosed()){$s=-1;return[AO.nil,E.ErrClosedPipe];}$r=a.nextStreamIDLock.Lock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(a.goAway>0){$s=2;continue;}$s=3;continue;case 2:$r=a.nextStreamIDLock.Unlock();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return[AO.nil,$pkg.ErrGoAway];case 3:a.nextStreamID=a.nextStreamID+(2)>>>0;b=a.nextStreamID;if(b===(c=b%2,c===c?c:$throwRuntimeError("integer divide by zero"))){$s=5;continue;}$s=6;continue;case 5:a.goAway=1;$r=a.nextStreamIDLock.Unlock();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return[AO.nil,$pkg.ErrGoAway];case 6:$r=a.nextStreamIDLock.Unlock();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d=AG(b,a.config.MaxFrameSize,a);f=a.writeFrame($clone(R(((a.config.Version<<24>>>24)),0,b),Q));$s=9;case 9:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;g=e[1];if(!($interfaceIsEqual(g,$ifaceNil))){$s=-1;return[AO.nil,g];}$r=a.streamLock.Lock();$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$deferred.push([$methodVal(a.streamLock,"Unlock"),[]]);h=$select([[a.chSocketReadError],[a.chSocketWriteError],[a.die],[]]);if(h[0]===0){$s=-1;return[AO.nil,$assertType(a.socketReadError.Load(),$error)];}else if(h[0]===1){$s=-1;return[AO.nil,$assertType(a.socketWriteError.Load(),$error)];}else if(h[0]===2){$s=-1;return[AO.nil,E.ErrClosedPipe];}else if(h[0]===3){i=b;(a.streams||$throwRuntimeError("assignment to entry in nil map"))[$Uint32.keyFor(i)]={k:i,v:d};$s=-1;return[d,$ifaceNil];}$s=-1;return[AO.nil,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[AO.nil,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AC.ptr.prototype.OpenStream};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AC.prototype.OpenStream=function(){return this.$val.OpenStream();};AC.ptr.prototype.Open=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.OpenStream();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;$s=-1;return[c[0],c[1]];}return;}if($f===undefined){$f={$blk:AC.ptr.prototype.Open};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};AC.prototype.Open=function(){return this.$val.Open();};AC.ptr.prototype.AcceptStream=function(){var a,b,c,d,e,f,g,h,i,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);a=this;b=$chanNil;c=$assertType(a.deadline.Load(),G.Time,true);d=$clone(c[0],G.Time);e=c[1];if(e&&!$clone(d,G.Time).IsZero()){f=G.NewTimer(G.Until($clone(d,G.Time)));$deferred.push([$methodVal(f,"Stop"),[]]);b=f.C;}h=$select([[a.chAccepts],[b],[a.chSocketReadError],[a.chProtoError],[a.die]]);$s=1;case 1:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;if(g[0]===0){i=g[1][0];$s=-1;return[i,$ifaceNil];}else if(g[0]===1){$s=-1;return[AO.nil,$pkg.ErrTimeout];}else if(g[0]===2){$s=-1;return[AO.nil,$assertType(a.socketReadError.Load(),$error)];}else if(g[0]===3){$s=-1;return[AO.nil,$assertType(a.protoError.Load(),$error)];}else if(g[0]===4){$s=-1;return[AO.nil,E.ErrClosedPipe];}$s=-1;return[AO.nil,$ifaceNil];}return;}}catch(err){$err=err;$s=-1;return[AO.nil,$ifaceNil];}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AC.ptr.prototype.AcceptStream};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AC.prototype.AcceptStream=function(){return this.$val.AcceptStream();};AC.ptr.prototype.Accept=function(){var a,b,c,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=a.AcceptStream();$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;$s=-1;return[c[0],c[1]];}return;}if($f===undefined){$f={$blk:AC.ptr.prototype.Accept};}$f.a=a;$f.b=b;$f.c=c;$f.$s=$s;$f.$r=$r;return $f;};AC.prototype.Accept=function(){return this.$val.Accept();};AC.ptr.prototype.Close=function(){var a,b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=[b];b[0]=this;a[0]=false;$r=b[0].dieOnce.Do((function(a,b){return function(){$close(b[0].die);a[0]=true;};})(a,b));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(a[0]){$s=2;continue;}$s=3;continue;case 2:$r=b[0].streamLock.Lock();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}c=b[0].streams;d=0;e=$keys(c);case 6:if(!(d0){if(J.AddInt32((b.$ptr_bucket||(b.$ptr_bucket=new AR(function(){return this.$target.bucket;},function($v){this.$target.bucket=$v;},b))),((e>>0)))>0){b.notifyBucket();}}delete b.streams[$Uint32.keyFor(a)];$r=b.streamLock.Unlock();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AC.ptr.prototype.streamClosed};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AC.prototype.streamClosed=function(a){return this.$val.streamClosed(a);};AC.ptr.prototype.returnTokens=function(a){var a,b;b=this;if(J.AddInt32((b.$ptr_bucket||(b.$ptr_bucket=new AR(function(){return this.$target.bucket;},function($v){this.$target.bucket=$v;},b))),((a>>0)))>0){b.notifyBucket();}};AC.prototype.returnTokens=function(a){return this.$val.returnTokens(a);};AC.ptr.prototype.recvLoop=function(){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=AS.zero();c=AS.zero();case 1:case 3:if(!(J.LoadInt32((a.$ptr_bucket||(a.$ptr_bucket=new AR(function(){return this.$target.bucket;},function($v){this.$target.bucket=$v;},a))))<=0&&!a.IsClosed())){$s=4;continue;}e=$select([[a.bucketNotify],[a.die]]);$s=5;case 5:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;if(d[0]===0){}else if(d[0]===1){$s=-1;return;}$s=3;continue;case 4:g=E.ReadFull(a.conn,new AJ(b));$s=6;case 6:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}f=g;h=f[1];if($interfaceIsEqual(h,$ifaceNil)){$s=7;continue;}$s=8;continue;case 7:J.StoreInt32((a.$ptr_dataReady||(a.$ptr_dataReady=new AR(function(){return this.$target.dataReady;},function($v){this.$target.dataReady=$v;},a))),1);if(!((new S($clone(b,S)).Version()===((a.config.Version<<24>>>24))))){$s=10;continue;}$s=11;continue;case 10:$r=a.notifyProtoError($pkg.ErrInvalidProtocol);$s=12;case 12:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 11:i=new S($clone(b,S)).StreamID();j=new S($clone(b,S)).Cmd();if(j===(3)){$s=14;continue;}if(j===(0)){$s=15;continue;}if(j===(1)){$s=16;continue;}if(j===(2)){$s=17;continue;}if(j===(4)){$s=18;continue;}$s=19;continue;case 14:$s=20;continue;case 15:$r=a.streamLock.Lock();$s=21;case 21:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}k=(l=a.streams[$Uint32.keyFor(i)],l!==undefined?[l.v,true]:[AO.nil,false]);m=k[1];if(!m){$s=22;continue;}$s=23;continue;case 22:n=AG(i,a.config.MaxFrameSize,a);o=i;(a.streams||$throwRuntimeError("assignment to entry in nil map"))[$Uint32.keyFor(o)]={k:o,v:n};q=$select([[a.chAccepts,n],[a.die]]);$s=24;case 24:if($c){$c=false;q=q.$blk();}if(q&&q.$blk!==undefined){break s;}p=q;if(p[0]===0){}else if(p[0]===1){}case 23:$r=a.streamLock.Unlock();$s=25;case 25:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=20;continue;case 16:$r=a.streamLock.Lock();$s=26;case 26:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}r=(s=a.streams[$Uint32.keyFor(i)],s!==undefined?[s.v,true]:[AO.nil,false]);t=r[0];u=r[1];if(u){$s=27;continue;}$s=28;continue;case 27:$r=t.fin();$s=29;case 29:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}t.notifyReadEvent();case 28:$r=a.streamLock.Unlock();$s=30;case 30:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=20;continue;case 17:if(new S($clone(b,S)).Length()>0){$s=31;continue;}$s=32;continue;case 31:v=K.Get(((new S($clone(b,S)).Length()>>0)));$s=33;case 33:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}w=v;y=E.ReadFull(a.conn,w);$s=34;case 34:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}x=y;z=x[0];aa=x[1];if($interfaceIsEqual(aa,$ifaceNil)){$s=35;continue;}$s=36;continue;case 35:$r=a.streamLock.Lock();$s=38;case 38:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}ab=(ac=a.streams[$Uint32.keyFor(i)],ac!==undefined?[ac.v,true]:[AO.nil,false]);ad=ab[0];ae=ab[1];if(ae){$s=39;continue;}$s=40;continue;case 39:af=ad.pushBytes(w);$s=41;case 41:if($c){$c=false;af=af.$blk();}if(af&&af.$blk!==undefined){break s;}af;J.AddInt32((a.$ptr_bucket||(a.$ptr_bucket=new AR(function(){return this.$target.bucket;},function($v){this.$target.bucket=$v;},a))),-((z>>0)));ad.notifyReadEvent();case 40:$r=a.streamLock.Unlock();$s=42;case 42:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=37;continue;case 36:$r=a.notifyReadError(aa);$s=43;case 43:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 37:case 32:$s=20;continue;case 18:ah=E.ReadFull(a.conn,new AJ(c));$s=44;case 44:if($c){$c=false;ah=ah.$blk();}if(ah&&ah.$blk!==undefined){break s;}ag=ah;ai=ag[1];if($interfaceIsEqual(ai,$ifaceNil)){$s=45;continue;}$s=46;continue;case 45:$r=a.streamLock.Lock();$s=48;case 48:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}aj=(ak=a.streams[$Uint32.keyFor(i)],ak!==undefined?[ak.v,true]:[AO.nil,false]);al=aj[0];am=aj[1];if(am){al.update(new T($clone(c,T)).Consumed(),new T($clone(c,T)).Window());}$r=a.streamLock.Unlock();$s=49;case 49:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=47;continue;case 46:$r=a.notifyReadError(ai);$s=50;case 50:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 47:$s=20;continue;case 19:$r=a.notifyProtoError($pkg.ErrInvalidProtocol);$s=51;case 51:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 20:case 13:$s=9;continue;case 8:$r=a.notifyReadError(h);$s=52;case 52:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 9:$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:AC.ptr.prototype.recvLoop};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};AC.prototype.recvLoop=function(){return this.$val.recvLoop();};AC.ptr.prototype.keepalive=function(){var a,b,c,d,e,f,g,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);a=this;b=G.NewTicker(a.config.KeepAliveInterval);c=G.NewTicker(a.config.KeepAliveTimeout);$deferred.push([$methodVal(b,"Stop"),[]]);$deferred.push([$methodVal(c,"Stop"),[]]);case 1:e=$select([[b.C],[c.C],[a.die]]);$s=3;case 3:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;if(d[0]===0){$s=4;continue;}if(d[0]===1){$s=5;continue;}if(d[0]===2){$s=6;continue;}$s=7;continue;case 4:f=a.writeFrameInternal($clone(R(((a.config.Version<<24>>>24)),3,0),Q),b.C,new $Uint64(0,0));$s=8;case 8:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}f;a.notifyBucket();$s=7;continue;case 5:if(!J.CompareAndSwapInt32((a.$ptr_dataReady||(a.$ptr_dataReady=new AR(function(){return this.$target.dataReady;},function($v){this.$target.dataReady=$v;},a))),1,0)){$s=9;continue;}$s=10;continue;case 9:if(J.LoadInt32((a.$ptr_bucket||(a.$ptr_bucket=new AR(function(){return this.$target.bucket;},function($v){this.$target.bucket=$v;},a))))>0){$s=11;continue;}$s=12;continue;case 11:g=a.Close();$s=13;case 13:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}g;$s=-1;return;case 12:case 10:$s=7;continue;case 6:$s=-1;return;case 7:$s=1;continue;case 2:$s=-1;return;}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AC.ptr.prototype.keepalive};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AC.prototype.keepalive=function(){return this.$val.keepalive();};AC.ptr.prototype.shaperLoop=function(){var a,b,c,d,e,f,g,h,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=this;a[0]=AE.nil;c=new Z.ptr(new $Uint64(0,0),new Q.ptr(0,0,0,AJ.nil),$chanNil);d=$chanNil;case 1:if(a[0].$length>0){$s=3;continue;}$s=4;continue;case 3:d=b.writes;e=H.Pop((a.$ptr||(a.$ptr=new AT(function(){return this.$target[0];},function($v){this.$target[0]=$v;},a))));$s=6;case 6:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}Z.copy(c,$assertType(e,Z));$s=5;continue;case 4:d=$chanNil;case 5:g=$select([[b.die],[b.shaper],[d,$clone(c,Z)]]);$s=7;case 7:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}f=g;if(f[0]===0){$s=8;continue;}if(f[0]===1){$s=9;continue;}if(f[0]===2){$s=10;continue;}$s=11;continue;case 8:$s=-1;return;case 9:h=$clone(f[1][0],Z);if(!(d===$chanNil)){$s=12;continue;}$s=13;continue;case 12:$r=H.Push((a.$ptr||(a.$ptr=new AT(function(){return this.$target[0];},function($v){this.$target[0]=$v;},a))),new c.constructor.elem(c));$s=14;case 14:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 13:$r=H.Push((a.$ptr||(a.$ptr=new AT(function(){return this.$target[0];},function($v){this.$target[0]=$v;},a))),new h.constructor.elem(h));$s=15;case 15:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=11;continue;case 10:case 11:$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:AC.ptr.prototype.shaperLoop};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$r=$r;return $f;};AC.prototype.shaperLoop=function(){return this.$val.shaperLoop();};AC.ptr.prototype.sendLoop=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=AJ.nil;c=0;d=$ifaceNil;e=AU.nil;f=$assertType(a.conn,AB,true);g=f[0];h=f[1];if(h){b=$makeSlice(AJ,8);e=$makeSlice(AU,2);}else{b=$makeSlice(AJ,65544);}case 1:j=$select([[a.die],[a.writes]]);$s=3;case 3:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}i=j;if(i[0]===0){$s=4;continue;}if(i[0]===1){$s=5;continue;}$s=6;continue;case 4:$s=-1;return;case 5:k=$clone(i[1][0],Z);(0>=b.$length?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+0]=k.frame.ver);(1>=b.$length?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+1]=k.frame.cmd);$clone(C.LittleEndian,C.littleEndian).PutUint16($subslice(b,2),((k.frame.data.$length<<16>>>16)));$clone(C.LittleEndian,C.littleEndian).PutUint32($subslice(b,4),k.frame.sid);if(e.$length>0){$s=7;continue;}$s=8;continue;case 7:(0>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+0]=$subslice(b,0,8));(1>=e.$length?($throwRuntimeError("index out of range"),undefined):e.$array[e.$offset+1]=k.frame.data);m=g.WriteBuffers(e);$s=10;case 10:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;c=l[0];d=l[1];$s=9;continue;case 8:$copySlice($subslice(b,8),k.frame.data);o=a.conn.Write($subslice(b,0,(8+k.frame.data.$length>>0)));$s=11;case 11:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}n=o;c=n[0];d=n[1];case 9:c=c-(8)>>0;if(c<0){c=0;}p=new AA.ptr(c,d);$r=$send(k.result,$clone($clone(p,AA),AA));$s=12;case 12:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$close(k.result);if(!($interfaceIsEqual(d,$ifaceNil))){$s=13;continue;}$s=14;continue;case 13:$r=a.notifyWriteError(d);$s=15;case 15:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;case 14:case 6:$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:AC.ptr.prototype.sendLoop};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.$s=$s;$f.$r=$r;return $f;};AC.prototype.sendLoop=function(){return this.$val.sendLoop();};AC.ptr.prototype.writeFrame=function(a){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;d=this;f=d.writeFrameInternal($clone(a,Q),$chanNil,new $Uint64(0,0));$s=1;case 1:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;b=e[0];c=e[1];$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AC.ptr.prototype.writeFrame};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};AC.prototype.writeFrame=function(a){return this.$val.writeFrame(a);};AC.ptr.prototype.writeFrameInternal=function(a,b,c){var a,b,c,d,e,f,g,h,i,j,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=this;e=new Z.ptr(c,$clone(a,Q),new $Chan(AA,1));g=$select([[d.shaper,$clone(e,Z)],[d.die],[d.chSocketWriteError],[b]]);$s=1;case 1:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}f=g;if(f[0]===0){$s=2;continue;}if(f[0]===1){$s=3;continue;}if(f[0]===2){$s=4;continue;}if(f[0]===3){$s=5;continue;}$s=6;continue;case 2:$s=6;continue;case 3:$s=-1;return[0,E.ErrClosedPipe];case 4:$s=-1;return[0,$assertType(d.socketWriteError.Load(),$error)];case 5:$s=-1;return[0,$pkg.ErrTimeout];case 6:i=$select([[e.result],[d.die],[d.chSocketWriteError],[b]]);$s=7;case 7:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;if(h[0]===0){j=$clone(h[1][0],AA);$s=-1;return[j.n,j.err];}else if(h[0]===1){$s=-1;return[0,E.ErrClosedPipe];}else if(h[0]===2){$s=-1;return[0,$assertType(d.socketWriteError.Load(),$error)];}else if(h[0]===3){$s=-1;return[0,$pkg.ErrTimeout];}$s=-1;return[0,$ifaceNil];}return;}if($f===undefined){$f={$blk:AC.ptr.prototype.writeFrameInternal};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.$s=$s;$f.$r=$r;return $f;};AC.prototype.writeFrameInternal=function(a,b,c){return this.$val.writeFrameInternal(a,b,c);};AE.prototype.Len=function(){var a;a=this;return a.$length;};$ptrType(AE).prototype.Len=function(){return this.$get().Len();};AE.prototype.Less=function(a,b){var a,b,c,d,e;c=this;return(d=((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]).prio,e=((b<0||b>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]).prio,(d.$high=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]),Z);e=$clone(((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]),Z);Z.copy(((a<0||a>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+a]),d);Z.copy(((b<0||b>=c.$length)?($throwRuntimeError("index out of range"),undefined):c.$array[c.$offset+b]),e);};$ptrType(AE).prototype.Swap=function(a,b){return this.$get().Swap(a,b);};$ptrType(AE).prototype.Push=function(a){var a,b;b=this;b.$set($append(b.$get(),$assertType(a,Z)));};$ptrType(AE).prototype.Pop=function(){var a,b,c,d,e;a=this;b=a.$get();c=b.$length;e=$clone((d=c-1>>0,((d<0||d>=b.$length)?($throwRuntimeError("index out of range"),undefined):b.$array[b.$offset+d])),Z);a.$set($subslice(b,0,(c-1>>0)));return new e.constructor.elem(e);};AG=function(a,b,c){var a,b,c,d;d=new AF.ptr(0,AM.nil,AU.nil,AU.nil,new B.Mutex.ptr(0,0),0,$chanNil,$chanNil,new B.Once.ptr(new B.Mutex.ptr(0,0),0),$chanNil,new B.Once.ptr(new B.Mutex.ptr(0,0),0),new J.Value.ptr($ifaceNil),new J.Value.ptr($ifaceNil),0,0,0,0,0,$chanNil);d.id=a;d.chReadEvent=new $Chan(AN,1);d.chUpdate=new $Chan(AN,1);d.frameSize=b;d.sess=c;d.die=new $Chan(AN,0);d.chFinEvent=new $Chan(AN,0);d.peerWindow=262144;return d;};AF.ptr.prototype.ID=function(){var a;a=this;return a.id;};AF.prototype.ID=function(){return this.$val.ID();};AF.ptr.prototype.Read=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;d=this;case 1:f=d.tryRead(a);$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;b=e[0];c=e[1];if($interfaceIsEqual(c,$pkg.ErrWouldBlock)){$s=4;continue;}$s=5;continue;case 4:g=d.waitRead();$s=7;case 7:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}h=g;if(!($interfaceIsEqual(h,$ifaceNil))){i=0;j=h;b=i;c=j;$s=-1;return[b,c];}$s=6;continue;case 5:k=b;l=c;b=k;c=l;$s=-1;return[b,c];case 6:$s=1;continue;case 2:$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.Read};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.Read=function(a){return this.$val.Read(a);};AF.ptr.prototype.tryRead=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;d=this;if(d.sess.config.Version===2){$s=1;continue;}$s=2;continue;case 1:f=d.tryReadv2(a);$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;b=e[0];c=e[1];$s=-1;return[b,c];case 2:if(a.$length===0){g=0;h=$ifaceNil;b=g;c=h;$s=-1;return[b,c];}$r=d.bufferLock.Lock();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(d.buffers.$length>0){b=$copySlice(a,(i=d.buffers,(0>=i.$length?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+0])));(k=d.buffers,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0]=$subslice((j=d.buffers,(0>=j.$length?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+0])),b)));if((l=d.buffers,(0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0])).$length===0){(m=d.buffers,(0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0]=AJ.nil));d.buffers=$subslice(d.buffers,1);K.Put((n=d.heads,(0>=n.$length?($throwRuntimeError("index out of range"),undefined):n.$array[n.$offset+0])));d.heads=$subslice(d.heads,1);}}$r=d.bufferLock.Unlock();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(b>0){d.sess.returnTokens(b);o=b;p=$ifaceNil;b=o;c=p;$s=-1;return[b,c];}q=$select([[d.die],[]]);if(q[0]===0){r=0;s=E.EOF;b=r;c=s;$s=-1;return[b,c];}else if(q[0]===1){t=0;u=$pkg.ErrWouldBlock;b=t;c=u;$s=-1;return[b,c];}$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.tryRead};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.tryRead=function(a){return this.$val.tryRead(a);};AF.ptr.prototype.tryReadv2=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;d=this;if(a.$length===0){e=0;f=$ifaceNil;b=e;c=f;$s=-1;return[b,c];}g=0;$r=d.bufferLock.Lock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(d.buffers.$length>0){b=$copySlice(a,(h=d.buffers,(0>=h.$length?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+0])));(j=d.buffers,(0>=j.$length?($throwRuntimeError("index out of range"),undefined):j.$array[j.$offset+0]=$subslice((i=d.buffers,(0>=i.$length?($throwRuntimeError("index out of range"),undefined):i.$array[i.$offset+0])),b)));if((k=d.buffers,(0>=k.$length?($throwRuntimeError("index out of range"),undefined):k.$array[k.$offset+0])).$length===0){(l=d.buffers,(0>=l.$length?($throwRuntimeError("index out of range"),undefined):l.$array[l.$offset+0]=AJ.nil));d.buffers=$subslice(d.buffers,1);K.Put((m=d.heads,(0>=m.$length?($throwRuntimeError("index out of range"),undefined):m.$array[m.$offset+0])));d.heads=$subslice(d.heads,1);}}d.numRead=d.numRead+(((b>>>0)))>>>0;d.incr=d.incr+(((b>>>0)))>>>0;if(d.incr>=(((n=d.sess.config.MaxStreamBuffer/2,(n===n&&n!==1/0&&n!==-1/0)?n>>0:$throwRuntimeError("integer divide by zero"))>>>0))||(d.numRead===((b>>>0)))){g=d.numRead;d.incr=0;}$r=d.bufferLock.Unlock();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(b>0){$s=3;continue;}$s=4;continue;case 3:d.sess.returnTokens(b);if(g>0){$s=5;continue;}$s=6;continue;case 5:o=d.sendWindowUpdate(g);$s=8;case 8:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}p=o;q=b;r=p;b=q;c=r;$s=-1;return[b,c];case 6:s=b;t=$ifaceNil;b=s;c=t;$s=-1;return[b,c];case 7:case 4:u=$select([[d.die],[]]);if(u[0]===0){v=0;w=E.EOF;b=v;c=w;$s=-1;return[b,c];}else if(u[0]===1){x=0;y=$pkg.ErrWouldBlock;b=x;c=y;$s=-1;return[b,c];}$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.tryReadv2};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.tryReadv2=function(a){return this.$val.tryReadv2(a);};AF.ptr.prototype.WriteTo=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=new $Int64(0,0);c=$ifaceNil;d=this;if(d.sess.config.Version===2){$s=1;continue;}$s=2;continue;case 1:f=d.writeTov2(a);$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;b=e[0];c=e[1];$s=-1;return[b,c];case 2:case 4:g=AJ.nil;$r=d.bufferLock.Lock();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(d.buffers.$length>0){g=(h=d.buffers,(0>=h.$length?($throwRuntimeError("index out of range"),undefined):h.$array[h.$offset+0]));d.buffers=$subslice(d.buffers,1);d.heads=$subslice(d.heads,1);}$r=d.bufferLock.Unlock();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(!(g===AJ.nil)){$s=8;continue;}$s=9;continue;case 8:j=a.Write(g);$s=11;case 11:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}i=j;k=i[0];l=i[1];d.sess.returnTokens(g.$length);K.Put(g);if(k>0){b=(m=(new $Int64(0,k)),new $Int64(b.$high+m.$high,b.$low+m.$low));}if(!($interfaceIsEqual(l,$ifaceNil))){n=b;o=l;b=n;c=o;$s=-1;return[b,c];}$s=10;continue;case 9:p=d.waitRead();$s=12;case 12:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=p;if(!($interfaceIsEqual(q,$ifaceNil))){r=b;s=q;b=r;c=s;$s=-1;return[b,c];}case 10:$s=4;continue;case 5:$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.WriteTo};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.WriteTo=function(a){return this.$val.WriteTo(a);};AF.ptr.prototype.writeTov2=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=new $Int64(0,0);c=$ifaceNil;d=this;case 1:e=0;f=AJ.nil;$r=d.bufferLock.Lock();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(d.buffers.$length>0){f=(g=d.buffers,(0>=g.$length?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+0]));d.buffers=$subslice(d.buffers,1);d.heads=$subslice(d.heads,1);}d.numRead=d.numRead+(((f.$length>>>0)))>>>0;d.incr=d.incr+(((f.$length>>>0)))>>>0;if(d.incr>=(((h=d.sess.config.MaxStreamBuffer/2,(h===h&&h!==1/0&&h!==-1/0)?h>>0:$throwRuntimeError("integer divide by zero"))>>>0))||(d.numRead===((f.$length>>>0)))){e=d.numRead;d.incr=0;}$r=d.bufferLock.Unlock();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(!(f===AJ.nil)){$s=5;continue;}$s=6;continue;case 5:j=a.Write(f);$s=8;case 8:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}i=j;k=i[0];l=i[1];d.sess.returnTokens(f.$length);K.Put(f);if(k>0){b=(m=(new $Int64(0,k)),new $Int64(b.$high+m.$high,b.$low+m.$low));}if(!($interfaceIsEqual(l,$ifaceNil))){n=b;o=l;b=n;c=o;$s=-1;return[b,c];}if(e>0){$s=9;continue;}$s=10;continue;case 9:p=d.sendWindowUpdate(e);$s=11;case 11:if($c){$c=false;p=p.$blk();}if(p&&p.$blk!==undefined){break s;}q=p;if(!($interfaceIsEqual(q,$ifaceNil))){r=b;s=q;b=r;c=s;$s=-1;return[b,c];}case 10:$s=7;continue;case 6:t=d.waitRead();$s=12;case 12:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}u=t;if(!($interfaceIsEqual(u,$ifaceNil))){v=b;w=u;b=v;c=w;$s=-1;return[b,c];}case 7:$s=1;continue;case 2:$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.writeTov2};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.writeTov2=function(a){return this.$val.writeTov2(a);};AF.ptr.prototype.sendWindowUpdate=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=this;c=AV.nil;d=$chanNil;e=$assertType(b.readDeadline.Load(),G.Time,true);f=$clone(e[0],G.Time);g=e[1];if(g&&!$clone(f,G.Time).IsZero()){c=G.NewTimer(G.Until($clone(f,G.Time)));$deferred.push([$methodVal(c,"Stop"),[]]);d=c.C;}h=$clone(R(((b.sess.config.Version<<24>>>24)),4,b.id),Q);i=AS.zero();$clone(C.LittleEndian,C.littleEndian).PutUint32(new AJ(i),a);$clone(C.LittleEndian,C.littleEndian).PutUint32($subslice(new AJ(i),4),((b.sess.config.MaxStreamBuffer>>>0)));h.data=new AJ(i);k=b.sess.writeFrameInternal($clone(h,Q),d,new $Uint64(0,0));$s=1;case 1:if($c){$c=false;k=k.$blk();}if(k&&k.$blk!==undefined){break s;}j=k;l=j[1];$s=-1;return l;}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AF.ptr.prototype.sendWindowUpdate};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AF.prototype.sendWindowUpdate=function(a){return this.$val.sendWindowUpdate(a);};AF.ptr.prototype.waitRead=function(){var a,b,c,d,e,f,g,h,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);a=this;b=AV.nil;c=$chanNil;d=$assertType(a.readDeadline.Load(),G.Time,true);e=$clone(d[0],G.Time);f=d[1];if(f&&!$clone(e,G.Time).IsZero()){b=G.NewTimer(G.Until($clone(e,G.Time)));$deferred.push([$methodVal(b,"Stop"),[]]);c=b.C;}h=$select([[a.chReadEvent],[a.chFinEvent],[a.sess.chSocketReadError],[a.sess.chProtoError],[c],[a.die]]);$s=1;case 1:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}g=h;if(g[0]===0){$s=2;continue;}if(g[0]===1){$s=3;continue;}if(g[0]===2){$s=4;continue;}if(g[0]===3){$s=5;continue;}if(g[0]===4){$s=6;continue;}if(g[0]===5){$s=7;continue;}$s=8;continue;case 2:$s=-1;return $ifaceNil;case 3:$r=a.bufferLock.Lock();$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$deferred.push([$methodVal(a.bufferLock,"Unlock"),[]]);if(a.buffers.$length>0){$s=-1;return $ifaceNil;}$s=-1;return E.EOF;case 4:$s=-1;return $assertType(a.sess.socketReadError.Load(),$error);case 5:$s=-1;return $assertType(a.sess.protoError.Load(),$error);case 6:$s=-1;return $pkg.ErrTimeout;case 7:$s=-1;return E.ErrClosedPipe;case 8:$s=-1;return $ifaceNil;}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:AF.ptr.prototype.waitRead};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AF.prototype.waitRead=function(){return this.$val.waitRead();};AF.ptr.prototype.Write=function(a){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=0;c=$ifaceNil;d=this;if(d.sess.config.Version===2){$s=1;continue;}$s=2;continue;case 1:f=d.writeV2(a);$s=3;case 3:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}e=f;b=e[0];c=e[1];$s=-1;return[b,c];case 2:g=$chanNil;h=$assertType(d.writeDeadline.Load(),G.Time,true);i=$clone(h[0],G.Time);j=h[1];if(j&&!$clone(i,G.Time).IsZero()){k=G.NewTimer(G.Until($clone(i,G.Time)));$deferred.push([$methodVal(k,"Stop"),[]]);g=k.C;}l=$select([[d.die],[]]);if(l[0]===0){m=0;n=E.ErrClosedPipe;b=m;c=n;$s=-1;return[b,c];}else if(l[0]===1){}o=0;p=$clone(R(((d.sess.config.Version<<24>>>24)),2,d.id),Q);q=a;case 4:if(!(q.$length>0)){$s=5;continue;}r=q.$length;if(r>d.frameSize){r=d.frameSize;}p.data=$subslice(q,0,r);q=$subslice(q,r);t=d.sess.writeFrameInternal($clone(p,Q),g,(new $Uint64(0,d.numWritten)));$s=6;case 6:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}s=t;u=s[0];v=s[1];d.numWritten=d.numWritten+(1)>>>0;o=o+(u)>>0;if(!($interfaceIsEqual(v,$ifaceNil))){w=o;x=v;b=w;c=x;$s=-1;return[b,c];}$s=4;continue;case 5:y=o;z=$ifaceNil;b=y;c=z;$s=-1;return[b,c];}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if(!$curGoroutine.asleep){return[b,c];}if($curGoroutine.asleep){if($f===undefined){$f={$blk:AF.ptr.prototype.Write};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AF.prototype.Write=function(a){return this.$val.Write(a);};AF.ptr.prototype.writeV2=function(a){var a,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;aa=$f.aa;ab=$f.ab;ac=$f.ac;ad=$f.ad;ae=$f.ae;af=$f.af;ag=$f.ag;ah=$f.ah;ai=$f.ai;aj=$f.aj;ak=$f.ak;al=$f.al;am=$f.am;an=$f.an;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=0;c=$ifaceNil;d=this;if(a.$length===0){e=0;f=$ifaceNil;b=e;c=f;$s=-1;return[b,c];}g=$select([[d.die],[]]);if(g[0]===0){h=0;i=E.ErrClosedPipe;b=h;c=i;$s=-1;return[b,c];}else if(g[0]===1){}j=$chanNil;k=$assertType(d.writeDeadline.Load(),G.Time,true);l=$clone(k[0],G.Time);m=k[1];if(m&&!$clone(l,G.Time).IsZero()){n=G.NewTimer(G.Until($clone(l,G.Time)));$deferred.push([$methodVal(n,"Stop"),[]]);j=n.C;}o=0;p=$clone(R(((d.sess.config.Version<<24>>>24)),2,d.id),Q);case 1:q=AJ.nil;r=(((J.LoadUint32((d.$ptr_numWritten||(d.$ptr_numWritten=new AW(function(){return this.$target.numWritten;},function($v){this.$target.numWritten=$v;},d))))-J.LoadUint32((d.$ptr_peerConsumed||(d.$ptr_peerConsumed=new AW(function(){return this.$target.peerConsumed;},function($v){this.$target.peerConsumed=$v;},d))))>>>0)>>0));if(r<0){s=0;t=$pkg.ErrConsumed;b=s;c=t;$s=-1;return[b,c];}u=((J.LoadUint32((d.$ptr_peerWindow||(d.$ptr_peerWindow=new AW(function(){return this.$target.peerWindow;},function($v){this.$target.peerWindow=$v;},d))))>>0))-r>>0;if(u>0){$s=3;continue;}$s=4;continue;case 3:if(u>((a.$length>>0))){q=a;a=AJ.nil;}else{q=$subslice(a,0,u);a=$subslice(a,u);}case 5:if(!(q.$length>0)){$s=6;continue;}v=q.$length;if(v>d.frameSize){v=d.frameSize;}p.data=$subslice(q,0,v);q=$subslice(q,v);x=d.sess.writeFrameInternal($clone(p,Q),j,(new $Uint64(0,J.LoadUint32((d.$ptr_numWritten||(d.$ptr_numWritten=new AW(function(){return this.$target.numWritten;},function($v){this.$target.numWritten=$v;},d)))))));$s=7;case 7:if($c){$c=false;x=x.$blk();}if(x&&x.$blk!==undefined){break s;}w=x;y=w[0];z=w[1];J.AddUint32((d.$ptr_numWritten||(d.$ptr_numWritten=new AW(function(){return this.$target.numWritten;},function($v){this.$target.numWritten=$v;},d))),((v>>>0)));o=o+(y)>>0;if(!($interfaceIsEqual(z,$ifaceNil))){aa=o;ab=z;b=aa;c=ab;$s=-1;return[b,c];}$s=5;continue;case 6:case 4:if(a.$length>0){$s=8;continue;}$s=9;continue;case 8:ad=$select([[d.chFinEvent],[d.die],[j],[d.sess.chSocketWriteError],[d.chUpdate]]);$s=11;case 11:if($c){$c=false;ad=ad.$blk();}if(ad&&ad.$blk!==undefined){break s;}ac=ad;if(ac[0]===0){ae=0;af=E.EOF;b=ae;c=af;$s=-1;return[b,c];}else if(ac[0]===1){ag=o;ah=E.ErrClosedPipe;b=ag;c=ah;$s=-1;return[b,c];}else if(ac[0]===2){ai=o;aj=$pkg.ErrTimeout;b=ai;c=aj;$s=-1;return[b,c];}else if(ac[0]===3){ak=o;al=$assertType(d.sess.socketWriteError.Load(),$error);b=ak;c=al;$s=-1;return[b,c];}else if(ac[0]===4){$s=1;continue;}$s=10;continue;case 9:am=o;an=$ifaceNil;b=am;c=an;$s=-1;return[b,c];case 10:$s=1;continue;case 2:$s=-1;return[b,c];}return;}}catch(err){$err=err;$s=-1;}finally{$callDeferred($deferred,$err);if(!$curGoroutine.asleep){return[b,c];}if($curGoroutine.asleep){if($f===undefined){$f={$blk:AF.ptr.prototype.writeV2};}$f.a=a;$f.aa=aa;$f.ab=ab;$f.ac=ac;$f.ad=ad;$f.ae=ae;$f.af=af;$f.ag=ag;$f.ah=ah;$f.ai=ai;$f.aj=aj;$f.ak=ak;$f.al=al;$f.am=am;$f.an=an;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};AF.prototype.writeV2=function(a){return this.$val.writeV2(a);};AF.ptr.prototype.Close=function(){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];b=[b];b[0]=this;a[0]=false;c=$ifaceNil;$r=b[0].dieOnce.Do((function(a,b){return function(){$close(b[0].die);a[0]=true;};})(a,b));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if(a[0]){$s=2;continue;}$s=3;continue;case 2:e=b[0].sess.writeFrame($clone(R(((b[0].sess.config.Version<<24>>>24)),1,b[0].id),Q));$s=5;case 5:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}d=e;c=d[1];$r=b[0].sess.streamClosed(b[0].id);$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return c;case 3:$s=-1;return E.ErrClosedPipe;case 4:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.Close};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.Close=function(){return this.$val.Close();};AF.ptr.prototype.GetDieCh=function(){var a;a=this;return a.die;};AF.prototype.GetDieCh=function(){return this.$val.GetDieCh();};AF.ptr.prototype.SetReadDeadline=function(a){var a,b;b=this;b.readDeadline.Store(new a.constructor.elem(a));b.notifyReadEvent();return $ifaceNil;};AF.prototype.SetReadDeadline=function(a){return this.$val.SetReadDeadline(a);};AF.ptr.prototype.SetWriteDeadline=function(a){var a,b;b=this;b.writeDeadline.Store(new a.constructor.elem(a));return $ifaceNil;};AF.prototype.SetWriteDeadline=function(a){return this.$val.SetWriteDeadline(a);};AF.ptr.prototype.SetDeadline=function(a){var a,b,c,d;b=this;c=b.SetReadDeadline($clone(a,G.Time));if(!($interfaceIsEqual(c,$ifaceNil))){return c;}d=b.SetWriteDeadline($clone(a,G.Time));if(!($interfaceIsEqual(d,$ifaceNil))){return d;}return $ifaceNil;};AF.prototype.SetDeadline=function(a){return this.$val.SetDeadline(a);};AF.ptr.prototype.sessionClose=function(){var a,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];a[0]=this;$r=a[0].dieOnce.Do((function(a){return function(){$close(a[0].die);};})(a));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.sessionClose};}$f.a=a;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.sessionClose=function(){return this.$val.sessionClose();};AF.ptr.prototype.LocalAddr=function(){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=$assertType(a.sess.conn,AP,true);c=b[0];d=b[1];if(d){$s=1;continue;}$s=2;continue;case 1:e=c.LocalAddr();$s=3;case 3:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;case 2:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.LocalAddr};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.LocalAddr=function(){return this.$val.LocalAddr();};AF.ptr.prototype.RemoteAddr=function(){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=this;b=$assertType(a.sess.conn,AQ,true);c=b[0];d=b[1];if(d){$s=1;continue;}$s=2;continue;case 1:e=c.RemoteAddr();$s=3;case 3:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}$s=-1;return e;case 2:$s=-1;return $ifaceNil;}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.RemoteAddr};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.RemoteAddr=function(){return this.$val.RemoteAddr();};AF.ptr.prototype.pushBytes=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=0;c=$ifaceNil;d=this;$r=d.bufferLock.Lock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d.buffers=$append(d.buffers,a);d.heads=$append(d.heads,a);$r=d.bufferLock.Unlock();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return[b,c];}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.pushBytes};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.pushBytes=function(a){return this.$val.pushBytes(a);};AF.ptr.prototype.recycleTokens=function(){var a,b,c,d,e,f,g,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=0;b=this;$r=b.bufferLock.Lock();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}c=b.buffers;d=0;while(true){if(!(d=f.$length)?($throwRuntimeError("index out of range"),undefined):f.$array[f.$offset+e])).$length)>>0;K.Put((g=b.heads,((e<0||e>=g.$length)?($throwRuntimeError("index out of range"),undefined):g.$array[g.$offset+e])));d++;}b.buffers=AU.nil;b.heads=AU.nil;$r=b.bufferLock.Unlock();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return a;}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.recycleTokens};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.recycleTokens=function(){return this.$val.recycleTokens();};AF.ptr.prototype.notifyReadEvent=function(){var a,b,$r;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;$r=$f.$r;}a=this;b=$select([[a.chReadEvent,new AN.ptr()],[]]);if(b[0]===0){}else if(b[0]===1){}if($f===undefined){$f={$blk:AF.ptr.prototype.notifyReadEvent};}$f.a=a;$f.b=b;$f.$r=$r;return $f;};AF.prototype.notifyReadEvent=function(){return this.$val.notifyReadEvent();};AF.ptr.prototype.update=function(a,b){var a,b,c,d,$r;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$r=$f.$r;}c=this;J.StoreUint32((c.$ptr_peerConsumed||(c.$ptr_peerConsumed=new AW(function(){return this.$target.peerConsumed;},function($v){this.$target.peerConsumed=$v;},c))),a);J.StoreUint32((c.$ptr_peerWindow||(c.$ptr_peerWindow=new AW(function(){return this.$target.peerWindow;},function($v){this.$target.peerWindow=$v;},c))),b);d=$select([[c.chUpdate,new AN.ptr()],[]]);if(d[0]===0){}else if(d[0]===1){}if($f===undefined){$f={$blk:AF.ptr.prototype.update};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$r=$r;return $f;};AF.prototype.update=function(a,b){return this.$val.update(a,b);};AF.ptr.prototype.fin=function(){var a,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];a[0]=this;$r=a[0].finEventOnce.Do((function(a){return function(){$close(a[0].chFinEvent);};})(a));$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$s=-1;return;}return;}if($f===undefined){$f={$blk:AF.ptr.prototype.fin};}$f.a=a;$f.$s=$s;$f.$r=$r;return $f;};AF.prototype.fin=function(){return this.$val.fin();};AH.methods=[{prop:"Get",name:"Get",pkg:"",typ:$funcType([$Int],[AJ],false)},{prop:"Put",name:"Put",pkg:"",typ:$funcType([AJ],[$error],false)}];S.methods=[{prop:"Version",name:"Version",pkg:"",typ:$funcType([],[$Uint8],false)},{prop:"Cmd",name:"Cmd",pkg:"",typ:$funcType([],[$Uint8],false)},{prop:"Length",name:"Length",pkg:"",typ:$funcType([],[$Uint16],false)},{prop:"StreamID",name:"StreamID",pkg:"",typ:$funcType([],[$Uint32],false)},{prop:"String",name:"String",pkg:"",typ:$funcType([],[$String],false)}];T.methods=[{prop:"Consumed",name:"Consumed",pkg:"",typ:$funcType([],[$Uint32],false)},{prop:"Window",name:"Window",pkg:"",typ:$funcType([],[$Uint32],false)}];AM.methods=[{prop:"OpenStream",name:"OpenStream",pkg:"",typ:$funcType([],[AO,$error],false)},{prop:"Open",name:"Open",pkg:"",typ:$funcType([],[E.ReadWriteCloser,$error],false)},{prop:"AcceptStream",name:"AcceptStream",pkg:"",typ:$funcType([],[AO,$error],false)},{prop:"Accept",name:"Accept",pkg:"",typ:$funcType([],[E.ReadWriteCloser,$error],false)},{prop:"Close",name:"Close",pkg:"",typ:$funcType([],[$error],false)},{prop:"notifyBucket",name:"notifyBucket",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[],false)},{prop:"notifyReadError",name:"notifyReadError",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([$error],[],false)},{prop:"notifyWriteError",name:"notifyWriteError",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([$error],[],false)},{prop:"notifyProtoError",name:"notifyProtoError",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([$error],[],false)},{prop:"IsClosed",name:"IsClosed",pkg:"",typ:$funcType([],[$Bool],false)},{prop:"NumStreams",name:"NumStreams",pkg:"",typ:$funcType([],[$Int],false)},{prop:"SetDeadline",name:"SetDeadline",pkg:"",typ:$funcType([G.Time],[$error],false)},{prop:"LocalAddr",name:"LocalAddr",pkg:"",typ:$funcType([],[I.Addr],false)},{prop:"RemoteAddr",name:"RemoteAddr",pkg:"",typ:$funcType([],[I.Addr],false)},{prop:"streamClosed",name:"streamClosed",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([$Uint32],[],false)},{prop:"returnTokens",name:"returnTokens",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([$Int],[],false)},{prop:"recvLoop",name:"recvLoop",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[],false)},{prop:"keepalive",name:"keepalive",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[],false)},{prop:"shaperLoop",name:"shaperLoop",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[],false)},{prop:"sendLoop",name:"sendLoop",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[],false)},{prop:"writeFrame",name:"writeFrame",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([Q],[$Int,$error],false)},{prop:"writeFrameInternal",name:"writeFrameInternal",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([Q,AY,$Uint64],[$Int,$error],false)}];AE.methods=[{prop:"Len",name:"Len",pkg:"",typ:$funcType([],[$Int],false)},{prop:"Less",name:"Less",pkg:"",typ:$funcType([$Int,$Int],[$Bool],false)},{prop:"Swap",name:"Swap",pkg:"",typ:$funcType([$Int,$Int],[],false)}];AT.methods=[{prop:"Push",name:"Push",pkg:"",typ:$funcType([$emptyInterface],[],false)},{prop:"Pop",name:"Pop",pkg:"",typ:$funcType([],[$emptyInterface],false)}];AO.methods=[{prop:"ID",name:"ID",pkg:"",typ:$funcType([],[$Uint32],false)},{prop:"Read",name:"Read",pkg:"",typ:$funcType([AJ],[$Int,$error],false)},{prop:"tryRead",name:"tryRead",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([AJ],[$Int,$error],false)},{prop:"tryReadv2",name:"tryReadv2",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([AJ],[$Int,$error],false)},{prop:"WriteTo",name:"WriteTo",pkg:"",typ:$funcType([E.Writer],[$Int64,$error],false)},{prop:"writeTov2",name:"writeTov2",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([E.Writer],[$Int64,$error],false)},{prop:"sendWindowUpdate",name:"sendWindowUpdate",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([$Uint32],[$error],false)},{prop:"waitRead",name:"waitRead",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[$error],false)},{prop:"Write",name:"Write",pkg:"",typ:$funcType([AJ],[$Int,$error],false)},{prop:"writeV2",name:"writeV2",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([AJ],[$Int,$error],false)},{prop:"Close",name:"Close",pkg:"",typ:$funcType([],[$error],false)},{prop:"GetDieCh",name:"GetDieCh",pkg:"",typ:$funcType([],[BD],false)},{prop:"SetReadDeadline",name:"SetReadDeadline",pkg:"",typ:$funcType([G.Time],[$error],false)},{prop:"SetWriteDeadline",name:"SetWriteDeadline",pkg:"",typ:$funcType([G.Time],[$error],false)},{prop:"SetDeadline",name:"SetDeadline",pkg:"",typ:$funcType([G.Time],[$error],false)},{prop:"sessionClose",name:"sessionClose",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[],false)},{prop:"LocalAddr",name:"LocalAddr",pkg:"",typ:$funcType([],[I.Addr],false)},{prop:"RemoteAddr",name:"RemoteAddr",pkg:"",typ:$funcType([],[I.Addr],false)},{prop:"pushBytes",name:"pushBytes",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([AJ],[$Int,$error],false)},{prop:"recycleTokens",name:"recycleTokens",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[$Int],false)},{prop:"notifyReadEvent",name:"notifyReadEvent",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[],false)},{prop:"update",name:"update",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([$Uint32,$Uint32],[],false)},{prop:"fin",name:"fin",pkg:"github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",typ:$funcType([],[],false)}];N.init("github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",[{prop:"buffers",name:"buffers",embedded:false,exported:false,typ:AI,tag:""}]);Q.init("github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",[{prop:"ver",name:"ver",embedded:false,exported:false,typ:$Uint8,tag:""},{prop:"cmd",name:"cmd",embedded:false,exported:false,typ:$Uint8,tag:""},{prop:"sid",name:"sid",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"data",name:"data",embedded:false,exported:false,typ:AJ,tag:""}]);S.init($Uint8,8);T.init($Uint8,8);U.init("",[{prop:"Version",name:"Version",embedded:false,exported:true,typ:$Int,tag:""},{prop:"KeepAliveDisabled",name:"KeepAliveDisabled",embedded:false,exported:true,typ:$Bool,tag:""},{prop:"KeepAliveInterval",name:"KeepAliveInterval",embedded:false,exported:true,typ:G.Duration,tag:""},{prop:"KeepAliveTimeout",name:"KeepAliveTimeout",embedded:false,exported:true,typ:G.Duration,tag:""},{prop:"MaxFrameSize",name:"MaxFrameSize",embedded:false,exported:true,typ:$Int,tag:""},{prop:"MaxReceiveBuffer",name:"MaxReceiveBuffer",embedded:false,exported:true,typ:$Int,tag:""},{prop:"MaxStreamBuffer",name:"MaxStreamBuffer",embedded:false,exported:true,typ:$Int,tag:""}]);Z.init("github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",[{prop:"prio",name:"prio",embedded:false,exported:false,typ:$Uint64,tag:""},{prop:"frame",name:"frame",embedded:false,exported:false,typ:Q,tag:""},{prop:"result",name:"result",embedded:false,exported:false,typ:AX,tag:""}]);AA.init("github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",[{prop:"n",name:"n",embedded:false,exported:false,typ:$Int,tag:""},{prop:"err",name:"err",embedded:false,exported:false,typ:$error,tag:""}]);AB.init([{prop:"WriteBuffers",name:"WriteBuffers",pkg:"",typ:$funcType([AU],[$Int,$error],false)}]);AC.init("github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",[{prop:"conn",name:"conn",embedded:false,exported:false,typ:E.ReadWriteCloser,tag:""},{prop:"config",name:"config",embedded:false,exported:false,typ:AL,tag:""},{prop:"nextStreamID",name:"nextStreamID",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"nextStreamIDLock",name:"nextStreamIDLock",embedded:false,exported:false,typ:B.Mutex,tag:""},{prop:"bucket",name:"bucket",embedded:false,exported:false,typ:$Int32,tag:""},{prop:"bucketNotify",name:"bucketNotify",embedded:false,exported:false,typ:AZ,tag:""},{prop:"streams",name:"streams",embedded:false,exported:false,typ:BA,tag:""},{prop:"streamLock",name:"streamLock",embedded:false,exported:false,typ:B.Mutex,tag:""},{prop:"die",name:"die",embedded:false,exported:false,typ:AZ,tag:""},{prop:"dieOnce",name:"dieOnce",embedded:false,exported:false,typ:B.Once,tag:""},{prop:"socketReadError",name:"socketReadError",embedded:false,exported:false,typ:J.Value,tag:""},{prop:"socketWriteError",name:"socketWriteError",embedded:false,exported:false,typ:J.Value,tag:""},{prop:"chSocketReadError",name:"chSocketReadError",embedded:false,exported:false,typ:AZ,tag:""},{prop:"chSocketWriteError",name:"chSocketWriteError",embedded:false,exported:false,typ:AZ,tag:""},{prop:"socketReadErrorOnce",name:"socketReadErrorOnce",embedded:false,exported:false,typ:B.Once,tag:""},{prop:"socketWriteErrorOnce",name:"socketWriteErrorOnce",embedded:false,exported:false,typ:B.Once,tag:""},{prop:"protoError",name:"protoError",embedded:false,exported:false,typ:J.Value,tag:""},{prop:"chProtoError",name:"chProtoError",embedded:false,exported:false,typ:AZ,tag:""},{prop:"protoErrorOnce",name:"protoErrorOnce",embedded:false,exported:false,typ:B.Once,tag:""},{prop:"chAccepts",name:"chAccepts",embedded:false,exported:false,typ:BB,tag:""},{prop:"dataReady",name:"dataReady",embedded:false,exported:false,typ:$Int32,tag:""},{prop:"goAway",name:"goAway",embedded:false,exported:false,typ:$Int32,tag:""},{prop:"deadline",name:"deadline",embedded:false,exported:false,typ:J.Value,tag:""},{prop:"shaper",name:"shaper",embedded:false,exported:false,typ:BC,tag:""},{prop:"writes",name:"writes",embedded:false,exported:false,typ:BC,tag:""}]);AE.init(Z);AF.init("github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux",[{prop:"id",name:"id",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"sess",name:"sess",embedded:false,exported:false,typ:AM,tag:""},{prop:"buffers",name:"buffers",embedded:false,exported:false,typ:AU,tag:""},{prop:"heads",name:"heads",embedded:false,exported:false,typ:AU,tag:""},{prop:"bufferLock",name:"bufferLock",embedded:false,exported:false,typ:B.Mutex,tag:""},{prop:"frameSize",name:"frameSize",embedded:false,exported:false,typ:$Int,tag:""},{prop:"chReadEvent",name:"chReadEvent",embedded:false,exported:false,typ:AZ,tag:""},{prop:"die",name:"die",embedded:false,exported:false,typ:AZ,tag:""},{prop:"dieOnce",name:"dieOnce",embedded:false,exported:false,typ:B.Once,tag:""},{prop:"chFinEvent",name:"chFinEvent",embedded:false,exported:false,typ:AZ,tag:""},{prop:"finEventOnce",name:"finEventOnce",embedded:false,exported:false,typ:B.Once,tag:""},{prop:"readDeadline",name:"readDeadline",embedded:false,exported:false,typ:J.Value,tag:""},{prop:"writeDeadline",name:"writeDeadline",embedded:false,exported:false,typ:J.Value,tag:""},{prop:"numRead",name:"numRead",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"numWritten",name:"numWritten",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"incr",name:"incr",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"peerConsumed",name:"peerConsumed",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"peerWindow",name:"peerWindow",embedded:false,exported:false,typ:$Uint32,tag:""},{prop:"chUpdate",name:"chUpdate",embedded:false,exported:false,typ:AZ,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=H.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=A.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=I.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=J.$init();$s=9;case 9:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=G.$init();$s=10;case 10:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}K=AH.nil;L=$toNativeArray($kindUint8,[0,9,1,10,13,21,2,29,11,14,16,18,22,25,3,30,8,12,20,28,15,17,24,7,19,27,23,6,26,5,4,31]);$pkg.ErrInvalidProtocol=A.New("invalid protocol");$pkg.ErrConsumed=A.New("peer consumed more than sent");$pkg.ErrGoAway=A.New("stream id overflows, should start a new connection");$pkg.ErrTimeout=A.New("timeout");$pkg.ErrWouldBlock=A.New("operation would block on IO");M();}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["github.com/v2fly/BrowserBridge/bridge"]=(function(){var $pkg={},$init,A,C,B,D,E,F,G,H,J,K,I;A=$packages["fmt"];C=$packages["github.com/v2fly/BrowserBridge/proto"];B=$packages["github.com/v2fly/BrowserBridge/vendor/github.com/gopherjs/websocket"];D=$packages["github.com/v2fly/BrowserBridge/vendor/github.com/xtaci/smux"];E=$packages["io"];F=$packages["net"];G=$packages["time"];H=$pkg.Settings=$newType(0,$kindStruct,"bridge.Settings",true,"github.com/v2fly/BrowserBridge/bridge",true,function(DialAddr_){this.$val=this;if(arguments.length===0){this.DialAddr="";return;}this.DialAddr=DialAddr_;});J=$sliceType($emptyInterface);K=$ptrType(D.Config);I=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=[a];case 1:b=G.NewTimer(new G.Duration(0,1000000000));c=(function(a){return function $b(){var c,d,e,f,g,h,i,j,k,l,m,n,o,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:d=B.Dial(a[0].DialAddr);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}c=d;e=c[0];f=c[1];if(!($interfaceIsEqual(f,$ifaceNil))){$s=2;continue;}$s=3;continue;case 2:g=A.Println(new J([f,new $String(a[0].DialAddr)]));$s=4;case 4:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}g;$s=-1;return;case 3:i=D.Client(e,K.nil);$s=5;case 5:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}h=i;j=h[0];f=h[1];if(!($interfaceIsEqual(f,$ifaceNil))){$s=-1;return;}case 6:k=[k];m=j.Accept();$s=8;case 8:if($c){$c=false;m=m.$blk();}if(m&&m.$blk!==undefined){break s;}l=m;k[0]=l[0];n=l[1];if(!($interfaceIsEqual(n,$ifaceNil))){$s=9;continue;}$s=10;continue;case 9:o=A.Println(new J([n]));$s=11;case 11:if($c){$c=false;o=o.$blk();}if(o&&o.$blk!==undefined){break s;}o;$s=-1;return;case 10:$go((function(a,k){return function $b(){var aa,ab,p,q,r,s,t,u,v,w,x,y,z,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;aa=$f.aa;ab=$f.ab;p=$f.p;q=$f.q;r=$f.r;s=$f.s;t=$f.t;u=$f.u;v=$f.v;w=$f.w;x=$f.x;y=$f.y;z=$f.z;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:p=[p];r=C.ReadRequest(k[0]);$s=1;case 1:if($c){$c=false;r=r.$blk();}if(r&&r.$blk!==undefined){break s;}q=r;s=q[0];p[0]=q[1];if(!($interfaceIsEqual(s,$ifaceNil))){$s=2;continue;}$s=3;continue;case 2:t=A.Println(new J([s]));$s=4;case 4:if($c){$c=false;t=t.$blk();}if(t&&t.$blk!==undefined){break s;}t;$s=-1;return;case 3:u=(function(a,k,p){return function $b(){var u,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;u=$f.u;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:u=B.Dial(p[0].Destination);$s=1;case 1:if($c){$c=false;u=u.$blk();}if(u&&u.$blk!==undefined){break s;}$s=-1;return u;}return;}if($f===undefined){$f={$blk:$b};}$f.u=u;$f.$s=$s;$f.$r=$r;return $f;};})(a,k,p);if(!((p[0].ProtocolStringSize===0))){$s=5;continue;}$s=6;continue;case 5:u=(function(a,k,p){return function $b(){var v,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;v=$f.v;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:v=B.Dial2(p[0].Destination,p[0].ProtocolString);$s=1;case 1:if($c){$c=false;v=v.$blk();}if(v&&v.$blk!==undefined){break s;}$s=-1;return v;}return;}if($f===undefined){$f={$blk:$b};}$f.v=v;$f.$s=$s;$f.$r=$r;return $f;};})(a,k,p);case 6:w=u();$s=7;case 7:if($c){$c=false;w=w.$blk();}if(w&&w.$blk!==undefined){break s;}v=w;x=v[0];s=v[1];if(!($interfaceIsEqual(s,$ifaceNil))){$s=8;continue;}$s=9;continue;case 8:y=A.Println(new J([s]));$s=10;case 10:if($c){$c=false;y=y.$blk();}if(y&&y.$blk!==undefined){break s;}y;z=k[0].Close();$s=11;case 11:if($c){$c=false;z=z.$blk();}if(z&&z.$blk!==undefined){break s;}z;$s=-1;return;case 9:$go(E.Copy,[k[0],x]);aa=E.Copy(x,k[0]);$s=12;case 12:if($c){$c=false;aa=aa.$blk();}if(aa&&aa.$blk!==undefined){break s;}aa;ab=k[0].Close();$s=13;case 13:if($c){$c=false;ab=ab.$blk();}if(ab&&ab.$blk!==undefined){break s;}ab;$s=-1;return;}return;}if($f===undefined){$f={$blk:$b};}$f.aa=aa;$f.ab=ab;$f.p=p;$f.q=q;$f.r=r;$f.s=s;$f.t=t;$f.u=u;$f.v=v;$f.w=w;$f.x=x;$f.y=y;$f.z=z;$f.$s=$s;$f.$r=$r;return $f;};})(a,k),[]);$s=6;continue;case 7:$s=-1;return;}return;}if($f===undefined){$f={$blk:$b};}$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.$s=$s;$f.$r=$r;return $f;};})(a);$r=c();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}d=$recv(b.C);$s=4;case 4:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}d[0];$s=1;continue;case 2:$s=-1;return;}return;}if($f===undefined){$f={$blk:I};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Bridge=I;H.init("",[{prop:"DialAddr",name:"DialAddr",embedded:false,exported:true,typ:$String,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=G.$init();$s=7;case 7:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["log"]=(function(){var $pkg={},$init,A,E,B,C,D,F,G,Z,AA,AB,AC,AD,I,H,J,R;A=$packages["fmt"];E=$packages["github.com/gopherjs/gopherjs/nosync"];B=$packages["io"];C=$packages["os"];D=$packages["runtime"];F=$packages["time"];G=$pkg.Logger=$newType(0,$kindStruct,"log.Logger",true,"log",true,function(mu_,prefix_,flag_,out_,buf_){this.$val=this;if(arguments.length===0){this.mu=new E.Mutex.ptr(false);this.prefix="";this.flag=0;this.out=$ifaceNil;this.buf=Z.nil;return;}this.mu=mu_;this.prefix=prefix_;this.flag=flag_;this.out=out_;this.buf=buf_;});Z=$sliceType($Uint8);AA=$arrayType($Uint8,20);AB=$ptrType(Z);AC=$sliceType($emptyInterface);AD=$ptrType(G);H=function(a,b,c){var a,b,c;return new G.ptr(new E.Mutex.ptr(false),b,c,a,Z.nil);};$pkg.New=H;G.ptr.prototype.SetOutput=function(a){var a,b,$deferred;var $err=null;try{$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=this;b.mu.Lock();$deferred.push([$methodVal(b.mu,"Unlock"),[]]);b.out=a;}catch(err){$err=err;}finally{$callDeferred($deferred,$err);}};G.prototype.SetOutput=function(a){return this.$val.SetOutput(a);};J=function(a,b,c){var a,b,c,d,e,f,g;d=AA.zero();e=19;while(true){if(!(b>=10||c>1)){break;}c=c-(1)>>0;g=(f=b/10,(f===f&&f!==1/0&&f!==-1/0)?f>>0:$throwRuntimeError("integer divide by zero"));((e<0||e>=d.length)?($throwRuntimeError("index out of range"),undefined):d[e]=((((48+b>>0)-($imul(g,10))>>0)<<24>>>24)));e=e-(1)>>0;b=g;}((e<0||e>=d.length)?($throwRuntimeError("index out of range"),undefined):d[e]=(((48+b>>0)<<24>>>24)));a.$set($appendSlice(a.$get(),$subslice(new Z(d),e)));};G.ptr.prototype.formatHeader=function(a,b,c,d){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;l=$f.l;m=$f.m;n=$f.n;o=$f.o;p=$f.p;q=$f.q;r=$f.r;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:e=this;a.$set($appendSlice(a.$get(),e.prefix));if(!(((e.flag&7)===0))){$s=1;continue;}$s=2;continue;case 1:if(!(((e.flag&32)===0))){F.Time.copy(b,$clone(b,F.Time).UTC());}if(!(((e.flag&1)===0))){$s=3;continue;}$s=4;continue;case 3:g=$clone(b,F.Time).Date();$s=5;case 5:if($c){$c=false;g=g.$blk();}if(g&&g.$blk!==undefined){break s;}f=g;h=f[0];i=f[1];j=f[2];J(a,h,4);a.$set($append(a.$get(),47));J(a,((i>>0)),2);a.$set($append(a.$get(),47));J(a,j,2);a.$set($append(a.$get(),32));case 4:if(!(((e.flag&6)===0))){$s=6;continue;}$s=7;continue;case 6:l=$clone(b,F.Time).Clock();$s=8;case 8:if($c){$c=false;l=l.$blk();}if(l&&l.$blk!==undefined){break s;}k=l;m=k[0];n=k[1];o=k[2];J(a,m,2);a.$set($append(a.$get(),58));J(a,n,2);a.$set($append(a.$get(),58));J(a,o,2);if(!(((e.flag&4)===0))){a.$set($append(a.$get(),46));J(a,(p=$clone(b,F.Time).Nanosecond()/1000,(p===p&&p!==1/0&&p!==-1/0)?p>>0:$throwRuntimeError("integer divide by zero")),6);}a.$set($append(a.$get(),32));case 7:case 2:if(!(((e.flag&24)===0))){if(!(((e.flag&16)===0))){q=c;r=c.length-1>>0;while(true){if(!(r>0)){break;}if(c.charCodeAt(r)===47){q=$substring(c,(r+1>>0));break;}r=r-(1)>>0;}c=q;}a.$set($appendSlice(a.$get(),c));a.$set($append(a.$get(),58));J(a,d,-1);a.$set($appendSlice(a.$get(),": "));}$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.formatHeader};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.l=l;$f.m=m;$f.n=n;$f.o=o;$f.p=p;$f.q=q;$f.r=r;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.formatHeader=function(a,b,c,d){return this.$val.formatHeader(a,b,c,d);};G.ptr.prototype.Output=function(a,b){var a,b,c,d,e,f,g,h,i,j,k,$s,$deferred,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;j=$f.j;k=$f.k;$s=$f.$s;$deferred=$f.$deferred;$r=$f.$r;}var $err=null;try{s:while(true){switch($s){case 0:$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);c=this;d=$clone(F.Now(),F.Time);e="";f=0;c.mu.Lock();$deferred.push([$methodVal(c.mu,"Unlock"),[]]);if(!(((c.flag&24)===0))){c.mu.Unlock();g=false;h=D.Caller(a);e=h[1];f=h[2];g=h[3];if(!g){e="???";f=0;}c.mu.Lock();}c.buf=$subslice(c.buf,0,0);$r=c.formatHeader((c.$ptr_buf||(c.$ptr_buf=new AB(function(){return this.$target.buf;},function($v){this.$target.buf=$v;},c))),$clone(d,F.Time),e,f);$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}c.buf=$appendSlice(c.buf,b);if((b.length===0)||!((b.charCodeAt((b.length-1>>0))===10))){c.buf=$append(c.buf,10);}j=c.out.Write(c.buf);$s=2;case 2:if($c){$c=false;j=j.$blk();}if(j&&j.$blk!==undefined){break s;}i=j;k=i[1];$s=-1;return k;}return;}}catch(err){$err=err;$s=-1;return $ifaceNil;}finally{$callDeferred($deferred,$err);if($curGoroutine.asleep){if($f===undefined){$f={$blk:G.ptr.prototype.Output};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.j=j;$f.k=k;$f.$s=$s;$f.$deferred=$deferred;$f.$r=$r;return $f;}}};G.prototype.Output=function(a,b){return this.$val.Output(a,b);};G.ptr.prototype.Printf=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=A.Sprintf(a,b);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;f=c.Output(2,e);$s=2;case 2:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}f;$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Printf};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Printf=function(a,b){return this.$val.Printf(a,b);};G.ptr.prototype.Print=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=A.Sprint(a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;e=b.Output(2,d);$s=2;case 2:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}e;$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Print};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Print=function(a){return this.$val.Print(a);};G.ptr.prototype.Println=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=A.Sprintln(a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;e=b.Output(2,d);$s=2;case 2:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}e;$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Println};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Println=function(a){return this.$val.Println(a);};G.ptr.prototype.Fatal=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=A.Sprint(a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;e=b.Output(2,d);$s=2;case 2:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}e;C.Exit(1);$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Fatal};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Fatal=function(a){return this.$val.Fatal(a);};G.ptr.prototype.Fatalf=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=A.Sprintf(a,b);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;f=c.Output(2,e);$s=2;case 2:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}f;C.Exit(1);$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Fatalf};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Fatalf=function(a,b){return this.$val.Fatalf(a,b);};G.ptr.prototype.Fatalln=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=A.Sprintln(a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;e=b.Output(2,d);$s=2;case 2:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}e;C.Exit(1);$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Fatalln};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Fatalln=function(a){return this.$val.Fatalln(a);};G.ptr.prototype.Panic=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=A.Sprint(a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;e=b.Output(2,d);$s=2;case 2:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}e;$panic(new $String(d));$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Panic};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Panic=function(a){return this.$val.Panic(a);};G.ptr.prototype.Panicf=function(a,b){var a,b,c,d,e,f,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:c=this;d=A.Sprintf(a,b);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}e=d;f=c.Output(2,e);$s=2;case 2:if($c){$c=false;f=f.$blk();}if(f&&f.$blk!==undefined){break s;}f;$panic(new $String(e));$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Panicf};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Panicf=function(a,b){return this.$val.Panicf(a,b);};G.ptr.prototype.Panicln=function(a){var a,b,c,d,e,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;e=$f.e;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=this;c=A.Sprintln(a);$s=1;case 1:if($c){$c=false;c=c.$blk();}if(c&&c.$blk!==undefined){break s;}d=c;e=b.Output(2,d);$s=2;case 2:if($c){$c=false;e=e.$blk();}if(e&&e.$blk!==undefined){break s;}e;$panic(new $String(d));$s=-1;return;}return;}if($f===undefined){$f={$blk:G.ptr.prototype.Panicln};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.$s=$s;$f.$r=$r;return $f;};G.prototype.Panicln=function(a){return this.$val.Panicln(a);};G.ptr.prototype.Flags=function(){var a,$deferred;var $err=null;try{$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);a=this;a.mu.Lock();$deferred.push([$methodVal(a.mu,"Unlock"),[]]);return a.flag;}catch(err){$err=err;return 0;}finally{$callDeferred($deferred,$err);}};G.prototype.Flags=function(){return this.$val.Flags();};G.ptr.prototype.SetFlags=function(a){var a,b,$deferred;var $err=null;try{$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=this;b.mu.Lock();$deferred.push([$methodVal(b.mu,"Unlock"),[]]);b.flag=a;}catch(err){$err=err;}finally{$callDeferred($deferred,$err);}};G.prototype.SetFlags=function(a){return this.$val.SetFlags(a);};G.ptr.prototype.Prefix=function(){var a,$deferred;var $err=null;try{$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);a=this;a.mu.Lock();$deferred.push([$methodVal(a.mu,"Unlock"),[]]);return a.prefix;}catch(err){$err=err;return"";}finally{$callDeferred($deferred,$err);}};G.prototype.Prefix=function(){return this.$val.Prefix();};G.ptr.prototype.SetPrefix=function(a){var a,b,$deferred;var $err=null;try{$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);b=this;b.mu.Lock();$deferred.push([$methodVal(b.mu,"Unlock"),[]]);b.prefix=a;}catch(err){$err=err;}finally{$callDeferred($deferred,$err);}};G.prototype.SetPrefix=function(a){return this.$val.SetPrefix(a);};G.ptr.prototype.Writer=function(){var a,$deferred;var $err=null;try{$deferred=[];$deferred.index=$curGoroutine.deferStack.length;$curGoroutine.deferStack.push($deferred);a=this;a.mu.Lock();$deferred.push([$methodVal(a.mu,"Unlock"),[]]);return a.out;}catch(err){$err=err;return $ifaceNil;}finally{$callDeferred($deferred,$err);}};G.prototype.Writer=function(){return this.$val.Writer();};R=function(a){var a,b,c,d,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;b=$f.b;c=$f.c;d=$f.d;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=A.Sprintln(a);$s=1;case 1:if($c){$c=false;b=b.$blk();}if(b&&b.$blk!==undefined){break s;}c=b;d=I.Output(2,c);$s=2;case 2:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}d;$s=-1;return;}return;}if($f===undefined){$f={$blk:R};}$f.a=a;$f.b=b;$f.c=c;$f.d=d;$f.$s=$s;$f.$r=$r;return $f;};$pkg.Println=R;AD.methods=[{prop:"SetOutput",name:"SetOutput",pkg:"",typ:$funcType([B.Writer],[],false)},{prop:"formatHeader",name:"formatHeader",pkg:"log",typ:$funcType([AB,F.Time,$String,$Int],[],false)},{prop:"Output",name:"Output",pkg:"",typ:$funcType([$Int,$String],[$error],false)},{prop:"Printf",name:"Printf",pkg:"",typ:$funcType([$String,AC],[],true)},{prop:"Print",name:"Print",pkg:"",typ:$funcType([AC],[],true)},{prop:"Println",name:"Println",pkg:"",typ:$funcType([AC],[],true)},{prop:"Fatal",name:"Fatal",pkg:"",typ:$funcType([AC],[],true)},{prop:"Fatalf",name:"Fatalf",pkg:"",typ:$funcType([$String,AC],[],true)},{prop:"Fatalln",name:"Fatalln",pkg:"",typ:$funcType([AC],[],true)},{prop:"Panic",name:"Panic",pkg:"",typ:$funcType([AC],[],true)},{prop:"Panicf",name:"Panicf",pkg:"",typ:$funcType([$String,AC],[],true)},{prop:"Panicln",name:"Panicln",pkg:"",typ:$funcType([AC],[],true)},{prop:"Flags",name:"Flags",pkg:"",typ:$funcType([],[$Int],false)},{prop:"SetFlags",name:"SetFlags",pkg:"",typ:$funcType([$Int],[],false)},{prop:"Prefix",name:"Prefix",pkg:"",typ:$funcType([],[$String],false)},{prop:"SetPrefix",name:"SetPrefix",pkg:"",typ:$funcType([$String],[],false)},{prop:"Writer",name:"Writer",pkg:"",typ:$funcType([],[B.Writer],false)}];G.init("log",[{prop:"mu",name:"mu",embedded:false,exported:false,typ:E.Mutex,tag:""},{prop:"prefix",name:"prefix",embedded:false,exported:false,typ:$String,tag:""},{prop:"flag",name:"flag",embedded:false,exported:false,typ:$Int,tag:""},{prop:"out",name:"out",embedded:false,exported:false,typ:B.Writer,tag:""},{prop:"buf",name:"buf",embedded:false,exported:false,typ:Z,tag:""}]);$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=F.$init();$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}I=H(C.Stderr,"",3);}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $packages["github.com/v2fly/BrowserBridge/bridge/bridgejs"]=(function(){var $pkg={},$init,A,B,C,D,E,G,F;A=$packages["fmt"];B=$packages["github.com/gopherjs/gopherjs/js"];C=$packages["github.com/v2fly/BrowserBridge/bridge"];D=$packages["log"];E=$packages["net/url"];G=$sliceType($emptyInterface);F=function(){var a,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;a=$f.a;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:a=A.Println(new G([new $String("V3")]));$s=1;case 1:if($c){$c=false;a=a.$blk();}if(a&&a.$blk!==undefined){break s;}a;$go((function $b(){var b,c,d,e,f,g,h,i,$s,$r;$s=0;var $f,$c=false;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;b=$f.b;c=$f.c;d=$f.d;e=$f.e;f=$f.f;g=$f.g;h=$f.h;i=$f.i;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:b=$internalize($global.window.location.href,$String);d=E.Parse(b);$s=1;case 1:if($c){$c=false;d=d.$blk();}if(d&&d.$blk!==undefined){break s;}c=d;e=c[0];f=c[1];if(!($interfaceIsEqual(f,$ifaceNil))){$s=2;continue;}$s=3;continue;case 2:$r=D.Println(new G([f]));$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}case 3:g=e.Host;h=A.Sprintf("ws://%v/link",new G([new $String(g)]));$s=5;case 5:if($c){$c=false;h=h.$blk();}if(h&&h.$blk!==undefined){break s;}$r=D.Println(new G([new $String(h)]));$s=6;case 6:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}i=A.Sprintf("ws://%v/link",new G([new $String(g)]));$s=7;case 7:if($c){$c=false;i=i.$blk();}if(i&&i.$blk!==undefined){break s;}$r=C.Bridge(new C.Settings.ptr(i));$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$global.location.reload();$s=-1;return;}return;}if($f===undefined){$f={$blk:$b};}$f.b=b;$f.c=c;$f.d=d;$f.e=e;$f.f=f;$f.g=g;$f.h=h;$f.i=i;$f.$s=$s;$f.$r=$r;return $f;}),[]);$s=-1;return;}return;}if($f===undefined){$f={$blk:F};}$f.a=a;$f.$s=$s;$f.$r=$r;return $f;};$init=function(){$pkg.$init=function(){};var $f,$c=false,$s=0,$r;if(this!==undefined&&this.$blk!==undefined){$f=this;$c=true;$s=$f.$s;$r=$f.$r;}s:while(true){switch($s){case 0:$r=A.$init();$s=1;case 1:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=B.$init();$s=2;case 2:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=C.$init();$s=3;case 3:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=D.$init();$s=4;case 4:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$r=E.$init();$s=5;case 5:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}if($pkg===$mainPkg){$s=6;continue;}$s=7;continue;case 6:$r=F();$s=8;case 8:if($c){$c=false;$r=$r.$blk();}if($r&&$r.$blk!==undefined){break s;}$mainFinished=true;case 7:}return;}if($f===undefined){$f={$blk:$init};}$f.$s=$s;$f.$r=$r;return $f;};$pkg.$init=$init;return $pkg;})(); $synthesizeMethods(); var $mainPkg = $packages["github.com/v2fly/BrowserBridge/bridge/bridgejs"]; $packages["runtime"].$init(); $go($mainPkg.$init, []); $flushConsole(); }).call(this); //# sourceMappingURL=bridgejs.js.map ================================================ FILE: release/friendly-filenames.json ================================================ { "darwin-amd64": { "friendlyName": "macos-64" }, "darwin-arm64": { "friendlyName": "macos-arm64-v8a" }, "dragonfly-amd64": { "friendlyName": "dragonfly-64" }, "freebsd-386": { "friendlyName": "freebsd-32" }, "freebsd-amd64": { "friendlyName": "freebsd-64" }, "freebsd-arm6": { "friendlyName": "freebsd-arm32-v6" }, "freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" }, "freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" }, "linux-386": { "friendlyName": "linux-32" }, "linux-amd64": { "friendlyName": "linux-64" }, "linux-amd64pie": { "friendlyName": "linux-64-pie" }, "linux-arm5": { "friendlyName": "linux-arm32-v5" }, "linux-arm64": { "friendlyName": "linux-arm64-v8a" }, "linux-arm64pie": { "friendlyName": "linux-arm64-v8a-pie" }, "linux-arm6": { "friendlyName": "linux-arm32-v6" }, "linux-arm7": { "friendlyName": "linux-arm32-v7a" }, "linux-mips64le": { "friendlyName": "linux-mips64le" }, "linux-mips64": { "friendlyName": "linux-mips64" }, "linux-mipsle": { "friendlyName": "linux-mips32le" }, "linux-mips": { "friendlyName": "linux-mips32" }, "linux-riscv64": { "friendlyName": "linux-riscv64" }, "linux-loong64": { "friendlyName": "linux-loong64" }, "openbsd-386": { "friendlyName": "openbsd-32" }, "openbsd-amd64": { "friendlyName": "openbsd-64" }, "openbsd-arm6": { "friendlyName": "openbsd-arm32-v6" }, "openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" }, "openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" }, "windows-amd64": { "friendlyName": "windows-64" }, "windows-386": { "friendlyName": "windows-32" }, "windows-arm64": { "friendlyName": "windows-arm64-v8a" }, "windows-arm7": { "friendlyName": "windows-arm32-v7a" }, "android-arm64": { "friendlyName": "android-arm64-v8a" } } ================================================ FILE: release/install-release.sh ================================================ #!/usr/bin/env bash # This file is accessible as https://install.direct/go.sh # Original source is located at github.com/v2fly/v2ray-core/release/install-release.sh # If not specify, default meaning of return value: # 0: Success # 1: System error # 2: Application error # 3: Network error #######color code######## RED="31m" # Error message YELLOW="33m" # Warning message colorEcho(){ echo -e "\033[${1}${@:2}\033[0m" 1>& 2 } colorEcho ${RED} "ERROR: This script has been DISCARDED, please switch to fhs-install-v2ray project." colorEcho ${YELLOW} "HOW TO USE: https://github.com/v2fly/fhs-install-v2ray" colorEcho ${YELLOW} "TO MIGRATE: https://github.com/v2fly/fhs-install-v2ray/wiki/Migrate-from-the-old-script-to-this" exit 255 ================================================ FILE: release/requestsign.sh ================================================ #!/usr/bin/env bash RELEASE_DATA=$(curl --data "version=${SIGN_VERSION}" --data "password=${SIGN_SERVICE_PASSWORD}" -X POST "${SIGN_SERIVCE_URL}" ) echo $RELEASE_DATA RELEASE_ID=$(echo $RELEASE_DATA| jq -r ".id") function uploadfile() { FILE=$1 CTYPE=$(file -b --mime-type $FILE) sleep 1 curl -H "Authorization: token ${GITHUB_TOKEN}" -H "Content-Type: ${CTYPE}" --data-binary @$FILE "https://uploads.github.com/repos/v2fly/v2ray-core/releases/${RELEASE_ID}/assets?name=$(basename $FILE)" sleep 1 } function upload() { FILE=$1 DGST=$1.dgst openssl dgst -md5 $FILE | sed 's/([^)]*)//g' >> $DGST openssl dgst -sha1 $FILE | sed 's/([^)]*)//g' >> $DGST openssl dgst -sha256 $FILE | sed 's/([^)]*)//g' >> $DGST openssl dgst -sha512 $FILE | sed 's/([^)]*)//g' >> $DGST uploadfile $FILE uploadfile $DGST } curl "https://raw.githubusercontent.com/v2fly/Release/master/v2fly/${SIGN_VERSION}.Release" > Release upload Release ================================================ FILE: release/user-package.sh ================================================ #!/usr/bin/env bash set -o errexit set -o pipefail set -o nounset # set -o xtrace trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; exit 1' ERR NOW=$(date '+%Y%m%d-%H%M%S') TMP=$(mktemp -d) SRCDIR=$(pwd) CODENAME="user" BUILDNAME=$NOW cleanup() { rm -rf "$TMP"; } trap cleanup INT TERM ERR get_source() { echo ">>> Clone v2fly/v2ray-core repo..." git clone https://github.com/v2fly/v2ray-core.git cd v2ray-core go mod download } build_v2() { if [[ $nosource != 1 ]]; then cd ${SRCDIR}/v2ray-core local VERSIONTAG=$(git describe --abbrev=0 --tags) else echo ">>> Use current directory as WORKDIR" local VERSIONTAG=$(git describe --abbrev=0 --tags) fi LDFLAGS="-s -w -buildid= -X github.com/v2fly/v2ray-core/v5.codename=${CODENAME} -X github.com/v2fly/v2ray-core/v5.build=${BUILDNAME} -X github.com/v2fly/v2ray-core/v5.version=${VERSIONTAG}" echo ">>> Compile v2ray ..." env CGO_ENABLED=0 go build -o "$TMP"/v2ray"${EXESUFFIX}" -ldflags "$LDFLAGS" ./main } build_dat() { echo ">>> Download latest geoip.dat" curl -s -L -o "$TMP"/geoip.dat "https://github.com/v2fly/geoip/raw/release/geoip.dat" echo ">>> Download latest geoip-only-cn-private.dat" curl -s -L -o "$TMP"/geoip-only-cn-private.dat "https://github.com/v2fly/geoip/raw/release/geoip-only-cn-private.dat" echo ">>> Download latest geosite.dat" curl -s -L -o "$TMP"/geosite.dat "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat" } copyconf() { echo ">>> Copying config..." cd ./release/config if [[ $GOOS == "linux" ]]; then tar c --exclude "*.dat" . | tar x -C "$TMP" else tar c --exclude "*.dat" --exclude "systemd/**" . | tar x -C "$TMP" fi } packzip() { echo ">>> Generating zip package" cd "$TMP" local PKG=${SRCDIR}/v2ray-custom-${GOARCH}-${GOOS}-${PKGSUFFIX}${NOW}.zip zip -r "$PKG" . echo ">>> Generated: $(basename "$PKG") at $(dirname "$PKG")" } packtgz() { echo ">>> Generating tgz package" cd "$TMP" local PKG=${SRCDIR}/v2ray-custom-${GOARCH}-${GOOS}-${PKGSUFFIX}${NOW}.tar.gz tar cvfz "$PKG" . echo ">>> Generated: $(basename "$PKG") at $(dirname "$PKG")" } packtgzAbPath() { local ABPATH="$1" echo ">>> Generating tgz package at $ABPATH" cd "$TMP" tar cvfz "$ABPATH" . echo ">>> Generated: $ABPATH" } pkg=zip nosource=0 nodat=0 noconf=0 GOOS=linux GOARCH=amd64 EXESUFFIX= PKGSUFFIX= for arg in "$@"; do case $arg in 386 | arm* | mips* | ppc64* | riscv64 | loong64 | s390x) GOARCH=$arg ;; windows) GOOS=$arg EXESUFFIX=.exe ;; darwin | dragonfly | freebsd | openbsd) GOOS=$arg ;; nodat) nodat=1 PKGSUFFIX=${PKGSUFFIX}nodat- ;; noconf) noconf=1 ;; nosource) nosource=1 ;; tgz) pkg=tgz ;; abpathtgz=*) pkg=${arg##abpathtgz=} ;; codename=*) CODENAME=${arg##codename=} ;; buildname=*) BUILDNAME=${arg##buildname=} ;; esac done if [[ $nosource != 1 ]]; then get_source fi export GOOS GOARCH echo "Build ARGS: GOOS=${GOOS} GOARCH=${GOARCH} CODENAME=${CODENAME} BUILDNAME=${BUILDNAME}" echo "PKG ARGS: pkg=${pkg}" build_v2 if [[ $nodat != 1 ]]; then build_dat fi if [[ $noconf != 1 ]]; then copyconf fi if [[ $pkg == "zip" ]]; then packzip elif [[ $pkg == "tgz" ]]; then packtgz else packtgzAbPath "$pkg" fi cleanup ================================================ FILE: testing/mocks/dns.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/v2fly/v2ray-core/v5/features/dns (interfaces: Client) // Package mocks is a generated GoMock package. package mocks import ( net "net" reflect "reflect" gomock "github.com/golang/mock/gomock" ) // DNSClient is a mock of Client interface. type DNSClient struct { ctrl *gomock.Controller recorder *DNSClientMockRecorder } // DNSClientMockRecorder is the mock recorder for DNSClient. type DNSClientMockRecorder struct { mock *DNSClient } // NewDNSClient creates a new mock instance. func NewDNSClient(ctrl *gomock.Controller) *DNSClient { mock := &DNSClient{ctrl: ctrl} mock.recorder = &DNSClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *DNSClient) EXPECT() *DNSClientMockRecorder { return m.recorder } // Close mocks base method. func (m *DNSClient) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *DNSClientMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*DNSClient)(nil).Close)) } // LookupIP mocks base method. func (m *DNSClient) LookupIP(arg0 string) ([]net.IP, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LookupIP", arg0) ret0, _ := ret[0].([]net.IP) ret1, _ := ret[1].(error) return ret0, ret1 } // LookupIP indicates an expected call of LookupIP. func (mr *DNSClientMockRecorder) LookupIP(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIP", reflect.TypeOf((*DNSClient)(nil).LookupIP), arg0) } // Start mocks base method. func (m *DNSClient) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *DNSClientMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*DNSClient)(nil).Start)) } // Type mocks base method. func (m *DNSClient) Type() interface{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Type") ret0, _ := ret[0].(interface{}) return ret0 } // Type indicates an expected call of Type. func (mr *DNSClientMockRecorder) Type() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*DNSClient)(nil).Type)) } ================================================ FILE: testing/mocks/io.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: io (interfaces: Reader,Writer) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" ) // Reader is a mock of Reader interface. type Reader struct { ctrl *gomock.Controller recorder *ReaderMockRecorder } // ReaderMockRecorder is the mock recorder for Reader. type ReaderMockRecorder struct { mock *Reader } // NewReader creates a new mock instance. func NewReader(ctrl *gomock.Controller) *Reader { mock := &Reader{ctrl: ctrl} mock.recorder = &ReaderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *Reader) EXPECT() *ReaderMockRecorder { return m.recorder } // Read mocks base method. func (m *Reader) Read(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Read indicates an expected call of Read. func (mr *ReaderMockRecorder) Read(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*Reader)(nil).Read), arg0) } // Writer is a mock of Writer interface. type Writer struct { ctrl *gomock.Controller recorder *WriterMockRecorder } // WriterMockRecorder is the mock recorder for Writer. type WriterMockRecorder struct { mock *Writer } // NewWriter creates a new mock instance. func NewWriter(ctrl *gomock.Controller) *Writer { mock := &Writer{ctrl: ctrl} mock.recorder = &WriterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *Writer) EXPECT() *WriterMockRecorder { return m.recorder } // Write mocks base method. func (m *Writer) Write(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Write indicates an expected call of Write. func (mr *WriterMockRecorder) Write(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*Writer)(nil).Write), arg0) } ================================================ FILE: testing/mocks/log.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/v2fly/v2ray-core/v5/common/log (interfaces: Handler) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" log "github.com/v2fly/v2ray-core/v5/common/log" ) // LogHandler is a mock of Handler interface. type LogHandler struct { ctrl *gomock.Controller recorder *LogHandlerMockRecorder } // LogHandlerMockRecorder is the mock recorder for LogHandler. type LogHandlerMockRecorder struct { mock *LogHandler } // NewLogHandler creates a new mock instance. func NewLogHandler(ctrl *gomock.Controller) *LogHandler { mock := &LogHandler{ctrl: ctrl} mock.recorder = &LogHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *LogHandler) EXPECT() *LogHandlerMockRecorder { return m.recorder } // Handle mocks base method. func (m *LogHandler) Handle(arg0 log.Message) { m.ctrl.T.Helper() m.ctrl.Call(m, "Handle", arg0) } // Handle indicates an expected call of Handle. func (mr *LogHandlerMockRecorder) Handle(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Handle", reflect.TypeOf((*LogHandler)(nil).Handle), arg0) } ================================================ FILE: testing/mocks/mux.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/v2fly/v2ray-core/v5/common/mux (interfaces: ClientWorkerFactory) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" mux "github.com/v2fly/v2ray-core/v5/common/mux" ) // MuxClientWorkerFactory is a mock of ClientWorkerFactory interface. type MuxClientWorkerFactory struct { ctrl *gomock.Controller recorder *MuxClientWorkerFactoryMockRecorder } // MuxClientWorkerFactoryMockRecorder is the mock recorder for MuxClientWorkerFactory. type MuxClientWorkerFactoryMockRecorder struct { mock *MuxClientWorkerFactory } // NewMuxClientWorkerFactory creates a new mock instance. func NewMuxClientWorkerFactory(ctrl *gomock.Controller) *MuxClientWorkerFactory { mock := &MuxClientWorkerFactory{ctrl: ctrl} mock.recorder = &MuxClientWorkerFactoryMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MuxClientWorkerFactory) EXPECT() *MuxClientWorkerFactoryMockRecorder { return m.recorder } // Create mocks base method. func (m *MuxClientWorkerFactory) Create() (*mux.ClientWorker, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Create") ret0, _ := ret[0].(*mux.ClientWorker) ret1, _ := ret[1].(error) return ret0, ret1 } // Create indicates an expected call of Create. func (mr *MuxClientWorkerFactoryMockRecorder) Create() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MuxClientWorkerFactory)(nil).Create)) } ================================================ FILE: testing/mocks/outbound.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/v2fly/v2ray-core/v5/features/outbound (interfaces: Manager,HandlerSelector) // Package mocks is a generated GoMock package. package mocks import ( context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" outbound "github.com/v2fly/v2ray-core/v5/features/outbound" ) // OutboundManager is a mock of Manager interface. type OutboundManager struct { ctrl *gomock.Controller recorder *OutboundManagerMockRecorder } // OutboundManagerMockRecorder is the mock recorder for OutboundManager. type OutboundManagerMockRecorder struct { mock *OutboundManager } // NewOutboundManager creates a new mock instance. func NewOutboundManager(ctrl *gomock.Controller) *OutboundManager { mock := &OutboundManager{ctrl: ctrl} mock.recorder = &OutboundManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *OutboundManager) EXPECT() *OutboundManagerMockRecorder { return m.recorder } // AddHandler mocks base method. func (m *OutboundManager) AddHandler(arg0 context.Context, arg1 outbound.Handler) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddHandler", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // AddHandler indicates an expected call of AddHandler. func (mr *OutboundManagerMockRecorder) AddHandler(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddHandler", reflect.TypeOf((*OutboundManager)(nil).AddHandler), arg0, arg1) } // Close mocks base method. func (m *OutboundManager) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *OutboundManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*OutboundManager)(nil).Close)) } // GetDefaultHandler mocks base method. func (m *OutboundManager) GetDefaultHandler() outbound.Handler { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDefaultHandler") ret0, _ := ret[0].(outbound.Handler) return ret0 } // GetDefaultHandler indicates an expected call of GetDefaultHandler. func (mr *OutboundManagerMockRecorder) GetDefaultHandler() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultHandler", reflect.TypeOf((*OutboundManager)(nil).GetDefaultHandler)) } // GetHandler mocks base method. func (m *OutboundManager) GetHandler(arg0 string) outbound.Handler { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHandler", arg0) ret0, _ := ret[0].(outbound.Handler) return ret0 } // GetHandler indicates an expected call of GetHandler. func (mr *OutboundManagerMockRecorder) GetHandler(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandler", reflect.TypeOf((*OutboundManager)(nil).GetHandler), arg0) } // RemoveHandler mocks base method. func (m *OutboundManager) RemoveHandler(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveHandler", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RemoveHandler indicates an expected call of RemoveHandler. func (mr *OutboundManagerMockRecorder) RemoveHandler(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveHandler", reflect.TypeOf((*OutboundManager)(nil).RemoveHandler), arg0, arg1) } // Start mocks base method. func (m *OutboundManager) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") ret0, _ := ret[0].(error) return ret0 } // Start indicates an expected call of Start. func (mr *OutboundManagerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*OutboundManager)(nil).Start)) } // Type mocks base method. func (m *OutboundManager) Type() interface{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Type") ret0, _ := ret[0].(interface{}) return ret0 } // Type indicates an expected call of Type. func (mr *OutboundManagerMockRecorder) Type() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*OutboundManager)(nil).Type)) } // OutboundHandlerSelector is a mock of HandlerSelector interface. type OutboundHandlerSelector struct { ctrl *gomock.Controller recorder *OutboundHandlerSelectorMockRecorder } // OutboundHandlerSelectorMockRecorder is the mock recorder for OutboundHandlerSelector. type OutboundHandlerSelectorMockRecorder struct { mock *OutboundHandlerSelector } // NewOutboundHandlerSelector creates a new mock instance. func NewOutboundHandlerSelector(ctrl *gomock.Controller) *OutboundHandlerSelector { mock := &OutboundHandlerSelector{ctrl: ctrl} mock.recorder = &OutboundHandlerSelectorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *OutboundHandlerSelector) EXPECT() *OutboundHandlerSelectorMockRecorder { return m.recorder } // Select mocks base method. func (m *OutboundHandlerSelector) Select(arg0 []string) []string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Select", arg0) ret0, _ := ret[0].([]string) return ret0 } // Select indicates an expected call of Select. func (mr *OutboundHandlerSelectorMockRecorder) Select(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*OutboundHandlerSelector)(nil).Select), arg0) } ================================================ FILE: testing/mocks/proxy.go ================================================ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/v2fly/v2ray-core/v5/proxy (interfaces: Inbound,Outbound) // Package mocks is a generated GoMock package. package mocks import ( context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" net "github.com/v2fly/v2ray-core/v5/common/net" routing "github.com/v2fly/v2ray-core/v5/features/routing" transport "github.com/v2fly/v2ray-core/v5/transport" internet "github.com/v2fly/v2ray-core/v5/transport/internet" ) // ProxyInbound is a mock of Inbound interface. type ProxyInbound struct { ctrl *gomock.Controller recorder *ProxyInboundMockRecorder } // ProxyInboundMockRecorder is the mock recorder for ProxyInbound. type ProxyInboundMockRecorder struct { mock *ProxyInbound } // NewProxyInbound creates a new mock instance. func NewProxyInbound(ctrl *gomock.Controller) *ProxyInbound { mock := &ProxyInbound{ctrl: ctrl} mock.recorder = &ProxyInboundMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *ProxyInbound) EXPECT() *ProxyInboundMockRecorder { return m.recorder } // Network mocks base method. func (m *ProxyInbound) Network() []net.Network { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Network") ret0, _ := ret[0].([]net.Network) return ret0 } // Network indicates an expected call of Network. func (mr *ProxyInboundMockRecorder) Network() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Network", reflect.TypeOf((*ProxyInbound)(nil).Network)) } // Process mocks base method. func (m *ProxyInbound) Process(arg0 context.Context, arg1 net.Network, arg2 internet.Connection, arg3 routing.Dispatcher) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Process", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // Process indicates an expected call of Process. func (mr *ProxyInboundMockRecorder) Process(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*ProxyInbound)(nil).Process), arg0, arg1, arg2, arg3) } // ProxyOutbound is a mock of Outbound interface. type ProxyOutbound struct { ctrl *gomock.Controller recorder *ProxyOutboundMockRecorder } // ProxyOutboundMockRecorder is the mock recorder for ProxyOutbound. type ProxyOutboundMockRecorder struct { mock *ProxyOutbound } // NewProxyOutbound creates a new mock instance. func NewProxyOutbound(ctrl *gomock.Controller) *ProxyOutbound { mock := &ProxyOutbound{ctrl: ctrl} mock.recorder = &ProxyOutboundMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *ProxyOutbound) EXPECT() *ProxyOutboundMockRecorder { return m.recorder } // Process mocks base method. func (m *ProxyOutbound) Process(arg0 context.Context, arg1 *transport.Link, arg2 internet.Dialer) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Process", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // Process indicates an expected call of Process. func (mr *ProxyOutboundMockRecorder) Process(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*ProxyOutbound)(nil).Process), arg0, arg1, arg2) } ================================================ FILE: testing/scenarios/cert/self-signed_cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIBhDCCASmgAwIBAgIQOGW77bhKIQBVcKBkruQEjDAKBggqhkjOPQQDAjAoMRIw EAYDVQQKEwlWMlJheSBJbmMxEjAQBgNVBAMTCVYyUmF5IEluYzAeFw0yNDA5MTUx NjIyMjRaFw0yNDEyMTQxNzIyMjRaMCgxEjAQBgNVBAoTCVYyUmF5IEluYzESMBAG A1UEAxMJVjJSYXkgSW5jMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmKTCe3pJ 6qYR8JSt4LHurI9ukGQISTBBLrFw8fDsWTeJielnHZdqgKL9swcC+IF/ikjzbT5W iP0EGUBGk6wbPKM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF BwMBMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwIDSQAwRgIhAKIZM1xlaHwSHsZP V6aN+AbWnoRgwVVJuY4I5q17FY0cAiEAiLr9NkHd9glFz0BzALygvo7gnNpgF69l DdiZ4828MXY= -----END CERTIFICATE----- ================================================ FILE: testing/scenarios/cert/self-signed_key.pem ================================================ -----BEGIN RSA PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmkOATgwafiG4sDs/ J1xVZfimvgREFJy0mtZHeGM2O6ehRANCAASYpMJ7eknqphHwlK3gse6sj26QZAhJ MEEusXDx8OxZN4mJ6Wcdl2qAov2zBwL4gX+KSPNtPlaI/QQZQEaTrBs8 -----END RSA PRIVATE KEY----- ================================================ FILE: testing/scenarios/command_test.go ================================================ package scenarios import ( "context" "fmt" "io" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/commander" "github.com/v2fly/v2ray-core/v5/app/policy" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/proxyman/command" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/stats" statscmd "github.com/v2fly/v2ray-core/v5/app/stats/command" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" ) func TestCommanderRemoveHandler(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() clientPort := tcp.PickPort() cmdPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&commander.Config{ Tag: "api", Service: []*anypb.Any{ serial.ToTypedMessage(&command.Config{}), }, }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { InboundTag: []string{"api"}, TargetTag: &router.RoutingRule_Tag{ Tag: "api", }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { Tag: "d", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), Networks: []net.Network{net.Network_TCP}, }), }, { Tag: "api", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(cmdPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), Networks: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { Tag: "default-outbound", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil { t.Fatal(err) } cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) common.Must(err) defer cmdConn.Close() hsClient := command.NewHandlerServiceClient(cmdConn) resp, err := hsClient.RemoveInbound(context.Background(), &command.RemoveInboundRequest{ Tag: "d", }) common.Must(err) if resp == nil { t.Error("unexpected nil response") } { _, err := net.DialTCP("tcp", nil, &net.TCPAddr{ IP: []byte{127, 0, 0, 1}, Port: int(clientPort), }) if err == nil { t.Error("unexpected nil error") } } } func TestCommanderAddRemoveUser(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() u1 := protocol.NewID(uuid.New()) u2 := protocol.NewID(uuid.New()) cmdPort := tcp.PickPort() serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&commander.Config{ Tag: "api", Service: []*anypb.Any{ serial.ToTypedMessage(&command.Config{}), }, }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { InboundTag: []string{"api"}, TargetTag: &router.RoutingRule_Tag{ Tag: "api", }, }, }, }), serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { Tag: "v", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: u1.String(), AlterId: 0, }), }, }, }), }, { Tag: "api", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(cmdPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), Networks: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { Tag: "d", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: u2.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != io.EOF && /*We might wish to drain the connection*/ (err != nil && !strings.HasSuffix(err.Error(), "i/o timeout")) { t.Fatal("expected error: ", err) } cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) common.Must(err) defer cmdConn.Close() hsClient := command.NewHandlerServiceClient(cmdConn) resp, err := hsClient.AlterInbound(context.Background(), &command.AlterInboundRequest{ Tag: "v", Operation: serial.ToTypedMessage( &command.AddUserOperation{ User: &protocol.User{ Email: "test@v2fly.org", Account: serial.ToTypedMessage(&vmess.Account{ Id: u2.String(), AlterId: 0, }), }, }), }) common.Must(err) if resp == nil { t.Fatal("nil response") } if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil { t.Fatal(err) } resp, err = hsClient.AlterInbound(context.Background(), &command.AlterInboundRequest{ Tag: "v", Operation: serial.ToTypedMessage(&command.RemoveUserOperation{Email: "test@v2fly.org"}), }) common.Must(err) if resp == nil { t.Fatal("nil response") } } func TestCommanderStats(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() cmdPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&stats.Config{}), serial.ToTypedMessage(&commander.Config{ Tag: "api", Service: []*anypb.Any{ serial.ToTypedMessage(&statscmd.Config{}), }, }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { InboundTag: []string{"api"}, TargetTag: &router.RoutingRule_Tag{ Tag: "api", }, }, }, }), serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, }, }, 1: { Stats: &policy.Policy_Stats{ UserUplink: true, UserDownlink: true, }, }, }, System: &policy.SystemPolicy{ Stats: &policy.SystemPolicy_Stats{ InboundUplink: true, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { Tag: "vmess", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Level: 1, Email: "test", Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, { Tag: "api", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(cmdPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to create all servers", err) } defer CloseAllServers(servers) if err := testTCPConn(clientPort, 10240*1024, time.Second*20)(); err != nil { t.Fatal(err) } cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) common.Must(err) defer cmdConn.Close() const name = "user>>>test>>>traffic>>>uplink" sClient := statscmd.NewStatsServiceClient(cmdConn) sresp, err := sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{ Name: name, Reset_: true, }) common.Must(err) if r := cmp.Diff(sresp.Stat, &statscmd.Stat{ Name: name, Value: 10240 * 1024, }, cmpopts.IgnoreUnexported(statscmd.Stat{})); r != "" { t.Error(r) } sresp, err = sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{ Name: name, }) common.Must(err) if r := cmp.Diff(sresp.Stat, &statscmd.Stat{ Name: name, Value: 0, }, cmpopts.IgnoreUnexported(statscmd.Stat{})); r != "" { t.Error(r) } sresp, err = sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{ Name: "inbound>>>vmess>>>traffic>>>uplink", Reset_: true, }) common.Must(err) if sresp.Stat.Value <= 10240*1024 { t.Error("value < 10240*1024: ", sresp.Stat.Value) } } ================================================ FILE: testing/scenarios/common.go ================================================ package scenarios import ( "bytes" "crypto/rand" "fmt" "io" "os" "os/exec" "path/filepath" "runtime" "sync" "syscall" "time" "golang.org/x/net/proxy" "google.golang.org/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/retry" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/units" ) func xor(b []byte) []byte { r := make([]byte, len(b)) for i, v := range b { r[i] = v ^ 'c' } return r } func readFrom(conn net.Conn, timeout time.Duration, length int) []byte { b := make([]byte, length) deadline := time.Now().Add(timeout) conn.SetReadDeadline(deadline) n, err := io.ReadFull(conn, b[:length]) if err != nil { fmt.Println("Unexpected error from readFrom:", err) } return b[:n] } func readFrom2(conn net.Conn, timeout time.Duration, length int) ([]byte, error) { b := make([]byte, length) deadline := time.Now().Add(timeout) conn.SetReadDeadline(deadline) n, err := io.ReadFull(conn, b[:length]) if err != nil { return nil, err } return b[:n], nil } func InitializeServerConfigs(configs ...*core.Config) ([]*exec.Cmd, error) { servers := make([]*exec.Cmd, 0, 10) for _, config := range configs { server, err := InitializeServerConfig(config) if err != nil { CloseAllServers(servers) return nil, err } servers = append(servers, server) } time.Sleep(time.Second * 2) return servers, nil } func InitializeServerConfig(config *core.Config) (*exec.Cmd, error) { err := BuildV2Ray() if err != nil { return nil, err } config = withDefaultApps(config) configBytes, err := proto.Marshal(config) if err != nil { return nil, err } proc := RunV2RayProtobuf(configBytes) if err := proc.Start(); err != nil { return nil, err } return proc, nil } var ( testBinaryPath string testBinaryPathGen sync.Once ) func genTestBinaryPath() { testBinaryPathGen.Do(func() { var tempDir string common.Must(retry.Timed(5, 100).On(func() error { dir, err := os.MkdirTemp("", "v2ray") if err != nil { return err } tempDir = dir return nil })) file := filepath.Join(tempDir, "v2ray.test") if runtime.GOOS == "windows" { file += ".exe" } testBinaryPath = file fmt.Printf("Generated binary path: %s\n", file) }) } func GetSourcePath() string { return filepath.Join("github.com", "v2fly", "v2ray-core", "v5", "main") } func CloseAllServers(servers []*exec.Cmd) { log.Record(&log.GeneralMessage{ Severity: log.Severity_Info, Content: "Closing all servers.", }) for _, server := range servers { if runtime.GOOS == "windows" { server.Process.Kill() } else { server.Process.Signal(syscall.SIGTERM) } } for _, server := range servers { server.Process.Wait() } log.Record(&log.GeneralMessage{ Severity: log.Severity_Info, Content: "All server closed.", }) } func CloseServer(server *exec.Cmd) { log.Record(&log.GeneralMessage{ Severity: log.Severity_Info, Content: "Closing server.", }) if runtime.GOOS == "windows" { server.Process.Kill() } else { server.Process.Signal(syscall.SIGTERM) } server.Process.Wait() log.Record(&log.GeneralMessage{ Severity: log.Severity_Info, Content: "Server closed.", }) } func withDefaultApps(config *core.Config) *core.Config { config.App = append(config.App, serial.ToTypedMessage(&dispatcher.Config{})) config.App = append(config.App, serial.ToTypedMessage(&proxyman.InboundConfig{})) config.App = append(config.App, serial.ToTypedMessage(&proxyman.OutboundConfig{})) return config } func testTCPConnViaSocks(socksPort, testPort net.Port, payloadSize int, timeout time.Duration) func() error { //nolint: unparam return func() error { socksDialer, err := proxy.SOCKS5("tcp", "127.0.0.1:"+socksPort.String(), nil, nil) if err != nil { return err } destAddr := &net.TCPAddr{ IP: []byte{127, 0, 0, 1}, Port: int(testPort), } conn, err := socksDialer.Dial("tcp", destAddr.String()) if err != nil { return err } defer conn.Close() return testTCPConn2(conn, payloadSize, timeout)() } } func testTCPConn(port net.Port, payloadSize int, timeout time.Duration) func() error { return func() error { conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ IP: []byte{127, 0, 0, 1}, Port: int(port), }) if err != nil { return err } defer conn.Close() return testTCPConn2(conn, payloadSize, timeout)() } } func testUDPConn(port net.Port, payloadSize int, timeout time.Duration) func() error { // nolint: unparam return func() error { conn, err := net.DialUDP("udp", nil, &net.UDPAddr{ IP: []byte{127, 0, 0, 1}, Port: int(port), }) if err != nil { return err } defer conn.Close() return testTCPConn2(conn, payloadSize, timeout)() } } func testTCPConn2(conn net.Conn, payloadSize int, timeout time.Duration) func() error { return func() (err1 error) { start := time.Now() defer func() { var m runtime.MemStats runtime.ReadMemStats(&m) // For info on each, see: https://golang.org/pkg/runtime/#MemStats fmt.Println("testConn finishes:", time.Since(start).Milliseconds(), "ms\t", err1, "\tAlloc =", units.ByteSize(m.Alloc).String(), "\tTotalAlloc =", units.ByteSize(m.TotalAlloc).String(), "\tSys =", units.ByteSize(m.Sys).String(), "\tNumGC =", m.NumGC) }() payload := make([]byte, payloadSize) common.Must2(rand.Read(payload)) nBytes, err := conn.Write(payload) if err != nil { return err } if nBytes != len(payload) { return errors.New("expect ", len(payload), " written, but actually ", nBytes) } response, err := readFrom2(conn, timeout, payloadSize) if err != nil { return err } _ = response if r := bytes.Compare(response, xor(payload)); r != 0 { return errors.New(r) } return nil } } ================================================ FILE: testing/scenarios/common_coverage.go ================================================ //go:build coverage // +build coverage package scenarios import ( "bytes" "os" "os/exec" "github.com/v2fly/v2ray-core/v5/common/uuid" ) func BuildV2Ray() error { genTestBinaryPath() if _, err := os.Stat(testBinaryPath); err == nil { return nil } cmd := exec.Command("go", "test", "-tags", "coverage coveragemain", "-coverpkg", "github.com/v2fly/v2ray-core/v5/...", "-c", "-o", testBinaryPath, GetSourcePath()) return cmd.Run() } func RunV2RayProtobuf(config []byte) *exec.Cmd { genTestBinaryPath() covDir := os.Getenv("V2RAY_COV") os.MkdirAll(covDir, os.ModeDir) randomID := uuid.New() profile := randomID.String() + ".out" proc := exec.Command(testBinaryPath, "run", "-format=pb", "-test.run", "TestRunMainForCoverage", "-test.coverprofile", profile, "-test.outputdir", covDir) proc.Stdin = bytes.NewBuffer(config) proc.Stderr = os.Stderr proc.Stdout = os.Stdout return proc } ================================================ FILE: testing/scenarios/common_instanceMgr.go ================================================ package scenarios import ( core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/instman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features/extension" ) func NewInstanceManagerInstanceConfig() *core.Config { config := &core.Config{} config.App = append(config.App, serial.ToTypedMessage(&instman.Config{})) return config } func NewInstanceManagerCoreInstance() (*core.Instance, extension.InstanceManagement) { coreConfig := NewInstanceManagerInstanceConfig() instance, err := core.New(coreConfig) if err != nil { panic(err) } common.Must(instance.Start()) instanceMgr := instance.GetFeature(extension.InstanceManagementType()) InstanceMgrIfce := instanceMgr.(extension.InstanceManagement) return instance, InstanceMgrIfce } ================================================ FILE: testing/scenarios/common_instanceMgr_test.go ================================================ package scenarios import "testing" func TestInstanceMgrInit(t *testing.T) { NewInstanceManagerCoreInstance() } ================================================ FILE: testing/scenarios/common_regular.go ================================================ //go:build !coverage // +build !coverage package scenarios import ( "bytes" "fmt" "os" "os/exec" ) func BuildV2Ray() error { genTestBinaryPath() if _, err := os.Stat(testBinaryPath); err == nil { return nil } fmt.Printf("Building V2Ray into path (%s)\n", testBinaryPath) cmd := exec.Command("go", "build", "-o="+testBinaryPath, GetSourcePath()) return cmd.Run() } func RunV2RayProtobuf(config []byte) *exec.Cmd { genTestBinaryPath() proc := exec.Command(testBinaryPath, "run", "-format=pb") proc.Stdin = bytes.NewBuffer(config) proc.Stderr = os.Stderr proc.Stdout = os.Stdout return proc } ================================================ FILE: testing/scenarios/config/grpc_client.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "vmess", "settings": { "address": "127.0.0.1", "port": 17783, "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" }, "streamSettings": { "transport": "grpc", "transportSettings": { }, "security": "tls", "securitySettings": { "pinnedPeerCertificateChainSha256": [ "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" ], "allowInsecureIfPinnedPeerCertificate": true } } } ], "inbounds": [ { "protocol": "socks", "settings": { "udpEnabled": false, "address": "127.0.0.1", "packetEncoding": "Packet" }, "port": 17784, "listen": "127.0.0.1" } ] } ================================================ FILE: testing/scenarios/config/grpc_server.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "freedom" } ], "inbounds": [ { "listen": "127.0.0.1", "port": 17783, "protocol": "vmess", "settings": { "users": [ "bcc71618-e552-42c2-a2a3-d4c17a9df764" ] }, "streamSettings": { "transport": "grpc", "transportSettings": { }, "security": "tls", "securitySettings": { "certificate": [ { "usage": "ENCIPHERMENT", "certificateFile": "cert/self-signed_cert.pem", "keyFile": "cert/self-signed_key.pem" } ] } } } ] } ================================================ FILE: testing/scenarios/config/grpc_servicename_client.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "vmess", "settings": { "address": "127.0.0.1", "port": 17793, "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" }, "streamSettings": { "transport": "grpc", "transportSettings": { "serviceName": "0eae44595474" }, "security": "tls", "securitySettings": { "pinnedPeerCertificateChainSha256": [ "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" ], "allowInsecureIfPinnedPeerCertificate": true } } } ], "inbounds": [ { "protocol": "socks", "settings": { "udpEnabled": false, "address": "127.0.0.1", "packetEncoding": "Packet" }, "port": 17794, "listen": "127.0.0.1" } ] } ================================================ FILE: testing/scenarios/config/grpc_servicename_server.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "freedom" } ], "inbounds": [ { "listen": "127.0.0.1", "port": 17793, "protocol": "vmess", "settings": { "users": [ "bcc71618-e552-42c2-a2a3-d4c17a9df764" ] }, "streamSettings": { "transport": "grpc", "transportSettings": { "serviceName": "0eae44595474" }, "security": "tls", "securitySettings": { "certificate": [ { "usage": "ENCIPHERMENT", "certificateFile": "cert/self-signed_cert.pem", "keyFile": "cert/self-signed_key.pem" } ] } } } ] } ================================================ FILE: testing/scenarios/config/httpupgrade_client.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "vmess", "settings": { "address": "127.0.0.1", "port": 17793, "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" }, "streamSettings": { "transport": "httpupgrade", "transportSettings": { "path": "b66efc0c7752" }, "security": "tls", "securitySettings": { "pinnedPeerCertificateChainSha256": [ "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" ], "allowInsecureIfPinnedPeerCertificate": true } } } ], "inbounds": [ { "protocol": "socks", "settings": { "udpEnabled": false, "address": "127.0.0.1", "packetEncoding": "Packet" }, "port": 17794, "listen": "127.0.0.1" } ] } ================================================ FILE: testing/scenarios/config/httpupgrade_earlydataShortEarlyData_client.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "vmess", "settings": { "address": "127.0.0.1", "port": 17793, "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" }, "streamSettings": { "transport": "httpupgrade", "transportSettings": { "path": "b66efc0c7752", "maxEarlyData": 32, "earlyDataHeaderName": "Sec-Websocket-Key" }, "security": "tls", "securitySettings": { "pinnedPeerCertificateChainSha256": [ "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" ], "allowInsecureIfPinnedPeerCertificate": true } } } ], "inbounds": [ { "protocol": "socks", "settings": { "udpEnabled": false, "address": "127.0.0.1", "packetEncoding": "Packet" }, "port": 17794, "listen": "127.0.0.1" } ] } ================================================ FILE: testing/scenarios/config/httpupgrade_earlydataShortEarlyData_server.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "freedom" } ], "inbounds": [ { "listen": "127.0.0.1", "port": 17793, "protocol": "vmess", "settings": { "users": [ "bcc71618-e552-42c2-a2a3-d4c17a9df764" ] }, "streamSettings": { "transport": "httpupgrade", "transportSettings": { "path": "b66efc0c7752", "maxEarlyData": 32, "earlyDataHeaderName": "Sec-Websocket-Key" }, "security": "tls", "securitySettings": { "certificate": [ { "usage": "ENCIPHERMENT", "certificateFile": "cert/self-signed_cert.pem", "keyFile": "cert/self-signed_key.pem" } ] } } } ] } ================================================ FILE: testing/scenarios/config/httpupgrade_earlydata_client.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "vmess", "settings": { "address": "127.0.0.1", "port": 17793, "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" }, "streamSettings": { "transport": "httpupgrade", "transportSettings": { "path": "b66efc0c7752", "maxEarlyData": 2048, "earlyDataHeaderName": "Sec-Websocket-Key" }, "security": "tls", "securitySettings": { "pinnedPeerCertificateChainSha256": [ "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" ], "allowInsecureIfPinnedPeerCertificate": true } } } ], "inbounds": [ { "protocol": "socks", "settings": { "udpEnabled": false, "address": "127.0.0.1", "packetEncoding": "Packet" }, "port": 17794, "listen": "127.0.0.1" } ] } ================================================ FILE: testing/scenarios/config/httpupgrade_earlydata_server.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "freedom" } ], "inbounds": [ { "listen": "127.0.0.1", "port": 17793, "protocol": "vmess", "settings": { "users": [ "bcc71618-e552-42c2-a2a3-d4c17a9df764" ] }, "streamSettings": { "transport": "httpupgrade", "transportSettings": { "path": "b66efc0c7752", "maxEarlyData": 2048, "earlyDataHeaderName": "Sec-Websocket-Key" }, "security": "tls", "securitySettings": { "certificate": [ { "usage": "ENCIPHERMENT", "certificateFile": "cert/self-signed_cert.pem", "keyFile": "cert/self-signed_key.pem" } ] } } } ] } ================================================ FILE: testing/scenarios/config/httpupgrade_server.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "freedom" } ], "inbounds": [ { "listen": "127.0.0.1", "port": 17793, "protocol": "vmess", "settings": { "users": [ "bcc71618-e552-42c2-a2a3-d4c17a9df764" ] }, "streamSettings": { "transport": "httpupgrade", "transportSettings": { "path": "b66efc0c7752" }, "security": "tls", "securitySettings": { "certificate": [ { "usage": "ENCIPHERMENT", "certificateFile": "cert/self-signed_cert.pem", "keyFile": "cert/self-signed_key.pem" } ] } } } ] } ================================================ FILE: testing/scenarios/config/meek_client.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "vmess", "settings": { "address": "127.0.0.1", "port": 17773, "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" }, "streamSettings": { "transport": "meek", "transportSettings": { "url": "https://127.0.0.1:17773/mrss48bvxrkfq1qzeqte5o61mmvc9gx6hq51" }, "security": "tls", "securitySettings": { "pinnedPeerCertificateChainSha256": [ "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" ], "allowInsecureIfPinnedPeerCertificate": true } } } ], "inbounds": [ { "protocol": "socks", "settings": { "udpEnabled": false, "address": "127.0.0.1", "packetEncoding": "Packet" }, "port": 17774, "listen": "127.0.0.1" } ] } ================================================ FILE: testing/scenarios/config/meek_server.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "freedom" } ], "inbounds": [ { "listen": "127.0.0.1", "port": 17773, "protocol": "vmess", "settings": { "users": [ "bcc71618-e552-42c2-a2a3-d4c17a9df764" ] }, "streamSettings": { "transport": "meek", "transportSettings": { "url": "http://127.0.0.1:12777" }, "security": "tls", "securitySettings": { "certificate": [ { "usage": "ENCIPHERMENT", "certificateFile": "cert/self-signed_cert.pem", "keyFile": "cert/self-signed_key.pem" } ] } } } ] } ================================================ FILE: testing/scenarios/config/mekya_client.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "vmess", "settings": { "address": "127.0.0.1", "port": 17773, "uuid": "bcc71618-e552-42c2-a2a3-d4c17a9df764" }, "streamSettings": { "transport": "mekya", "transportSettings": { "url": "https://127.0.0.1:17773/mrss48bvxrkfq1qzeqte5o61mmvc9gx6hq51", "maxWriteDelay": 80, "maxRequestSize": 96000, "pollingIntervalInitial": 200, "h2_pool_size": 8, "kcp": { "mtu": { "value": 1450 }, "tti": { "value": 15 }, "uplink_capacity": { "value": 40 }, "downlink_capacity": { "value": 2000 }, "congestion": false, "write_buffer": { "size": 671088640 }, "read_buffer": { "size": 671088640 } } }, "security": "tls", "securitySettings": { "pinnedPeerCertificateChainSha256": [ "kqHyvea27Pn+JiSqA72lhu9IKAKeGR+3yCyA8JR1mug=" ], "allowInsecureIfPinnedPeerCertificate": true } } } ], "inbounds": [ { "protocol": "socks", "settings": { "udpEnabled": false, "address": "127.0.0.1", "packetEncoding": "Packet" }, "port": 17774, "listen": "127.0.0.1" } ] } ================================================ FILE: testing/scenarios/config/mekya_server.json ================================================ { "log": { "error": { "level": "Debug", "type": "Console" }, "access": { "type": "None" } }, "outbounds": [ { "protocol": "freedom" } ], "inbounds": [ { "listen": "127.0.0.1", "port": 17773, "protocol": "vmess", "settings": { "users": [ "bcc71618-e552-42c2-a2a3-d4c17a9df764" ] }, "streamSettings": { "transport": "mekya", "transportSettings": { "url": "http://127.0.0.1:12777", "maxWriteSize": 10485760, "maxWriteDurationMs": 5000, "maxSimultaneousWriteConnection": 128, "packetWritingBuffer": 65536, "kcp": { "mtu": { "value": 1450 }, "tti": { "value": 15 }, "uplink_capacity": { "value": 40 }, "downlink_capacity": { "value": 2000 }, "congestion": false, "write_buffer": { "size": 671088640 }, "read_buffer": { "size": 671088640 } } }, "security": "tls", "securitySettings": { "certificate": [ { "usage": "ENCIPHERMENT", "certificateFile": "cert/self-signed_cert.pem", "keyFile": "cert/self-signed_key.pem" } ] } } } ] } ================================================ FILE: testing/scenarios/dns_test.go ================================================ package scenarios import ( "fmt" "testing" "time" xproxy "golang.org/x/net/proxy" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dns" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/socks" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" ) func TestResolveIP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dns.Config{ Hosts: map[string]*net.IPOrDomain{ "google.com": net.NewIPOrDomain(dest.Address), }, }), serial.ToTypedMessage(&router.Config{ DomainStrategy: router.DomainStrategy_IpIfNonMatch, Rule: []*router.RoutingRule{ { Cidr: []*routercommon.CIDR{ { Ip: []byte{127, 0, 0, 0}, Prefix: 8, }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "direct", }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_NO_AUTH, Accounts: map[string]string{ "Test Account": "Test Password", }, Address: net.NewIPOrDomain(net.LocalHostIP), UdpEnabled: false, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, { Tag: "direct", ProxySettings: serial.ToTypedMessage(&freedom.Config{ DomainStrategy: freedom.Config_USE_IP, }), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { noAuthDialer, err := xproxy.SOCKS5("tcp", net.TCPDestination(net.LocalHostIP, serverPort).NetAddr(), nil, xproxy.Direct) common.Must(err) conn, err := noAuthDialer.Dial("tcp", fmt.Sprintf("google.com:%d", dest.Port)) common.Must(err) defer conn.Close() if err := testTCPConn2(conn, 1024, time.Second*5)(); err != nil { t.Error(err) } } } ================================================ FILE: testing/scenarios/dokodemo_test.go ================================================ package scenarios import ( "testing" "time" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" ) func TestDokodemoTCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := uint32(tcp.PickPort()) clientPortRange := uint32(5) clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{From: clientPort, To: clientPort + clientPortRange}, Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) for port := clientPort; port <= clientPort+clientPortRange; port++ { if err := testTCPConn(net.Port(port), 1024, time.Second*2)(); err != nil { t.Error(err) } } } func TestDokodemoUDP(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, } dest, err := udpServer.Start() common.Must(err) defer udpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := uint32(udp.PickPort()) clientPortRange := uint32(5) clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{From: clientPort, To: clientPort + clientPortRange}, Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_UDP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for port := clientPort; port <= clientPort+clientPortRange; port++ { errg.Go(testUDPConn(net.Port(port), 1024, time.Second*5)) } if err := errg.Wait(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/feature_test.go ================================================ package scenarios import ( "context" "io" "net/http" "net/url" "testing" "time" xproxy "golang.org/x/net/proxy" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/proxyman" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/inbound" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/outbound" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" v2http "github.com/v2fly/v2ray-core/v5/proxy/http" "github.com/v2fly/v2ray-core/v5/proxy/socks" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func TestPassiveConnection(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, SendFirst: []byte("send first"), } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ IP: []byte{127, 0, 0, 1}, Port: int(serverPort), }) common.Must(err) { response := make([]byte, 1024) nBytes, err := conn.Read(response) common.Must(err) if string(response[:nBytes]) != "send first" { t.Error("unexpected first response: ", string(response[:nBytes])) } } if err := testTCPConn2(conn, 1024, time.Second*5)(); err != nil { t.Error(err) } } func TestProxy(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() serverUserID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: serverUserID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } proxyUserID := protocol.NewID(uuid.New()) proxyPort := tcp.PickPort() proxyConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(proxyPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: proxyUserID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: serverUserID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ ProxySettings: &internet.ProxyConfig{ Tag: "proxy", }, }), }, { Tag: "proxy", ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(proxyPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: proxyUserID.String(), }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, proxyConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil { t.Error(err) } } func TestProxyOverKCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() serverUserID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: serverUserID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } proxyUserID := protocol.NewID(uuid.New()) proxyPort := tcp.PickPort() proxyConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(proxyPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: proxyUserID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, }, }), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: serverUserID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ ProxySettings: &internet.ProxyConfig{ Tag: "proxy", }, StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, }, }), }, { Tag: "proxy", ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(proxyPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: proxyUserID.String(), }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, proxyConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil { t.Error(err) } } func TestBlackhole(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() tcpServer2 := tcp.Server{ MsgProcessor: xor, } dest2, err := tcpServer2.Start() common.Must(err) defer tcpServer2.Close() serverPort := tcp.PickPort() serverPort2 := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort2), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest2.Address), Port: uint32(dest2.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { Tag: "direct", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, { Tag: "blocked", ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, }, App: []*anypb.Any{ serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { TargetTag: &router.RoutingRule_Tag{ Tag: "blocked", }, PortRange: net.SinglePortRange(dest2.Port), }, }, }), }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(serverPort2, 1024, time.Second*5)(); err == nil { t.Error("nil error") } } func TestForward(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_NO_AUTH, Accounts: map[string]string{ "Test Account": "Test Password", }, Address: net.NewIPOrDomain(net.LocalHostIP), UdpEnabled: false, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{ DestinationOverride: &freedom.DestinationOverride{ Server: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(dest.Port), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { noAuthDialer, err := xproxy.SOCKS5("tcp", net.TCPDestination(net.LocalHostIP, serverPort).NetAddr(), nil, xproxy.Direct) common.Must(err) conn, err := noAuthDialer.Dial("tcp", "google.com:80") common.Must(err) defer conn.Close() if err := testTCPConn2(conn, 1024, time.Second*5)(); err != nil { t.Error(err) } } } func TestUDPConnection(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, } dest, err := udpServer.Start() common.Must(err) defer udpServer.Close() clientPort := udp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_UDP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testUDPConn(clientPort, 1024, time.Second*5)(); err != nil { t.Error(err) } time.Sleep(20 * time.Second) if err := testUDPConn(clientPort, 1024, time.Second*5)(); err != nil { t.Error(err) } } func TestDomainSniffing(t *testing.T) { sniffingPort := tcp.PickPort() httpPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { Tag: "snif", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(sniffingPort), Listen: net.NewIPOrDomain(net.LocalHostIP), DomainOverride: []proxyman.KnownProtocols{ proxyman.KnownProtocols_TLS, }, }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: 443, NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, { Tag: "http", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(httpPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, Outbound: []*core.OutboundHandlerConfig{ { Tag: "redir", ProxySettings: serial.ToTypedMessage(&freedom.Config{ DestinationOverride: &freedom.DestinationOverride{ Server: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(sniffingPort), }, }, }), }, { Tag: "direct", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, App: []*anypb.Any{ serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { TargetTag: &router.RoutingRule_Tag{ Tag: "direct", }, InboundTag: []string{"snif"}, }, { TargetTag: &router.RoutingRule_Tag{ Tag: "redir", }, InboundTag: []string{"http"}, }, }, }), serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { transport := &http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { return url.Parse("http://127.0.0.1:" + httpPort.String()) }, } client := &http.Client{ Transport: transport, } resp, err := client.Get("https://www.github.com/") common.Must(err) defer resp.Body.Close() if resp.StatusCode != 200 { t.Error("unexpected status code: ", resp.StatusCode) } common.Must(resp.Write(io.Discard)) } } func TestDialV2Ray(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, Inbound: []*core.InboundHandlerConfig{}, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) client, err := core.New(clientConfig) common.Must(err) conn, err := core.Dial(context.Background(), client, dest) common.Must(err) defer conn.Close() if err := testTCPConn2(conn, 1024, time.Second*5)(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/grpc_test.go ================================================ package scenarios import ( "context" "os" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" _ "github.com/v2fly/v2ray-core/v5/main/distro/all" ) func TestGRPCDefault(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() defer coreInst.Close() common.Must(InstMgrIfce.AddInstance( context.TODO(), "grpc_client", common.Must2(os.ReadFile("config/grpc_client.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.AddInstance( context.TODO(), "grpc_server", common.Must2(os.ReadFile("config/grpc_server.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "grpc_server")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "grpc_client")) defer func() { common.Must(InstMgrIfce.StopInstance(context.TODO(), "grpc_server")) common.Must(InstMgrIfce.StopInstance(context.TODO(), "grpc_client")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "grpc_server")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "grpc_client")) coreInst.Close() }() if err := testTCPConnViaSocks(17784, dest.Port, 1024, time.Second*2)(); err != nil { t.Error(err) } } func TestGRPCWithServiceName(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() defer coreInst.Close() common.Must(InstMgrIfce.AddInstance( context.TODO(), "grpc_client", common.Must2(os.ReadFile("config/grpc_servicename_client.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.AddInstance( context.TODO(), "grpc_server", common.Must2(os.ReadFile("config/grpc_servicename_server.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "grpc_server")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "grpc_client")) defer func() { common.Must(InstMgrIfce.StopInstance(context.TODO(), "grpc_server")) common.Must(InstMgrIfce.StopInstance(context.TODO(), "grpc_client")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "grpc_server")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "grpc_client")) coreInst.Close() }() if err := testTCPConnViaSocks(17794, dest.Port, 1024, time.Second*2)(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/http_test.go ================================================ package scenarios import ( "bytes" "context" "crypto/rand" "io" "net/http" "net/url" "testing" "time" "github.com/google/go-cmp/cmp" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/freedom" v2http "github.com/v2fly/v2ray-core/v5/proxy/http" v2httptest "github.com/v2fly/v2ray-core/v5/testing/servers/http" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" ) func TestHttpConformance(t *testing.T) { httpServerPort := tcp.PickPort() httpServer := &v2httptest.Server{ Port: httpServerPort, PathHandler: make(map[string]http.HandlerFunc), } _, err := httpServer.Start() common.Must(err) defer httpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { transport := &http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { return url.Parse("http://127.0.0.1:" + serverPort.String()) }, } client := &http.Client{ Transport: transport, } resp, err := client.Get("http://127.0.0.1:" + httpServerPort.String()) common.Must(err) defer resp.Body.Close() if resp.StatusCode != 200 { t.Fatal("status: ", resp.StatusCode) } content, err := io.ReadAll(resp.Body) common.Must(err) if string(content) != "Home" { t.Fatal("body: ", string(content)) } } } func TestHttpError(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: func(msg []byte) []byte { return []byte{} }, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() time.AfterFunc(time.Second*2, func() { tcpServer.ShouldClose = true }) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { transport := &http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { return url.Parse("http://127.0.0.1:" + serverPort.String()) }, } client := &http.Client{ Transport: transport, } resp, err := client.Get("http://127.0.0.1:" + dest.Port.String()) common.Must(err) defer resp.Body.Close() if resp.StatusCode != 503 { t.Error("status: ", resp.StatusCode) } } } func TestHTTPConnectMethod(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { transport := &http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { return url.Parse("http://127.0.0.1:" + serverPort.String()) }, } client := &http.Client{ Transport: transport, } payload := make([]byte, 1024*64) common.Must2(rand.Read(payload)) ctx := context.Background() req, err := http.NewRequestWithContext(ctx, "Connect", "http://"+dest.NetAddr()+"/", bytes.NewReader(payload)) req.Header.Set("X-a", "b") req.Header.Set("X-b", "d") common.Must(err) resp, err := client.Do(req) common.Must(err) defer resp.Body.Close() if resp.StatusCode != 200 { t.Fatal("status: ", resp.StatusCode) } content := make([]byte, len(payload)) common.Must2(io.ReadFull(resp.Body, content)) if r := cmp.Diff(content, xor(payload)); r != "" { t.Fatal(r) } } } func TestHttpPost(t *testing.T) { httpServerPort := tcp.PickPort() httpServer := &v2httptest.Server{ Port: httpServerPort, PathHandler: map[string]http.HandlerFunc{ "/testpost": func(w http.ResponseWriter, r *http.Request) { payload, err := buf.ReadAllToBytes(r.Body) r.Body.Close() if err != nil { w.WriteHeader(500) w.Write([]byte("Unable to read all payload")) return } payload = xor(payload) w.Write(payload) }, }, } _, err := httpServer.Start() common.Must(err) defer httpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{}), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { transport := &http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { return url.Parse("http://127.0.0.1:" + serverPort.String()) }, } client := &http.Client{ Transport: transport, } payload := make([]byte, 1024*64) common.Must2(rand.Read(payload)) resp, err := client.Post("http://127.0.0.1:"+httpServerPort.String()+"/testpost", "application/x-www-form-urlencoded", bytes.NewReader(payload)) common.Must(err) defer resp.Body.Close() if resp.StatusCode != 200 { t.Fatal("status: ", resp.StatusCode) } content, err := io.ReadAll(resp.Body) common.Must(err) if r := cmp.Diff(content, xor(payload)); r != "" { t.Fatal(r) } } } func setProxyBasicAuth(req *http.Request, user, pass string) { req.SetBasicAuth(user, pass) req.Header.Set("Proxy-Authorization", req.Header.Get("Authorization")) req.Header.Del("Authorization") } func TestHttpBasicAuth(t *testing.T) { httpServerPort := tcp.PickPort() httpServer := &v2httptest.Server{ Port: httpServerPort, PathHandler: make(map[string]http.HandlerFunc), } _, err := httpServer.Start() common.Must(err) defer httpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&v2http.ServerConfig{ Accounts: map[string]string{ "a": "b", }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { transport := &http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { return url.Parse("http://127.0.0.1:" + serverPort.String()) }, } client := &http.Client{ Transport: transport, } { resp, err := client.Get("http://127.0.0.1:" + httpServerPort.String()) common.Must(err) defer resp.Body.Close() if resp.StatusCode != 407 { t.Fatal("status: ", resp.StatusCode) } } { ctx := context.Background() req, err := http.NewRequestWithContext(ctx, "GET", "http://127.0.0.1:"+httpServerPort.String(), nil) common.Must(err) setProxyBasicAuth(req, "a", "c") resp, err := client.Do(req) common.Must(err) defer resp.Body.Close() if resp.StatusCode != 407 { t.Fatal("status: ", resp.StatusCode) } } { ctx := context.Background() req, err := http.NewRequestWithContext(ctx, "GET", "http://127.0.0.1:"+httpServerPort.String(), nil) common.Must(err) setProxyBasicAuth(req, "a", "b") resp, err := client.Do(req) common.Must(err) defer resp.Body.Close() if resp.StatusCode != 200 { t.Fatal("status: ", resp.StatusCode) } content, err := io.ReadAll(resp.Body) common.Must(err) if string(content) != "Home" { t.Fatal("body: ", string(content)) } } } } ================================================ FILE: testing/scenarios/httpupgrade_test.go ================================================ package scenarios import ( "context" "os" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" _ "github.com/v2fly/v2ray-core/v5/main/distro/all" ) func TestHTTPUpgrade(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() defer coreInst.Close() common.Must(InstMgrIfce.AddInstance( context.TODO(), "httpupgrade_client", common.Must2(os.ReadFile("config/httpupgrade_client.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.AddInstance( context.TODO(), "httpupgrade_server", common.Must2(os.ReadFile("config/httpupgrade_server.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "httpupgrade_client")) defer func() { common.Must(InstMgrIfce.StopInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.StopInstance(context.TODO(), "httpupgrade_client")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "httpupgrade_client")) coreInst.Close() }() if err := testTCPConnViaSocks(17794, dest.Port, 1024, time.Second*2)(); err != nil { t.Error(err) } } func TestHTTPUpgradeWithEarlyData(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() defer coreInst.Close() common.Must(InstMgrIfce.AddInstance( context.TODO(), "httpupgrade_client", common.Must2(os.ReadFile("config/httpupgrade_earlydata_client.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.AddInstance( context.TODO(), "httpupgrade_server", common.Must2(os.ReadFile("config/httpupgrade_earlydata_server.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "httpupgrade_client")) defer func() { common.Must(InstMgrIfce.StopInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.StopInstance(context.TODO(), "httpupgrade_client")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "httpupgrade_client")) coreInst.Close() }() if err := testTCPConnViaSocks(17794, dest.Port, 1024, time.Second*2)(); err != nil { t.Error(err) } } func TestHTTPUpgradeWithShortEarlyData(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() defer coreInst.Close() common.Must(InstMgrIfce.AddInstance( context.TODO(), "httpupgrade_client", common.Must2(os.ReadFile("config/httpupgrade_earlydataShortEarlyData_client.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.AddInstance( context.TODO(), "httpupgrade_server", common.Must2(os.ReadFile("config/httpupgrade_earlydataShortEarlyData_server.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "httpupgrade_client")) defer func() { common.Must(InstMgrIfce.StopInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.StopInstance(context.TODO(), "httpupgrade_client")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "httpupgrade_server")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "httpupgrade_client")) coreInst.Close() }() if err := testTCPConnViaSocks(17794, dest.Port, 1024, time.Second*2)(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/hy2_test.go ================================================ package scenarios import ( "testing" "time" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/hysteria2" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http" hyTransport "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2" tcpTransport "github.com/v2fly/v2ray-core/v5/transport/internet/tcp" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func TestVMessHysteria2Congestion(t *testing.T) { for _, v := range []string{"bbr", "brutal"} { testVMessHysteria2(t, v) } } func testVMessHysteria2(t *testing.T, congestionType string) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := udp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ ProtocolName: "hysteria2", SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage( &tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, }, ), }, TransportSettings: []*internet.TransportConfig{ { ProtocolName: "hysteria2", Settings: serial.ToTypedMessage(&hyTransport.Config{ Congestion: &hyTransport.Congestion{Type: congestionType, UpMbps: 100, DownMbps: 100}, Password: "password", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "hysteria2", SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage( &tls.Config{ ServerName: "www.v2fly.org", AllowInsecure: true, }, ), }, TransportSettings: []*internet.TransportConfig{ { ProtocolName: "hysteria2", Settings: serial.ToTypedMessage(&hyTransport.Config{ Congestion: &hyTransport.Congestion{Type: congestionType, UpMbps: 100, DownMbps: 100}, Password: "password", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_NONE, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to initialize all servers: ", err.Error()) } defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestHysteria2Offical(t *testing.T) { for _, v := range []bool{true, false} { testHysteria2Offical(t, v) } } func testHysteria2Offical(t *testing.T, isUDP bool) { var dest net.Destination var err error if isUDP { udpServer := udp.Server{ MsgProcessor: xor, } dest, err = udpServer.Start() common.Must(err) defer udpServer.Close() } else { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err = tcpServer.Start() common.Must(err) defer tcpServer.Close() } serverPort := udp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ ProtocolName: "hysteria2", SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage( &tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, }, ), }, TransportSettings: []*internet.TransportConfig{ { ProtocolName: "hysteria2", Settings: serial.ToTypedMessage(&hyTransport.Config{ Congestion: &hyTransport.Congestion{Type: "brutal", UpMbps: 100, DownMbps: 100}, UseUdpExtension: true, Password: "password", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&hysteria2.ServerConfig{}), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP, net.Network_UDP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "hysteria2", SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage( &tls.Config{ ServerName: "www.v2fly.org", AllowInsecure: true, }, ), }, TransportSettings: []*internet.TransportConfig{ { ProtocolName: "hysteria2", Settings: serial.ToTypedMessage(&hyTransport.Config{ Congestion: &hyTransport.Congestion{Type: "brutal", UpMbps: 100, DownMbps: 100}, UseUdpExtension: true, Password: "password", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&hysteria2.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&hysteria2.Account{}), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to initialize all servers: ", err.Error()) } defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { if isUDP { errg.Go(testUDPConn(clientPort, 1500, time.Second*4)) } else { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) } } if err := errg.Wait(); err != nil { t.Error(err) } } func TestHysteria2OnTCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() serverPort := udp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage( &tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, }, ), }, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_TCP, Settings: serial.ToTypedMessage(&tcpTransport.Config{ HeaderSettings: serial.ToTypedMessage(&http.Config{}), }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&hysteria2.ServerConfig{}), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage( &tls.Config{ ServerName: "www.v2fly.org", AllowInsecure: true, }, ), }, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_TCP, Settings: serial.ToTypedMessage(&tcpTransport.Config{ HeaderSettings: serial.ToTypedMessage(&http.Config{}), }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&hysteria2.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&hysteria2.Account{}), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to initialize all servers: ", err.Error()) } defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 1; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/meek_test.go ================================================ package scenarios import ( "context" "os" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" _ "github.com/v2fly/v2ray-core/v5/main/distro/all" ) func TestMeek(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() defer coreInst.Close() common.Must(InstMgrIfce.AddInstance( context.TODO(), "meek_client", common.Must2(os.ReadFile("config/meek_client.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.AddInstance( context.TODO(), "meek_server", common.Must2(os.ReadFile("config/meek_server.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "meek_server")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "meek_client")) defer func() { common.Must(InstMgrIfce.StopInstance(context.TODO(), "meek_server")) common.Must(InstMgrIfce.StopInstance(context.TODO(), "meek_client")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "meek_server")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "meek_client")) coreInst.Close() }() if err := testTCPConnViaSocks(17774, dest.Port, 1024, time.Second*2)(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/mekya_test.go ================================================ package scenarios import ( "context" "os" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" _ "github.com/v2fly/v2ray-core/v5/main/distro/all" ) func TestMekya(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() coreInst, InstMgrIfce := NewInstanceManagerCoreInstance() defer coreInst.Close() common.Must(InstMgrIfce.AddInstance( context.TODO(), "mekya_client", common.Must2(os.ReadFile("config/mekya_client.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.AddInstance( context.TODO(), "mekya_server", common.Must2(os.ReadFile("config/mekya_server.json")).([]byte), "jsonv5")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "mekya_server")) common.Must(InstMgrIfce.StartInstance(context.TODO(), "mekya_client")) defer func() { common.Must(InstMgrIfce.StopInstance(context.TODO(), "mekya_server")) common.Must(InstMgrIfce.StopInstance(context.TODO(), "mekya_client")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "mekya_server")) common.Must(InstMgrIfce.UntrackInstance(context.TODO(), "mekya_client")) coreInst.Close() }() if err := testTCPConnViaSocks(17774, dest.Port, 1024, time.Second*2)(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/policy_test.go ================================================ package scenarios import ( "io" "testing" "time" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/policy" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" ) func startQuickClosingTCPServer() (net.Listener, error) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return nil, err } go func() { for { conn, err := listener.Accept() if err != nil { break } b := make([]byte, 1024) conn.Read(b) conn.Close() } }() return listener, nil } func TestVMessClosing(t *testing.T) { tcpServer, err := startQuickClosingTCPServer() common.Must(err) defer tcpServer.Close() dest := net.DestinationFromAddr(tcpServer.Addr()) userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*2)(); err != io.EOF { t.Error(err) } } func TestZeroBuffer(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, }, Buffer: &policy.Policy_Buffer{ Connection: 0, }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*20)) } if err := errg.Wait(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/reverse_test.go ================================================ package scenarios import ( "testing" "time" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/policy" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/reverse" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" ) func TestReverseProxy(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) externalPort := tcp.PickPort() reversePort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&reverse.Config{ PortalConfig: []*reverse.PortalConfig{ { Tag: "portal", Domain: "test.v2fly.org", }, }, }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { Domain: []*routercommon.Domain{ {Type: routercommon.Domain_Full, Value: "test.v2fly.org"}, }, TargetTag: &router.RoutingRule_Tag{ Tag: "portal", }, }, { InboundTag: []string{"external"}, TargetTag: &router.RoutingRule_Tag{ Tag: "portal", }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { Tag: "external", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(externalPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(reversePort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&reverse.Config{ BridgeConfig: []*reverse.BridgeConfig{ { Tag: "bridge", Domain: "test.v2fly.org", }, }, }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { Domain: []*routercommon.Domain{ {Type: routercommon.Domain_Full, Value: "test.v2fly.org"}, }, TargetTag: &router.RoutingRule_Tag{ Tag: "reverse", }, }, { InboundTag: []string{"bridge"}, TargetTag: &router.RoutingRule_Tag{ Tag: "freedom", }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { Tag: "freedom", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, { Tag: "reverse", ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(reversePort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 32; i++ { errg.Go(testTCPConn(externalPort, 10240*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Fatal(err) } } func TestReverseProxyLongRunning(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) externalPort := tcp.PickPort() reversePort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, }, }, }, }), serial.ToTypedMessage(&reverse.Config{ PortalConfig: []*reverse.PortalConfig{ { Tag: "portal", Domain: "test.v2fly.org", }, }, }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { Domain: []*routercommon.Domain{ {Type: routercommon.Domain_Full, Value: "test.v2fly.org"}, }, TargetTag: &router.RoutingRule_Tag{ Tag: "portal", }, }, { InboundTag: []string{"external"}, TargetTag: &router.RoutingRule_Tag{ Tag: "portal", }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { Tag: "external", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(externalPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(reversePort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, }, }, }, }), serial.ToTypedMessage(&reverse.Config{ BridgeConfig: []*reverse.BridgeConfig{ { Tag: "bridge", Domain: "test.v2fly.org", }, }, }), serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { Domain: []*routercommon.Domain{ {Type: routercommon.Domain_Full, Value: "test.v2fly.org"}, }, TargetTag: &router.RoutingRule_Tag{ Tag: "reverse", }, }, { InboundTag: []string{"bridge"}, TargetTag: &router.RoutingRule_Tag{ Tag: "freedom", }, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { Tag: "freedom", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, { Tag: "reverse", ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(reversePort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) for i := 0; i < 4096; i++ { if err := testTCPConn(externalPort, 1024, time.Second*20)(); err != nil { t.Error(err) } } } ================================================ FILE: testing/scenarios/shadowsocks_test.go ================================================ package scenarios import ( "testing" "time" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" ) func TestShadowsocksChaCha20Poly1305TCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() account := serial.ToTypedMessage(&shadowsocks.Account{ Password: "shadowsocks-password", CipherType: shadowsocks.CipherType_CHACHA20_POLY1305, }) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ User: &protocol.User{ Account: account, Level: 1, }, Network: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), Networks: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: account, }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errGroup errgroup.Group for i := 0; i < 10; i++ { errGroup.Go(testTCPConn(clientPort, 10240*1024, time.Second*20)) } if err := errGroup.Wait(); err != nil { t.Error(err) } } func TestShadowsocksAES256GCMTCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() account := serial.ToTypedMessage(&shadowsocks.Account{ Password: "shadowsocks-password", CipherType: shadowsocks.CipherType_AES_256_GCM, }) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ User: &protocol.User{ Account: account, Level: 1, }, Network: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), Networks: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: account, }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errGroup errgroup.Group for i := 0; i < 10; i++ { errGroup.Go(testTCPConn(clientPort, 10240*1024, time.Second*20)) } if err := errGroup.Wait(); err != nil { t.Error(err) } } func TestShadowsocksAES128GCMUDP(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, } dest, err := udpServer.Start() common.Must(err) defer udpServer.Close() account := serial.ToTypedMessage(&shadowsocks.Account{ Password: "shadowsocks-password", CipherType: shadowsocks.CipherType_AES_128_GCM, }) serverPort := udp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ User: &protocol.User{ Account: account, Level: 1, }, Network: []net.Network{net.Network_UDP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := udp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), Networks: []net.Network{net.Network_UDP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: account, }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errGroup errgroup.Group for i := 0; i < 10; i++ { errGroup.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errGroup.Wait(); err != nil { t.Error(err) } } func TestShadowsocksAES128GCMUDPMux(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, } dest, err := udpServer.Start() common.Must(err) defer udpServer.Close() account := serial.ToTypedMessage(&shadowsocks.Account{ Password: "shadowsocks-password", CipherType: shadowsocks.CipherType_AES_128_GCM, }) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ User: &protocol.User{ Account: account, Level: 1, }, Network: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := udp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), Networks: []net.Network{net.Network_UDP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ MultiplexSettings: &proxyman.MultiplexingConfig{ Enabled: true, Concurrency: 8, }, }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: account, }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errGroup errgroup.Group for i := 0; i < 10; i++ { errGroup.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errGroup.Wait(); err != nil { t.Error(err) } } func TestShadowsocksNone(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() account := serial.ToTypedMessage(&shadowsocks.Account{ Password: "shadowsocks-password", CipherType: shadowsocks.CipherType_NONE, }) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ User: &protocol.User{ Account: account, Level: 1, }, Network: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), Networks: []net.Network{net.Network_TCP}, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: account, }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errGroup errgroup.Group for i := 0; i < 10; i++ { errGroup.Go(testTCPConn(clientPort, 10240*1024, time.Second*20)) } if err := errGroup.Wait(); err != nil { t.Fatal(err) } } ================================================ FILE: testing/scenarios/socks_test.go ================================================ package scenarios import ( "testing" "time" xproxy "golang.org/x/net/proxy" "google.golang.org/protobuf/types/known/anypb" socks4 "h12.io/socks" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/socks" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" ) func TestSocksBridgeTCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_PASSWORD, Accounts: map[string]string{ "Test Account": "Test Password", }, Address: net.NewIPOrDomain(net.LocalHostIP), UdpEnabled: false, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&socks.Account{ Username: "Test Account", Password: "Test Password", }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*2)(); err != nil { t.Error(err) } } func TestSocksBridageUDP(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, } dest, err := udpServer.Start() common.Must(err) defer udpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_PASSWORD, Accounts: map[string]string{ "Test Account": "Test Password", }, Address: net.NewIPOrDomain(net.LocalHostIP), UdpEnabled: true, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP, net.Network_UDP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&socks.Account{ Username: "Test Account", Password: "Test Password", }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testUDPConn(clientPort, 1024, time.Second*5)(); err != nil { t.Error(err) } } func TestSocksBridageUDPWithRouting(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, } dest, err := udpServer.Start() common.Must(err) defer udpServer.Close() serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&router.Config{ Rule: []*router.RoutingRule{ { TargetTag: &router.RoutingRule_Tag{ Tag: "out", }, InboundTag: []string{"socks"}, }, }, }), }, Inbound: []*core.InboundHandlerConfig{ { Tag: "socks", ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_NO_AUTH, Address: net.NewIPOrDomain(net.LocalHostIP), UdpEnabled: true, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, { Tag: "out", ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP, net.Network_UDP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&socks.ClientConfig{ Server: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testUDPConn(clientPort, 1024, time.Second*5)(); err != nil { t.Error(err) } } func TestSocksConformanceMod(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() authPort := tcp.PickPort() noAuthPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(authPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_PASSWORD, Accounts: map[string]string{ "Test Account": "Test Password", }, Address: net.NewIPOrDomain(net.LocalHostIP), UdpEnabled: false, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(noAuthPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&socks.ServerConfig{ AuthType: socks.AuthType_NO_AUTH, Accounts: map[string]string{ "Test Account": "Test Password", }, Address: net.NewIPOrDomain(net.LocalHostIP), UdpEnabled: false, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } servers, err := InitializeServerConfigs(serverConfig) common.Must(err) defer CloseAllServers(servers) { noAuthDialer, err := xproxy.SOCKS5("tcp", net.TCPDestination(net.LocalHostIP, noAuthPort).NetAddr(), nil, xproxy.Direct) common.Must(err) conn, err := noAuthDialer.Dial("tcp", dest.NetAddr()) common.Must(err) defer conn.Close() if err := testTCPConn2(conn, 1024, time.Second*5)(); err != nil { t.Error(err) } } { authDialer, err := xproxy.SOCKS5("tcp", net.TCPDestination(net.LocalHostIP, authPort).NetAddr(), &xproxy.Auth{User: "Test Account", Password: "Test Password"}, xproxy.Direct) common.Must(err) conn, err := authDialer.Dial("tcp", dest.NetAddr()) common.Must(err) defer conn.Close() if err := testTCPConn2(conn, 1024, time.Second*5)(); err != nil { t.Error(err) } } { dialer := socks4.Dial("socks4://" + net.TCPDestination(net.LocalHostIP, noAuthPort).NetAddr()) conn, err := dialer("tcp", dest.NetAddr()) common.Must(err) defer conn.Close() if err := testTCPConn2(conn, 1024, time.Second*5)(); err != nil { t.Error(err) } } { dialer := socks4.Dial("socks4://" + net.TCPDestination(net.LocalHostIP, noAuthPort).NetAddr()) conn, err := dialer("tcp", net.TCPDestination(net.LocalHostIP, tcpServer.Port).NetAddr()) common.Must(err) defer conn.Close() if err := testTCPConn2(conn, 1024, time.Second*5)(); err != nil { t.Error(err) } } } ================================================ FILE: testing/scenarios/tls_test.go ================================================ package scenarios import ( "crypto/x509" "runtime" "testing" "time" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/http" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" "github.com/v2fly/v2ray-core/v5/transport/internet/websocket" ) func TestSimpleTLSConnection(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ AllowInsecure: true, }), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*20)(); err != nil { t.Fatal(err) } } func TestAutoIssuingCertificate(t *testing.T) { if runtime.GOOS == "windows" { // Not supported on Windows yet. return } if runtime.GOARCH == "arm64" { return } tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() caCert, err := cert.Generate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment|x509.KeyUsageCertSign)) common.Must(err) certPEM, keyPEM := caCert.ToPEM() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ Certificate: []*tls.Certificate{{ Certificate: certPEM, Key: keyPEM, Usage: tls.Certificate_AUTHORITY_ISSUE, }}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ ServerName: "v2fly.org", Certificate: []*tls.Certificate{{ Certificate: certPEM, Usage: tls.Certificate_AUTHORITY_VERIFY, }}, }), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) for i := 0; i < 10; i++ { if err := testTCPConn(clientPort, 1024, time.Second*20)(); err != nil { t.Error(err) } } } func TestIPAddressesCertificate(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() caCert, err := cert.Generate(nil, cert.IPAddresses(net.LocalHostIP.IP()), cert.Authority(true), cert.KeyUsage(x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment|x509.KeyUsageCertSign)) common.Must(err) certPEM, keyPEM := caCert.ToPEM() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ Certificate: []*tls.Certificate{{ Certificate: certPEM, Key: keyPEM, }}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ DisableSystemRoot: true, Certificate: []*tls.Certificate{{ Certificate: certPEM, Usage: tls.Certificate_AUTHORITY_VERIFY, }}, }), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) for i := 0; i < 10; i++ { if err := testTCPConn(clientPort, 1024, time.Second*20)(); err != nil { t.Error(err) } } } func TestDNSNamesCertificate(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() caCert, err := cert.Generate(nil, cert.DNSNames("v2fly.org"), cert.Authority(true), cert.KeyUsage(x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment|x509.KeyUsageCertSign)) common.Must(err) certPEM, keyPEM := caCert.ToPEM() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ Certificate: []*tls.Certificate{{ Certificate: certPEM, Key: keyPEM, }}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ DisableSystemRoot: true, ServerName: "v2fly.org", Certificate: []*tls.Certificate{{ Certificate: certPEM, Usage: tls.Certificate_AUTHORITY_VERIFY, }}, }), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) for i := 0; i < 10; i++ { if err := testTCPConn(clientPort, 1024, time.Second*20)(); err != nil { t.Error(err) } } } func TestTLSOverKCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := udp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ AllowInsecure: true, }), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*20)(); err != nil { t.Error(err) } } func TestTLSOverWebSocket(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_WebSocket, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_WebSocket, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_WebSocket, Settings: serial.ToTypedMessage(&websocket.Config{}), }, }, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ AllowInsecure: true, }), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*20)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestHTTP2(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_HTTP, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_HTTP, Settings: serial.ToTypedMessage(&http.Config{ Host: []string{"v2fly.org"}, Path: "/testpath", }), }, }, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_HTTP, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_HTTP, Settings: serial.ToTypedMessage(&http.Config{ Host: []string{"v2fly.org"}, Path: "/testpath", }), }, }, SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ AllowInsecure: true, }), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 7168*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestSimpleTLSConnectionPinned(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() certificateDer := cert.MustGenerate(nil) certificate := tls.ParseCertificate(certificateDer) certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate}) userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ Certificate: []*tls.Certificate{certificate}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ AllowInsecure: true, PinnedPeerCertificateChainSha256: [][]byte{certHash}, }), }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*20)(); err != nil { t.Fatal(err) } } ================================================ FILE: testing/scenarios/transport_test.go ================================================ package scenarios import ( "os" "runtime" "testing" "time" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wechat" "github.com/v2fly/v2ray-core/v5/transport/internet/quic" tcptransport "github.com/v2fly/v2ray-core/v5/transport/internet/tcp" ) func TestHTTPConnectionHeader(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_TCP, Settings: serial.ToTypedMessage(&tcptransport.Config{ HeaderSettings: serial.ToTypedMessage(&http.Config{}), }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_TCP, Settings: serial.ToTypedMessage(&tcptransport.Config{ HeaderSettings: serial.ToTypedMessage(&http.Config{}), }), }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*2)(); err != nil { t.Error(err) } } func TestDomainSocket(t *testing.T) { if runtime.GOOS == "windows" || runtime.GOOS == "android" { t.Skip("Not supported on windows and android") return } tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() const dsPath = "/tmp/ds_scenario" os.Remove(dsPath) userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_DomainSocket, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_DomainSocket, Settings: serial.ToTypedMessage(&domainsocket.Config{ Path: dsPath, }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_DomainSocket, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_DomainSocket, Settings: serial.ToTypedMessage(&domainsocket.Config{ Path: dsPath, }), }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) if err := testTCPConn(clientPort, 1024, time.Second*2)(); err != nil { t.Error(err) } } func TestVMessQuic(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := udp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ ProtocolName: "quic", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "quic", Settings: serial.ToTypedMessage(&quic.Config{ Header: serial.ToTypedMessage(&wechat.VideoConfig{}), Security: &protocol.SecurityConfig{ Type: protocol.SecurityType_NONE, }, }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "quic", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "quic", Settings: serial.ToTypedMessage(&quic.Config{ Header: serial.ToTypedMessage(&wechat.VideoConfig{}), Security: &protocol.SecurityConfig{ Type: protocol.SecurityType_NONE, }, }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to initialize all servers: ", err.Error()) } defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Error(err) } } ================================================ FILE: testing/scenarios/vmess_test.go ================================================ package scenarios import ( "os" "testing" "time" "github.com/golang/protobuf/ptypes/any" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" ) func TestVMessDynamicPort(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) retry := 1 serverPort := tcp.PickPort() for { serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, Detour: &inbound.DetourConfig{ To: "detour", }, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort + 100), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{ From: uint32(serverPort + 1), To: uint32(serverPort + 99), }, Listen: net.NewIPOrDomain(net.LocalHostIP), AllocationStrategy: &proxyman.AllocationStrategy{ Type: proxyman.AllocationStrategy_Random, Concurrency: &proxyman.AllocationStrategy_AllocationStrategyConcurrency{ Value: 2, }, Refresh: &proxyman.AllocationStrategy_AllocationStrategyRefresh{ Value: 5, }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{}), Tag: "detour", }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } server, _ := InitializeServerConfig(serverConfig) if server != nil && tcpConnAvailableAtPort(t, serverPort+100) { defer CloseServer(server) break } retry++ if retry > 5 { t.Fatal("All attempts failed to start server") } serverPort = tcp.PickPort() } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), }, }, } server, err := InitializeServerConfig(clientConfig) common.Must(err) defer CloseServer(server) if !tcpConnAvailableAtPort(t, clientPort) { t.Fail() } } func tcpConnAvailableAtPort(t *testing.T, port net.Port) bool { for i := 1; ; i++ { if i > 10 { t.Log("All attempts failed to test tcp conn") return false } time.Sleep(time.Millisecond * 10) if err := testTCPConn(port, 1024, time.Second*2)(); err != nil { t.Log("err ", err) } else { t.Log("success with", i, "attempts") break } } return true } func TestVMessGCM(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to initialize all servers: ", err.Error()) } defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestVMessGCMReadv(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } const envName = "V2RAY_BUF_READV" common.Must(os.Setenv(envName, "enable")) defer os.Unsetenv(envName) servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to initialize all servers: ", err.Error()) } defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestVMessGCMUDP(t *testing.T) { udpServer := udp.Server{ MsgProcessor: xor, } dest, err := udpServer.Start() common.Must(err) defer udpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := udp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_UDP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testUDPConn(clientPort, 1024, time.Second*5)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestVMessChacha20(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_CHACHA20_POLY1305, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*20)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestVMessNone(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_NONE, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestVMessKCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := udp.PickPort() serverConfig := &core.Config{ App: []*any.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Minute*2)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestVMessKCPLarge(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := udp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_MKCP, Settings: serial.ToTypedMessage(&kcp.Config{ ReadBuffer: &kcp.ReadBuffer{ Size: 512 * 1024, }, WriteBuffer: &kcp.WriteBuffer{ Size: 512 * 1024, }, UplinkCapacity: &kcp.UplinkCapacity{ Value: 20, }, DownlinkCapacity: &kcp.DownlinkCapacity{ Value: 20, }, }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ Protocol: internet.TransportProtocol_MKCP, TransportSettings: []*internet.TransportConfig{ { Protocol: internet.TransportProtocol_MKCP, Settings: serial.ToTypedMessage(&kcp.Config{ ReadBuffer: &kcp.ReadBuffer{ Size: 512 * 1024, }, WriteBuffer: &kcp.WriteBuffer{ Size: 512 * 1024, }, UplinkCapacity: &kcp.UplinkCapacity{ Value: 20, }, DownlinkCapacity: &kcp.DownlinkCapacity{ Value: 20, }, }), }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) var errg errgroup.Group for i := 0; i < 2; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Minute*5)) } if err := errg.Wait(); err != nil { t.Error(err) } defer func() { <-time.After(5 * time.Second) CloseAllServers(servers) }() } func TestVMessGCMMux(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ MultiplexSettings: &proxyman.MultiplexingConfig{ Enabled: true, Concurrency: 4, }, }), ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) for range "abcd" { var errg errgroup.Group for i := 0; i < 16; i++ { errg.Go(testTCPConn(clientPort, 10240, time.Second*20)) } if err := errg.Wait(); err != nil { t.Fatal(err) } time.Sleep(time.Second) } } func TestVMessGCMMuxUDP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() udpServer := udp.Server{ MsgProcessor: xor, } udpDest, err := udpServer.Start() common.Must(err) defer udpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientUDPPort := udp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientUDPPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(udpDest.Address), Port: uint32(udpDest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_UDP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ MultiplexSettings: &proxyman.MultiplexingConfig{ Enabled: true, Concurrency: 4, }, }), ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) for range "abcd" { var errg errgroup.Group for i := 0; i < 16; i++ { errg.Go(testTCPConn(clientPort, 10240, time.Second*20)) errg.Go(testUDPConn(clientUDPPort, 1024, time.Second*10)) } if err := errg.Wait(); err != nil { t.Error(err) } time.Sleep(time.Second) } defer func() { <-time.After(5 * time.Second) CloseAllServers(servers) }() } func TestVMessZero(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_ZERO, }, }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) common.Must(err) defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestVMessGCMLengthAuth(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, TestsEnabled: "AuthenticatedLength", }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to initialize all servers: ", err.Error()) } defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Error(err) } } func TestVMessGCMLengthAuthPlusNoTerminationSignal(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: xor, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(serverPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, TestsEnabled: "AuthenticatedLength|NoTerminationSignal", }), }, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&freedom.Config{}), }, }, } clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{Level: clog.Severity_Debug, Type: log.LogType_Console}, }), }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(clientPort), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(dest.Address), Port: uint32(dest.Port), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP}, }, }), }, }, Outbound: []*core.OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), AlterId: 0, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, TestsEnabled: "AuthenticatedLength|NoTerminationSignal", }), }, }, }, }, }), }, }, } servers, err := InitializeServerConfigs(serverConfig, clientConfig) if err != nil { t.Fatal("Failed to initialize all servers: ", err.Error()) } defer CloseAllServers(servers) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40)) } if err := errg.Wait(); err != nil { t.Error(err) } } ================================================ FILE: testing/servers/http/http.go ================================================ package tcp import ( "net/http" "github.com/v2fly/v2ray-core/v5/common/net" ) type Server struct { Port net.Port PathHandler map[string]http.HandlerFunc server *http.Server } func (s *Server) ServeHTTP(resp http.ResponseWriter, req *http.Request) { if req.URL.Path == "/" { resp.Header().Set("Content-Type", "text/plain; charset=utf-8") resp.WriteHeader(http.StatusOK) resp.Write([]byte("Home")) return } handler, found := s.PathHandler[req.URL.Path] if found { handler(resp, req) } } func (s *Server) Start() (net.Destination, error) { s.server = &http.Server{ Addr: "127.0.0.1:" + s.Port.String(), Handler: s, } go s.server.ListenAndServe() return net.TCPDestination(net.LocalHostIP, s.Port), nil } func (s *Server) Close() error { return s.server.Close() } ================================================ FILE: testing/servers/tcp/port.go ================================================ package tcp import "github.com/v2fly/v2ray-core/v5/common/net" // PickPort returns an unused TCP port of the system. func PickPort() net.Port { listener := pickPort() defer listener.Close() addr := listener.Addr().(*net.TCPAddr) return net.Port(addr.Port) } func pickPort() net.Listener { listener, err := net.Listen("tcp4", "127.0.0.1:0") if err != nil { listener = pickPort() } return listener } ================================================ FILE: testing/servers/tcp/tcp.go ================================================ package tcp import ( "context" "fmt" "io" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) type Server struct { Port net.Port MsgProcessor func(msg []byte) []byte ShouldClose bool SendFirst []byte Listen net.Address listener net.Listener } func (server *Server) Start() (net.Destination, error) { return server.StartContext(context.Background(), nil) } func (server *Server) StartContext(ctx context.Context, sockopt *internet.SocketConfig) (net.Destination, error) { listenerAddr := server.Listen if listenerAddr == nil { listenerAddr = net.LocalHostIP } listener, err := internet.ListenSystem(ctx, &net.TCPAddr{ IP: listenerAddr.IP(), Port: int(server.Port), }, sockopt) if err != nil { return net.Destination{}, err } localAddr := listener.Addr().(*net.TCPAddr) server.Port = net.Port(localAddr.Port) server.listener = listener go server.acceptConnections(listener.(*net.TCPListener)) return net.TCPDestination(net.IPAddress(localAddr.IP), net.Port(localAddr.Port)), nil } func (server *Server) acceptConnections(listener *net.TCPListener) { for { conn, err := listener.Accept() if err != nil { fmt.Printf("Failed accept TCP connection: %v\n", err) return } go server.handleConnection(conn) } } func (server *Server) handleConnection(conn net.Conn) { if len(server.SendFirst) > 0 { conn.Write(server.SendFirst) } pReader, pWriter := pipe.New(pipe.WithoutSizeLimit()) err := task.Run(context.Background(), func() error { defer pWriter.Close() for { b := buf.New() if _, err := b.ReadFrom(conn); err != nil { if err == io.EOF { return nil } return err } copy(b.Bytes(), server.MsgProcessor(b.Bytes())) if err := pWriter.WriteMultiBuffer(buf.MultiBuffer{b}); err != nil { return err } } }, func() error { defer pReader.Interrupt() w := buf.NewWriter(conn) for { mb, err := pReader.ReadMultiBuffer() if err != nil { if err == io.EOF { return nil } return err } if err := w.WriteMultiBuffer(mb); err != nil { return err } } }) if err != nil { fmt.Println("failed to transfer data: ", err.Error()) } conn.Close() } func (server *Server) Close() error { return server.listener.Close() } ================================================ FILE: testing/servers/udp/port.go ================================================ package udp import "github.com/v2fly/v2ray-core/v5/common/net" // PickPort returns an unused UDP port of the system. func PickPort() net.Port { conn := pickPort() defer conn.Close() addr := conn.LocalAddr().(*net.UDPAddr) return net.Port(addr.Port) } func pickPort() *net.UDPConn { conn, err := net.ListenUDP("udp4", &net.UDPAddr{ IP: net.LocalHostIP.IP(), Port: 0, }) if err != nil { conn = pickPort() } return conn } ================================================ FILE: testing/servers/udp/udp.go ================================================ package udp import ( "fmt" "github.com/v2fly/v2ray-core/v5/common/net" ) type Server struct { Port net.Port MsgProcessor func(msg []byte) []byte accepting bool conn *net.UDPConn } func (server *Server) Start() (net.Destination, error) { conn, err := net.ListenUDP("udp", &net.UDPAddr{ IP: []byte{127, 0, 0, 1}, Port: int(server.Port), Zone: "", }) if err != nil { return net.Destination{}, err } server.Port = net.Port(conn.LocalAddr().(*net.UDPAddr).Port) fmt.Println("UDP server started on port ", server.Port) server.conn = conn go server.handleConnection(conn) localAddr := conn.LocalAddr().(*net.UDPAddr) return net.UDPDestination(net.IPAddress(localAddr.IP), net.Port(localAddr.Port)), nil } func (server *Server) handleConnection(conn *net.UDPConn) { server.accepting = true for server.accepting { buffer := make([]byte, 2*1024) nBytes, addr, err := conn.ReadFromUDP(buffer) if err != nil { fmt.Printf("Failed to read from UDP: %v\n", err) continue } response := server.MsgProcessor(buffer[:nBytes]) if _, err := conn.WriteToUDP(response, addr); err != nil { fmt.Println("Failed to write to UDP: ", err.Error()) } } } func (server *Server) Close() error { server.accepting = false return server.conn.Close() } ================================================ FILE: transport/config.go ================================================ package transport import ( "github.com/v2fly/v2ray-core/v5/transport/internet" ) // Apply applies this Config. func (c *Config) Apply() error { if c == nil { return nil } return internet.ApplyGlobalTransportSettings(c.TransportSettings) } ================================================ FILE: transport/config.pb.go ================================================ package transport import ( internet "github.com/v2fly/v2ray-core/v5/transport/internet" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Global transport settings. This affects all type of connections that go // through V2Ray. Deprecated. Use each settings in StreamConfig. // // Deprecated: Marked as deprecated in transport/config.proto. type Config struct { state protoimpl.MessageState `protogen:"open.v1"` TransportSettings []*internet.TransportConfig `protobuf:"bytes,1,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetTransportSettings() []*internet.TransportConfig { if x != nil { return x.TransportSettings } return nil } var File_transport_config_proto protoreflect.FileDescriptor const file_transport_config_proto_rawDesc = "" + "\n" + "\x16transport/config.proto\x12\x14v2ray.core.transport\x1a\x1ftransport/internet/config.proto\"k\n" + "\x06Config\x12]\n" + "\x12transport_settings\x18\x01 \x03(\v2..v2ray.core.transport.internet.TransportConfigR\x11transportSettings:\x02\x18\x01B]\n" + "\x18com.v2ray.core.transportP\x01Z(github.com/v2fly/v2ray-core/v5/transport\xaa\x02\x14V2Ray.Core.Transportb\x06proto3" var ( file_transport_config_proto_rawDescOnce sync.Once file_transport_config_proto_rawDescData []byte ) func file_transport_config_proto_rawDescGZIP() []byte { file_transport_config_proto_rawDescOnce.Do(func() { file_transport_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_config_proto_rawDesc), len(file_transport_config_proto_rawDesc))) }) return file_transport_config_proto_rawDescData } var file_transport_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.Config (*internet.TransportConfig)(nil), // 1: v2ray.core.transport.internet.TransportConfig } var file_transport_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.transport.Config.transport_settings:type_name -> v2ray.core.transport.internet.TransportConfig 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_config_proto_init() } func file_transport_config_proto_init() { if File_transport_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_config_proto_rawDesc), len(file_transport_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_config_proto_goTypes, DependencyIndexes: file_transport_config_proto_depIdxs, MessageInfos: file_transport_config_proto_msgTypes, }.Build() File_transport_config_proto = out.File file_transport_config_proto_goTypes = nil file_transport_config_proto_depIdxs = nil } ================================================ FILE: transport/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport; option csharp_namespace = "V2Ray.Core.Transport"; option go_package = "github.com/v2fly/v2ray-core/v5/transport"; option java_package = "com.v2ray.core.transport"; option java_multiple_files = true; import "transport/internet/config.proto"; // Global transport settings. This affects all type of connections that go // through V2Ray. Deprecated. Use each settings in StreamConfig. message Config { option deprecated = true; repeated v2ray.core.transport.internet.TransportConfig transport_settings = 1; } ================================================ FILE: transport/internet/config.go ================================================ package internet import ( "context" "github.com/golang/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protoext" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features" ) type ConfigCreator func() interface{} var ( globalTransportConfigCreatorCache = make(map[string]ConfigCreator) globalTransportSettings []*TransportConfig ) const unknownProtocol = "unknown" func transportProtocolToString(protocol TransportProtocol) string { switch protocol { case TransportProtocol_TCP: return "tcp" case TransportProtocol_UDP: return "udp" case TransportProtocol_HTTP: return "http" case TransportProtocol_MKCP: return "mkcp" case TransportProtocol_WebSocket: return "websocket" case TransportProtocol_DomainSocket: return "domainsocket" default: return unknownProtocol } } func RegisterProtocolConfigCreator(name string, creator ConfigCreator) error { if _, found := globalTransportConfigCreatorCache[name]; found { return newError("protocol ", name, " is already registered").AtError() } globalTransportConfigCreatorCache[name] = creator common.RegisterConfig(creator(), func(ctx context.Context, config interface{}) (interface{}, error) { return nil, newError("transport config should use CreateTransportConfig instead") }) return nil } func CreateTransportConfig(name string) (interface{}, error) { creator, ok := globalTransportConfigCreatorCache[name] if !ok { return nil, newError("unknown transport protocol: ", name) } return creator(), nil } func (c *TransportConfig) GetTypedSettings() (interface{}, error) { return serial.GetInstanceOf(c.Settings) } func (c *TransportConfig) GetUnifiedProtocolName() string { if len(c.ProtocolName) > 0 { return c.ProtocolName } return transportProtocolToString(c.Protocol) } func (c *StreamConfig) GetEffectiveProtocol() string { if c == nil { return "tcp" } if len(c.ProtocolName) > 0 { return c.ProtocolName } return transportProtocolToString(c.Protocol) } func (c *StreamConfig) GetEffectiveTransportSettings() (interface{}, error) { protocol := c.GetEffectiveProtocol() return c.GetTransportSettingsFor(protocol) } func (c *StreamConfig) GetTransportSettingsFor(protocol string) (interface{}, error) { if c != nil { for _, settings := range c.TransportSettings { if settings.GetUnifiedProtocolName() == protocol { return settings.GetTypedSettings() } } } for _, settings := range globalTransportSettings { if settings.GetUnifiedProtocolName() == protocol { return settings.GetTypedSettings() } } return CreateTransportConfig(protocol) } func (c *StreamConfig) GetEffectiveSecuritySettings() (interface{}, error) { for _, settings := range c.SecuritySettings { if serial.V2Type(settings) == c.SecurityType { return serial.GetInstanceOf(settings) } } return serial.GetInstance(c.SecurityType) } func (c *StreamConfig) HasSecuritySettings() bool { return len(c.SecurityType) > 0 } func ApplyGlobalTransportSettings(settings []*TransportConfig) error { features.PrintDeprecatedFeatureWarning("global transport settings") globalTransportSettings = settings return nil } func (c *ProxyConfig) HasTag() bool { return c != nil && len(c.Tag) > 0 } func (m SocketConfig_TProxyMode) IsEnabled() bool { return m != SocketConfig_Off } func getOriginalMessageName(streamSettings *MemoryStreamConfig) string { msgOpts, err := protoext.GetMessageOptions(proto.MessageV2(streamSettings.ProtocolSettings).ProtoReflect().Descriptor()) if err == nil { if msgOpts.TransportOriginalName != "" { return msgOpts.TransportOriginalName } } return "" } ================================================ FILE: transport/internet/config.pb.go ================================================ package internet import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type TransportProtocol int32 const ( TransportProtocol_TCP TransportProtocol = 0 TransportProtocol_UDP TransportProtocol = 1 TransportProtocol_MKCP TransportProtocol = 2 TransportProtocol_WebSocket TransportProtocol = 3 TransportProtocol_HTTP TransportProtocol = 4 TransportProtocol_DomainSocket TransportProtocol = 5 ) // Enum value maps for TransportProtocol. var ( TransportProtocol_name = map[int32]string{ 0: "TCP", 1: "UDP", 2: "MKCP", 3: "WebSocket", 4: "HTTP", 5: "DomainSocket", } TransportProtocol_value = map[string]int32{ "TCP": 0, "UDP": 1, "MKCP": 2, "WebSocket": 3, "HTTP": 4, "DomainSocket": 5, } ) func (x TransportProtocol) Enum() *TransportProtocol { p := new(TransportProtocol) *p = x return p } func (x TransportProtocol) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (TransportProtocol) Descriptor() protoreflect.EnumDescriptor { return file_transport_internet_config_proto_enumTypes[0].Descriptor() } func (TransportProtocol) Type() protoreflect.EnumType { return &file_transport_internet_config_proto_enumTypes[0] } func (x TransportProtocol) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use TransportProtocol.Descriptor instead. func (TransportProtocol) EnumDescriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{0} } // MPTCP is the state of MPTCP settings. // Define it here to avoid conflict with TCPFastOpenState. type MPTCPState int32 const ( // AsIs is to leave the current MPTCP state as is, unmodified. MPTCPState_AsIs MPTCPState = 0 // Enable is for enabling MPTCP explictly. MPTCPState_Enable MPTCPState = 1 // Disable is for disabling MPTCP explictly. MPTCPState_Disable MPTCPState = 2 ) // Enum value maps for MPTCPState. var ( MPTCPState_name = map[int32]string{ 0: "AsIs", 1: "Enable", 2: "Disable", } MPTCPState_value = map[string]int32{ "AsIs": 0, "Enable": 1, "Disable": 2, } ) func (x MPTCPState) Enum() *MPTCPState { p := new(MPTCPState) *p = x return p } func (x MPTCPState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (MPTCPState) Descriptor() protoreflect.EnumDescriptor { return file_transport_internet_config_proto_enumTypes[1].Descriptor() } func (MPTCPState) Type() protoreflect.EnumType { return &file_transport_internet_config_proto_enumTypes[1] } func (x MPTCPState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use MPTCPState.Descriptor instead. func (MPTCPState) EnumDescriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{1} } type SocketConfig_TCPFastOpenState int32 const ( // AsIs is to leave the current TFO state as is, unmodified. SocketConfig_AsIs SocketConfig_TCPFastOpenState = 0 // Enable is for enabling TFO explictly. SocketConfig_Enable SocketConfig_TCPFastOpenState = 1 // Disable is for disabling TFO explictly. SocketConfig_Disable SocketConfig_TCPFastOpenState = 2 ) // Enum value maps for SocketConfig_TCPFastOpenState. var ( SocketConfig_TCPFastOpenState_name = map[int32]string{ 0: "AsIs", 1: "Enable", 2: "Disable", } SocketConfig_TCPFastOpenState_value = map[string]int32{ "AsIs": 0, "Enable": 1, "Disable": 2, } ) func (x SocketConfig_TCPFastOpenState) Enum() *SocketConfig_TCPFastOpenState { p := new(SocketConfig_TCPFastOpenState) *p = x return p } func (x SocketConfig_TCPFastOpenState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (SocketConfig_TCPFastOpenState) Descriptor() protoreflect.EnumDescriptor { return file_transport_internet_config_proto_enumTypes[2].Descriptor() } func (SocketConfig_TCPFastOpenState) Type() protoreflect.EnumType { return &file_transport_internet_config_proto_enumTypes[2] } func (x SocketConfig_TCPFastOpenState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use SocketConfig_TCPFastOpenState.Descriptor instead. func (SocketConfig_TCPFastOpenState) EnumDescriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{3, 0} } type SocketConfig_TProxyMode int32 const ( // TProxy is off. SocketConfig_Off SocketConfig_TProxyMode = 0 // TProxy mode. SocketConfig_TProxy SocketConfig_TProxyMode = 1 // Redirect mode. SocketConfig_Redirect SocketConfig_TProxyMode = 2 ) // Enum value maps for SocketConfig_TProxyMode. var ( SocketConfig_TProxyMode_name = map[int32]string{ 0: "Off", 1: "TProxy", 2: "Redirect", } SocketConfig_TProxyMode_value = map[string]int32{ "Off": 0, "TProxy": 1, "Redirect": 2, } ) func (x SocketConfig_TProxyMode) Enum() *SocketConfig_TProxyMode { p := new(SocketConfig_TProxyMode) *p = x return p } func (x SocketConfig_TProxyMode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor { return file_transport_internet_config_proto_enumTypes[3].Descriptor() } func (SocketConfig_TProxyMode) Type() protoreflect.EnumType { return &file_transport_internet_config_proto_enumTypes[3] } func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use SocketConfig_TProxyMode.Descriptor instead. func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{3, 1} } type TransportConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Type of network that this settings supports. // Deprecated. Use the string form below. // // Deprecated: Marked as deprecated in transport/internet/config.proto. Protocol TransportProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=v2ray.core.transport.internet.TransportProtocol" json:"protocol,omitempty"` // Type of network that this settings supports. ProtocolName string `protobuf:"bytes,3,opt,name=protocol_name,json=protocolName,proto3" json:"protocol_name,omitempty"` // Specific settings. Must be of the transports. Settings *anypb.Any `protobuf:"bytes,2,opt,name=settings,proto3" json:"settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TransportConfig) Reset() { *x = TransportConfig{} mi := &file_transport_internet_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TransportConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*TransportConfig) ProtoMessage() {} func (x *TransportConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TransportConfig.ProtoReflect.Descriptor instead. func (*TransportConfig) Descriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{0} } // Deprecated: Marked as deprecated in transport/internet/config.proto. func (x *TransportConfig) GetProtocol() TransportProtocol { if x != nil { return x.Protocol } return TransportProtocol_TCP } func (x *TransportConfig) GetProtocolName() string { if x != nil { return x.ProtocolName } return "" } func (x *TransportConfig) GetSettings() *anypb.Any { if x != nil { return x.Settings } return nil } type StreamConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Effective network. Deprecated. Use the string form below. // // Deprecated: Marked as deprecated in transport/internet/config.proto. Protocol TransportProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=v2ray.core.transport.internet.TransportProtocol" json:"protocol,omitempty"` // Effective network. ProtocolName string `protobuf:"bytes,5,opt,name=protocol_name,json=protocolName,proto3" json:"protocol_name,omitempty"` TransportSettings []*TransportConfig `protobuf:"bytes,2,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"` // Type of security. Must be a message name of the settings proto. SecurityType string `protobuf:"bytes,3,opt,name=security_type,json=securityType,proto3" json:"security_type,omitempty"` // Settings for transport security. For now the only choice is TLS. SecuritySettings []*anypb.Any `protobuf:"bytes,4,rep,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"` SocketSettings *SocketConfig `protobuf:"bytes,6,opt,name=socket_settings,json=socketSettings,proto3" json:"socket_settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StreamConfig) Reset() { *x = StreamConfig{} mi := &file_transport_internet_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StreamConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*StreamConfig) ProtoMessage() {} func (x *StreamConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StreamConfig.ProtoReflect.Descriptor instead. func (*StreamConfig) Descriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{1} } // Deprecated: Marked as deprecated in transport/internet/config.proto. func (x *StreamConfig) GetProtocol() TransportProtocol { if x != nil { return x.Protocol } return TransportProtocol_TCP } func (x *StreamConfig) GetProtocolName() string { if x != nil { return x.ProtocolName } return "" } func (x *StreamConfig) GetTransportSettings() []*TransportConfig { if x != nil { return x.TransportSettings } return nil } func (x *StreamConfig) GetSecurityType() string { if x != nil { return x.SecurityType } return "" } func (x *StreamConfig) GetSecuritySettings() []*anypb.Any { if x != nil { return x.SecuritySettings } return nil } func (x *StreamConfig) GetSocketSettings() *SocketConfig { if x != nil { return x.SocketSettings } return nil } type ProxyConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` TransportLayerProxy bool `protobuf:"varint,2,opt,name=transportLayerProxy,proto3" json:"transportLayerProxy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProxyConfig) Reset() { *x = ProxyConfig{} mi := &file_transport_internet_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ProxyConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProxyConfig) ProtoMessage() {} func (x *ProxyConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProxyConfig.ProtoReflect.Descriptor instead. func (*ProxyConfig) Descriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{2} } func (x *ProxyConfig) GetTag() string { if x != nil { return x.Tag } return "" } func (x *ProxyConfig) GetTransportLayerProxy() bool { if x != nil { return x.TransportLayerProxy } return false } // SocketConfig is options to be applied on network sockets. type SocketConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Mark of the connection. If non-zero, the value will be set to SO_MARK. Mark uint32 `protobuf:"varint,1,opt,name=mark,proto3" json:"mark,omitempty"` // TFO is the state of TFO settings. Tfo SocketConfig_TCPFastOpenState `protobuf:"varint,2,opt,name=tfo,proto3,enum=v2ray.core.transport.internet.SocketConfig_TCPFastOpenState" json:"tfo,omitempty"` // TProxy is for enabling TProxy socket option. Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=v2ray.core.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"` // ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket // option. This option is for UDP only. ReceiveOriginalDestAddress bool `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"` BindAddress []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"` BindPort uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"` AcceptProxyProtocol bool `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` TcpKeepAliveInterval int32 `protobuf:"varint,8,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"` TfoQueueLength uint32 `protobuf:"varint,9,opt,name=tfo_queue_length,json=tfoQueueLength,proto3" json:"tfo_queue_length,omitempty"` TcpKeepAliveIdle int32 `protobuf:"varint,10,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"` BindToDevice string `protobuf:"bytes,11,opt,name=bind_to_device,json=bindToDevice,proto3" json:"bind_to_device,omitempty"` RxBufSize int64 `protobuf:"varint,12,opt,name=rx_buf_size,json=rxBufSize,proto3" json:"rx_buf_size,omitempty"` TxBufSize int64 `protobuf:"varint,13,opt,name=tx_buf_size,json=txBufSize,proto3" json:"tx_buf_size,omitempty"` ForceBufSize bool `protobuf:"varint,14,opt,name=force_buf_size,json=forceBufSize,proto3" json:"force_buf_size,omitempty"` Mptcp MPTCPState `protobuf:"varint,15,opt,name=mptcp,proto3,enum=v2ray.core.transport.internet.MPTCPState" json:"mptcp,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SocketConfig) Reset() { *x = SocketConfig{} mi := &file_transport_internet_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SocketConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*SocketConfig) ProtoMessage() {} func (x *SocketConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead. func (*SocketConfig) Descriptor() ([]byte, []int) { return file_transport_internet_config_proto_rawDescGZIP(), []int{3} } func (x *SocketConfig) GetMark() uint32 { if x != nil { return x.Mark } return 0 } func (x *SocketConfig) GetTfo() SocketConfig_TCPFastOpenState { if x != nil { return x.Tfo } return SocketConfig_AsIs } func (x *SocketConfig) GetTproxy() SocketConfig_TProxyMode { if x != nil { return x.Tproxy } return SocketConfig_Off } func (x *SocketConfig) GetReceiveOriginalDestAddress() bool { if x != nil { return x.ReceiveOriginalDestAddress } return false } func (x *SocketConfig) GetBindAddress() []byte { if x != nil { return x.BindAddress } return nil } func (x *SocketConfig) GetBindPort() uint32 { if x != nil { return x.BindPort } return 0 } func (x *SocketConfig) GetAcceptProxyProtocol() bool { if x != nil { return x.AcceptProxyProtocol } return false } func (x *SocketConfig) GetTcpKeepAliveInterval() int32 { if x != nil { return x.TcpKeepAliveInterval } return 0 } func (x *SocketConfig) GetTfoQueueLength() uint32 { if x != nil { return x.TfoQueueLength } return 0 } func (x *SocketConfig) GetTcpKeepAliveIdle() int32 { if x != nil { return x.TcpKeepAliveIdle } return 0 } func (x *SocketConfig) GetBindToDevice() string { if x != nil { return x.BindToDevice } return "" } func (x *SocketConfig) GetRxBufSize() int64 { if x != nil { return x.RxBufSize } return 0 } func (x *SocketConfig) GetTxBufSize() int64 { if x != nil { return x.TxBufSize } return 0 } func (x *SocketConfig) GetForceBufSize() bool { if x != nil { return x.ForceBufSize } return false } func (x *SocketConfig) GetMptcp() MPTCPState { if x != nil { return x.Mptcp } return MPTCPState_AsIs } var File_transport_internet_config_proto protoreflect.FileDescriptor const file_transport_internet_config_proto_rawDesc = "" + "\n" + "\x1ftransport/internet/config.proto\x12\x1dv2ray.core.transport.internet\x1a\x19google/protobuf/any.proto\"\xba\x01\n" + "\x0fTransportConfig\x12P\n" + "\bprotocol\x18\x01 \x01(\x0e20.v2ray.core.transport.internet.TransportProtocolB\x02\x18\x01R\bprotocol\x12#\n" + "\rprotocol_name\x18\x03 \x01(\tR\fprotocolName\x120\n" + "\bsettings\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\bsettings\"\xa2\x03\n" + "\fStreamConfig\x12P\n" + "\bprotocol\x18\x01 \x01(\x0e20.v2ray.core.transport.internet.TransportProtocolB\x02\x18\x01R\bprotocol\x12#\n" + "\rprotocol_name\x18\x05 \x01(\tR\fprotocolName\x12]\n" + "\x12transport_settings\x18\x02 \x03(\v2..v2ray.core.transport.internet.TransportConfigR\x11transportSettings\x12#\n" + "\rsecurity_type\x18\x03 \x01(\tR\fsecurityType\x12A\n" + "\x11security_settings\x18\x04 \x03(\v2\x14.google.protobuf.AnyR\x10securitySettings\x12T\n" + "\x0fsocket_settings\x18\x06 \x01(\v2+.v2ray.core.transport.internet.SocketConfigR\x0esocketSettings\"Q\n" + "\vProxyConfig\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x120\n" + "\x13transportLayerProxy\x18\x02 \x01(\bR\x13transportLayerProxy\"\xbe\x06\n" + "\fSocketConfig\x12\x12\n" + "\x04mark\x18\x01 \x01(\rR\x04mark\x12N\n" + "\x03tfo\x18\x02 \x01(\x0e2<.v2ray.core.transport.internet.SocketConfig.TCPFastOpenStateR\x03tfo\x12N\n" + "\x06tproxy\x18\x03 \x01(\x0e26.v2ray.core.transport.internet.SocketConfig.TProxyModeR\x06tproxy\x12A\n" + "\x1dreceive_original_dest_address\x18\x04 \x01(\bR\x1areceiveOriginalDestAddress\x12!\n" + "\fbind_address\x18\x05 \x01(\fR\vbindAddress\x12\x1b\n" + "\tbind_port\x18\x06 \x01(\rR\bbindPort\x122\n" + "\x15accept_proxy_protocol\x18\a \x01(\bR\x13acceptProxyProtocol\x125\n" + "\x17tcp_keep_alive_interval\x18\b \x01(\x05R\x14tcpKeepAliveInterval\x12(\n" + "\x10tfo_queue_length\x18\t \x01(\rR\x0etfoQueueLength\x12-\n" + "\x13tcp_keep_alive_idle\x18\n" + " \x01(\x05R\x10tcpKeepAliveIdle\x12$\n" + "\x0ebind_to_device\x18\v \x01(\tR\fbindToDevice\x12\x1e\n" + "\vrx_buf_size\x18\f \x01(\x03R\trxBufSize\x12\x1e\n" + "\vtx_buf_size\x18\r \x01(\x03R\ttxBufSize\x12$\n" + "\x0eforce_buf_size\x18\x0e \x01(\bR\fforceBufSize\x12?\n" + "\x05mptcp\x18\x0f \x01(\x0e2).v2ray.core.transport.internet.MPTCPStateR\x05mptcp\"5\n" + "\x10TCPFastOpenState\x12\b\n" + "\x04AsIs\x10\x00\x12\n" + "\n" + "\x06Enable\x10\x01\x12\v\n" + "\aDisable\x10\x02\"/\n" + "\n" + "TProxyMode\x12\a\n" + "\x03Off\x10\x00\x12\n" + "\n" + "\x06TProxy\x10\x01\x12\f\n" + "\bRedirect\x10\x02*Z\n" + "\x11TransportProtocol\x12\a\n" + "\x03TCP\x10\x00\x12\a\n" + "\x03UDP\x10\x01\x12\b\n" + "\x04MKCP\x10\x02\x12\r\n" + "\tWebSocket\x10\x03\x12\b\n" + "\x04HTTP\x10\x04\x12\x10\n" + "\fDomainSocket\x10\x05*/\n" + "\n" + "MPTCPState\x12\b\n" + "\x04AsIs\x10\x00\x12\n" + "\n" + "\x06Enable\x10\x01\x12\v\n" + "\aDisable\x10\x02Bx\n" + "!com.v2ray.core.transport.internetP\x01Z1github.com/v2fly/v2ray-core/v5/transport/internet\xaa\x02\x1dV2Ray.Core.Transport.Internetb\x06proto3" var ( file_transport_internet_config_proto_rawDescOnce sync.Once file_transport_internet_config_proto_rawDescData []byte ) func file_transport_internet_config_proto_rawDescGZIP() []byte { file_transport_internet_config_proto_rawDescOnce.Do(func() { file_transport_internet_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc))) }) return file_transport_internet_config_proto_rawDescData } var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 4) var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_transport_internet_config_proto_goTypes = []any{ (TransportProtocol)(0), // 0: v2ray.core.transport.internet.TransportProtocol (MPTCPState)(0), // 1: v2ray.core.transport.internet.MPTCPState (SocketConfig_TCPFastOpenState)(0), // 2: v2ray.core.transport.internet.SocketConfig.TCPFastOpenState (SocketConfig_TProxyMode)(0), // 3: v2ray.core.transport.internet.SocketConfig.TProxyMode (*TransportConfig)(nil), // 4: v2ray.core.transport.internet.TransportConfig (*StreamConfig)(nil), // 5: v2ray.core.transport.internet.StreamConfig (*ProxyConfig)(nil), // 6: v2ray.core.transport.internet.ProxyConfig (*SocketConfig)(nil), // 7: v2ray.core.transport.internet.SocketConfig (*anypb.Any)(nil), // 8: google.protobuf.Any } var file_transport_internet_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.transport.internet.TransportConfig.protocol:type_name -> v2ray.core.transport.internet.TransportProtocol 8, // 1: v2ray.core.transport.internet.TransportConfig.settings:type_name -> google.protobuf.Any 0, // 2: v2ray.core.transport.internet.StreamConfig.protocol:type_name -> v2ray.core.transport.internet.TransportProtocol 4, // 3: v2ray.core.transport.internet.StreamConfig.transport_settings:type_name -> v2ray.core.transport.internet.TransportConfig 8, // 4: v2ray.core.transport.internet.StreamConfig.security_settings:type_name -> google.protobuf.Any 7, // 5: v2ray.core.transport.internet.StreamConfig.socket_settings:type_name -> v2ray.core.transport.internet.SocketConfig 2, // 6: v2ray.core.transport.internet.SocketConfig.tfo:type_name -> v2ray.core.transport.internet.SocketConfig.TCPFastOpenState 3, // 7: v2ray.core.transport.internet.SocketConfig.tproxy:type_name -> v2ray.core.transport.internet.SocketConfig.TProxyMode 1, // 8: v2ray.core.transport.internet.SocketConfig.mptcp:type_name -> v2ray.core.transport.internet.MPTCPState 9, // [9:9] is the sub-list for method output_type 9, // [9:9] is the sub-list for method input_type 9, // [9:9] is the sub-list for extension type_name 9, // [9:9] is the sub-list for extension extendee 0, // [0:9] is the sub-list for field type_name } func init() { file_transport_internet_config_proto_init() } func file_transport_internet_config_proto_init() { if File_transport_internet_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc)), NumEnums: 4, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_config_proto_goTypes, DependencyIndexes: file_transport_internet_config_proto_depIdxs, EnumInfos: file_transport_internet_config_proto_enumTypes, MessageInfos: file_transport_internet_config_proto_msgTypes, }.Build() File_transport_internet_config_proto = out.File file_transport_internet_config_proto_goTypes = nil file_transport_internet_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet; option csharp_namespace = "V2Ray.Core.Transport.Internet"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet"; option java_package = "com.v2ray.core.transport.internet"; option java_multiple_files = true; import "google/protobuf/any.proto"; enum TransportProtocol { TCP = 0; UDP = 1; MKCP = 2; WebSocket = 3; HTTP = 4; DomainSocket = 5; } message TransportConfig { // Type of network that this settings supports. // Deprecated. Use the string form below. TransportProtocol protocol = 1 [ deprecated = true ]; // Type of network that this settings supports. string protocol_name = 3; // Specific settings. Must be of the transports. google.protobuf.Any settings = 2; } message StreamConfig { // Effective network. Deprecated. Use the string form below. TransportProtocol protocol = 1 [ deprecated = true ]; // Effective network. string protocol_name = 5; repeated TransportConfig transport_settings = 2; // Type of security. Must be a message name of the settings proto. string security_type = 3; // Settings for transport security. For now the only choice is TLS. repeated google.protobuf.Any security_settings = 4; SocketConfig socket_settings = 6; } message ProxyConfig { string tag = 1; bool transportLayerProxy = 2; } // MPTCP is the state of MPTCP settings. // Define it here to avoid conflict with TCPFastOpenState. enum MPTCPState { // AsIs is to leave the current MPTCP state as is, unmodified. AsIs = 0; // Enable is for enabling MPTCP explictly. Enable = 1; // Disable is for disabling MPTCP explictly. Disable = 2; } // SocketConfig is options to be applied on network sockets. message SocketConfig { // Mark of the connection. If non-zero, the value will be set to SO_MARK. uint32 mark = 1; enum TCPFastOpenState { // AsIs is to leave the current TFO state as is, unmodified. AsIs = 0; // Enable is for enabling TFO explictly. Enable = 1; // Disable is for disabling TFO explictly. Disable = 2; } // TFO is the state of TFO settings. TCPFastOpenState tfo = 2; enum TProxyMode { // TProxy is off. Off = 0; // TProxy mode. TProxy = 1; // Redirect mode. Redirect = 2; } // TProxy is for enabling TProxy socket option. TProxyMode tproxy = 3; // ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket // option. This option is for UDP only. bool receive_original_dest_address = 4; bytes bind_address = 5; uint32 bind_port = 6; bool accept_proxy_protocol = 7; int32 tcp_keep_alive_interval = 8; uint32 tfo_queue_length = 9; int32 tcp_keep_alive_idle = 10; string bind_to_device = 11; int64 rx_buf_size = 12; int64 tx_buf_size = 13; bool force_buf_size = 14; MPTCPState mptcp = 15; } ================================================ FILE: transport/internet/connection.go ================================================ package internet import ( "net" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/features/stats" ) type Connection interface { net.Conn } type AbstractPacketConnReader interface { ReadFrom(p []byte) (n int, addr net.Addr, err error) } type AbstractPacketConnWriter interface { WriteTo(p []byte, addr net.Addr) (n int, err error) } type AbstractPacketConn interface { AbstractPacketConnReader AbstractPacketConnWriter common.Closable } type PacketConn interface { AbstractPacketConn net.PacketConn } type StatCouterConnection struct { Connection ReadCounter stats.Counter WriteCounter stats.Counter } func (c *StatCouterConnection) Read(b []byte) (int, error) { nBytes, err := c.Connection.Read(b) if c.ReadCounter != nil { c.ReadCounter.Add(int64(nBytes)) } return nBytes, err } func (c *StatCouterConnection) Write(b []byte) (int, error) { nBytes, err := c.Connection.Write(b) if c.WriteCounter != nil { c.WriteCounter.Add(int64(nBytes)) } return nBytes, err } ================================================ FILE: transport/internet/dialer.go ================================================ package internet import ( "context" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet/tagged" ) // Dialer is the interface for dialing outbound connections. type Dialer interface { // Dial dials a system connection to the given destination. Dial(ctx context.Context, destination net.Destination) (Connection, error) // Address returns the address used by this Dialer. Maybe nil if not known. Address() net.Address } // dialFunc is an interface to dial network connection to a specific destination. type dialFunc func(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (Connection, error) var transportDialerCache = make(map[string]dialFunc) // RegisterTransportDialer registers a Dialer with given name. func RegisterTransportDialer(protocol string, dialer dialFunc) error { if _, found := transportDialerCache[protocol]; found { return newError(protocol, " dialer already registered").AtError() } transportDialerCache[protocol] = dialer return nil } // Dial dials a internet connection towards the given destination. func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (Connection, error) { if dest.Network == net.Network_TCP { if streamSettings == nil { s, err := ToMemoryStreamConfig(nil) if err != nil { return nil, newError("failed to create default stream settings").Base(err) } streamSettings = s } protocol := streamSettings.ProtocolName if originalProtocolName := getOriginalMessageName(streamSettings); originalProtocolName != "" { protocol = originalProtocolName } dialer := transportDialerCache[protocol] if dialer == nil { return nil, newError(protocol, " dialer not registered").AtError() } return dialer(ctx, dest, streamSettings) } if dest.Network == net.Network_UDP { udpDialer := transportDialerCache["udp"] if udpDialer == nil { return nil, newError("UDP dialer not registered").AtError() } return udpDialer(ctx, dest, streamSettings) } return nil, newError("unknown network ", dest.Network) } // DialSystem calls system dialer to create a network connection. func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { outbound := session.OutboundFromContext(ctx) var src net.Address if outbound != nil { src = outbound.Gateway } if transportLayerOutgoingTag := session.GetTransportLayerProxyTagFromContext(ctx); transportLayerOutgoingTag != "" { return DialTaggedOutbound(ctx, dest, transportLayerOutgoingTag) } originalAddr := dest.Address if outbound != nil && outbound.Resolver != nil && dest.Address.Family().IsDomain() { if addr := outbound.Resolver(ctx, dest.Address.Domain()); addr != nil { dest.Address = addr } } switch { case src != nil && dest.Address != originalAddr: newError("dialing to ", dest, " resolved from ", originalAddr, " via ", src).WriteToLog(session.ExportIDToError(ctx)) case src != nil: newError("dialing to ", dest, " via ", src).WriteToLog(session.ExportIDToError(ctx)) case dest.Address != originalAddr: newError("dialing to ", dest, " resolved from ", originalAddr).WriteToLog(session.ExportIDToError(ctx)) } return effectiveSystemDialer.Dial(ctx, src, dest, sockopt) } func DialTaggedOutbound(ctx context.Context, dest net.Destination, tag string) (net.Conn, error) { if tagged.Dialer == nil { return nil, newError("tagged dial not enabled") } return tagged.Dialer(ctx, dest, tag) } ================================================ FILE: transport/internet/dialer_test.go ================================================ package internet_test import ( "context" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" . "github.com/v2fly/v2ray-core/v5/transport/internet" ) func TestDialWithLocalAddr(t *testing.T) { server := &tcp.Server{} dest, err := server.Start() common.Must(err) defer server.Close() conn, err := DialSystem(context.Background(), net.TCPDestination(net.LocalHostIP, dest.Port), nil) common.Must(err) if r := cmp.Diff(conn.RemoteAddr().String(), "127.0.0.1:"+dest.Port.String()); r != "" { t.Error(r) } conn.Close() } ================================================ FILE: transport/internet/domainsocket/config.go ================================================ package domainsocket import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) const ( protocolName = "domainsocket" sizeofSunPath = 108 ) func (c *Config) GetUnixAddr() (*net.UnixAddr, error) { path := c.Path if path == "" { return nil, newError("empty domain socket path") } if c.Abstract && path[0] != '\x00' { path = "\x00" + path } if c.Abstract && c.Padding { raw := []byte(path) addr := make([]byte, sizeofSunPath) copy(addr, raw) path = string(addr) } return &net.UnixAddr{ Name: path, Net: "unix", }, nil } func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/domainsocket/config.pb.go ================================================ package domainsocket import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // Path of the domain socket. This overrides the IP/Port parameter from // upstream caller. Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // Abstract specifies whether to use abstract namespace or not. // Traditionally Unix domain socket is file system based. Abstract domain // socket can be used without acquiring file lock. Abstract bool `protobuf:"varint,2,opt,name=abstract,proto3" json:"abstract,omitempty"` // Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to // connect(2) or bind(2) when using abstract UDS. Padding bool `protobuf:"varint,3,opt,name=padding,proto3" json:"padding,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_domainsocket_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_domainsocket_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_domainsocket_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetPath() string { if x != nil { return x.Path } return "" } func (x *Config) GetAbstract() bool { if x != nil { return x.Abstract } return false } func (x *Config) GetPadding() bool { if x != nil { return x.Padding } return false } var File_transport_internet_domainsocket_config_proto protoreflect.FileDescriptor const file_transport_internet_domainsocket_config_proto_rawDesc = "" + "\n" + ",transport/internet/domainsocket/config.proto\x12*v2ray.core.transport.internet.domainsocket\x1a common/protoext/extensions.proto\"q\n" + "\x06Config\x12\x12\n" + "\x04path\x18\x01 \x01(\tR\x04path\x12\x1a\n" + "\babstract\x18\x02 \x01(\bR\babstract\x12\x18\n" + "\apadding\x18\x03 \x01(\bR\apadding:\x1d\x82\xb5\x18\x19\n" + "\ttransport\x12\fdomainsocketB\x9f\x01\n" + ".com.v2ray.core.transport.internet.domainsocketP\x01Z>github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket\xaa\x02*V2Ray.Core.Transport.Internet.DomainSocketb\x06proto3" var ( file_transport_internet_domainsocket_config_proto_rawDescOnce sync.Once file_transport_internet_domainsocket_config_proto_rawDescData []byte ) func file_transport_internet_domainsocket_config_proto_rawDescGZIP() []byte { file_transport_internet_domainsocket_config_proto_rawDescOnce.Do(func() { file_transport_internet_domainsocket_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_domainsocket_config_proto_rawDesc), len(file_transport_internet_domainsocket_config_proto_rawDesc))) }) return file_transport_internet_domainsocket_config_proto_rawDescData } var file_transport_internet_domainsocket_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_domainsocket_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.domainsocket.Config } var file_transport_internet_domainsocket_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_domainsocket_config_proto_init() } func file_transport_internet_domainsocket_config_proto_init() { if File_transport_internet_domainsocket_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_domainsocket_config_proto_rawDesc), len(file_transport_internet_domainsocket_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_domainsocket_config_proto_goTypes, DependencyIndexes: file_transport_internet_domainsocket_config_proto_depIdxs, MessageInfos: file_transport_internet_domainsocket_config_proto_msgTypes, }.Build() File_transport_internet_domainsocket_config_proto = out.File file_transport_internet_domainsocket_config_proto_goTypes = nil file_transport_internet_domainsocket_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/domainsocket/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.domainsocket; option csharp_namespace = "V2Ray.Core.Transport.Internet.DomainSocket"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket"; option java_package = "com.v2ray.core.transport.internet.domainsocket"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "domainsocket"; // Path of the domain socket. This overrides the IP/Port parameter from // upstream caller. string path = 1; // Abstract specifies whether to use abstract namespace or not. // Traditionally Unix domain socket is file system based. Abstract domain // socket can be used without acquiring file lock. bool abstract = 2; // Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to // connect(2) or bind(2) when using abstract UDS. bool padding = 3; } ================================================ FILE: transport/internet/domainsocket/dial.go ================================================ //go:build !windows && !wasm // +build !windows,!wasm package domainsocket import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { settings := streamSettings.ProtocolSettings.(*Config) addr, err := settings.GetUnixAddr() if err != nil { return nil, err } conn, err := net.DialUnix("unix", nil, addr) if err != nil { return nil, newError("failed to dial unix: ", settings.Path).Base(err).AtWarning() } if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { return tls.Client(conn, config.GetTLSConfig(tls.WithDestination(dest))), nil } return conn, nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, Dial)) } ================================================ FILE: transport/internet/domainsocket/errgen.go ================================================ package domainsocket //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/domainsocket/errors.generated.go ================================================ package domainsocket import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/domainsocket/listener.go ================================================ //go:build !windows && !wasm // +build !windows,!wasm package domainsocket import ( "context" gotls "crypto/tls" "os" "strings" "golang.org/x/sys/unix" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) type Listener struct { addr *net.UnixAddr ln net.Listener tlsConfig *gotls.Config config *Config addConn internet.ConnHandler locker *fileLocker } func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { settings := streamSettings.ProtocolSettings.(*Config) addr, err := settings.GetUnixAddr() if err != nil { return nil, err } unixListener, err := net.ListenUnix("unix", addr) if err != nil { return nil, newError("failed to listen domain socket").Base(err).AtWarning() } ln := &Listener{ addr: addr, ln: unixListener, config: settings, addConn: handler, } if !settings.Abstract { ln.locker = &fileLocker{ path: settings.Path + ".lock", } if err := ln.locker.Acquire(); err != nil { unixListener.Close() return nil, err } } if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { ln.tlsConfig = config.GetTLSConfig() } go ln.run() return ln, nil } func (ln *Listener) Addr() net.Addr { return ln.addr } func (ln *Listener) Close() error { if ln.locker != nil { ln.locker.Release() } return ln.ln.Close() } func (ln *Listener) run() { for { conn, err := ln.ln.Accept() if err != nil { if strings.Contains(err.Error(), "closed") { break } newError("failed to accepted raw connections").Base(err).AtWarning().WriteToLog() continue } if ln.tlsConfig != nil { conn = tls.Server(conn, ln.tlsConfig) } ln.addConn(internet.Connection(conn)) } } type fileLocker struct { path string file *os.File } func (fl *fileLocker) Acquire() error { f, err := os.Create(fl.path) if err != nil { return err } if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { f.Close() return newError("failed to lock file: ", fl.path).Base(err) } fl.file = f return nil } func (fl *fileLocker) Release() { if err := unix.Flock(int(fl.file.Fd()), unix.LOCK_UN); err != nil { newError("failed to unlock file: ", fl.path).Base(err).WriteToLog() } if err := fl.file.Close(); err != nil { newError("failed to close file: ", fl.path).Base(err).WriteToLog() } if err := os.Remove(fl.path); err != nil { newError("failed to remove file: ", fl.path).Base(err).WriteToLog() } } func init() { common.Must(internet.RegisterTransportListener(protocolName, Listen)) } ================================================ FILE: transport/internet/domainsocket/listener_test.go ================================================ //go:build !windows && !android // +build !windows,!android package domainsocket_test import ( "context" "runtime" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" . "github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket" ) func TestListen(t *testing.T) { ctx := context.Background() streamSettings := &internet.MemoryStreamConfig{ ProtocolName: "domainsocket", ProtocolSettings: &Config{ Path: "/tmp/ts3", }, } listener, err := Listen(ctx, nil, net.Port(0), streamSettings, func(conn internet.Connection) { defer conn.Close() b := buf.New() defer b.Release() common.Must2(b.ReadFrom(conn)) b.WriteString("Response") common.Must2(conn.Write(b.Bytes())) }) common.Must(err) defer listener.Close() conn, err := Dial(ctx, net.Destination{}, streamSettings) common.Must(err) defer conn.Close() common.Must2(conn.Write([]byte("Request"))) b := buf.New() defer b.Release() common.Must2(b.ReadFrom(conn)) if b.String() != "RequestResponse" { t.Error("expected response as 'RequestResponse' but got ", b.String()) } } func TestListenAbstract(t *testing.T) { if runtime.GOOS != "linux" { return } ctx := context.Background() streamSettings := &internet.MemoryStreamConfig{ ProtocolName: "domainsocket", ProtocolSettings: &Config{ Path: "/tmp/ts3", Abstract: true, }, } listener, err := Listen(ctx, nil, net.Port(0), streamSettings, func(conn internet.Connection) { defer conn.Close() b := buf.New() defer b.Release() common.Must2(b.ReadFrom(conn)) b.WriteString("Response") common.Must2(conn.Write(b.Bytes())) }) common.Must(err) defer listener.Close() conn, err := Dial(ctx, net.Destination{}, streamSettings) common.Must(err) defer conn.Close() common.Must2(conn.Write([]byte("Request"))) b := buf.New() defer b.Release() common.Must2(b.ReadFrom(conn)) if b.String() != "RequestResponse" { t.Error("expected response as 'RequestResponse' but got ", b.String()) } } ================================================ FILE: transport/internet/dtls/config.pb.go ================================================ package dtls import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type DTLSMode int32 const ( DTLSMode_INVALID DTLSMode = 0 DTLSMode_PSK DTLSMode = 1 ) // Enum value maps for DTLSMode. var ( DTLSMode_name = map[int32]string{ 0: "INVALID", 1: "PSK", } DTLSMode_value = map[string]int32{ "INVALID": 0, "PSK": 1, } ) func (x DTLSMode) Enum() *DTLSMode { p := new(DTLSMode) *p = x return p } func (x DTLSMode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (DTLSMode) Descriptor() protoreflect.EnumDescriptor { return file_transport_internet_dtls_config_proto_enumTypes[0].Descriptor() } func (DTLSMode) Type() protoreflect.EnumType { return &file_transport_internet_dtls_config_proto_enumTypes[0] } func (x DTLSMode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use DTLSMode.Descriptor instead. func (DTLSMode) EnumDescriptor() ([]byte, []int) { return file_transport_internet_dtls_config_proto_rawDescGZIP(), []int{0} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Mode DTLSMode `protobuf:"varint,1,opt,name=mode,proto3,enum=v2ray.core.transport.internet.dtls.DTLSMode" json:"mode,omitempty"` Psk []byte `protobuf:"bytes,2,opt,name=psk,proto3" json:"psk,omitempty"` Mtu uint32 `protobuf:"varint,3,opt,name=mtu,proto3" json:"mtu,omitempty"` ReplayProtectionWindow uint32 `protobuf:"varint,4,opt,name=replay_protection_window,json=replayProtectionWindow,proto3" json:"replay_protection_window,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_dtls_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_dtls_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_dtls_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetMode() DTLSMode { if x != nil { return x.Mode } return DTLSMode_INVALID } func (x *Config) GetPsk() []byte { if x != nil { return x.Psk } return nil } func (x *Config) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } func (x *Config) GetReplayProtectionWindow() uint32 { if x != nil { return x.ReplayProtectionWindow } return 0 } var File_transport_internet_dtls_config_proto protoreflect.FileDescriptor const file_transport_internet_dtls_config_proto_rawDesc = "" + "\n" + "$transport/internet/dtls/config.proto\x12\"v2ray.core.transport.internet.dtls\x1a common/protoext/extensions.proto\"\xbf\x01\n" + "\x06Config\x12@\n" + "\x04mode\x18\x01 \x01(\x0e2,.v2ray.core.transport.internet.dtls.DTLSModeR\x04mode\x12\x10\n" + "\x03psk\x18\x02 \x01(\fR\x03psk\x12\x10\n" + "\x03mtu\x18\x03 \x01(\rR\x03mtu\x128\n" + "\x18replay_protection_window\x18\x04 \x01(\rR\x16replayProtectionWindow:\x15\x82\xb5\x18\x11\n" + "\ttransport\x12\x04dtls* \n" + "\bDTLSMode\x12\v\n" + "\aINVALID\x10\x00\x12\a\n" + "\x03PSK\x10\x01B\x87\x01\n" + "&com.v2ray.core.transport.internet.dtlsP\x01Z6github.com/v2fly/v2ray-core/v5/transport/internet/dtls\xaa\x02\"V2Ray.Core.Transport.Internet.Dtlsb\x06proto3" var ( file_transport_internet_dtls_config_proto_rawDescOnce sync.Once file_transport_internet_dtls_config_proto_rawDescData []byte ) func file_transport_internet_dtls_config_proto_rawDescGZIP() []byte { file_transport_internet_dtls_config_proto_rawDescOnce.Do(func() { file_transport_internet_dtls_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_dtls_config_proto_rawDesc), len(file_transport_internet_dtls_config_proto_rawDesc))) }) return file_transport_internet_dtls_config_proto_rawDescData } var file_transport_internet_dtls_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_transport_internet_dtls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_dtls_config_proto_goTypes = []any{ (DTLSMode)(0), // 0: v2ray.core.transport.internet.dtls.DTLSMode (*Config)(nil), // 1: v2ray.core.transport.internet.dtls.Config } var file_transport_internet_dtls_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.transport.internet.dtls.Config.mode:type_name -> v2ray.core.transport.internet.dtls.DTLSMode 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_dtls_config_proto_init() } func file_transport_internet_dtls_config_proto_init() { if File_transport_internet_dtls_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_dtls_config_proto_rawDesc), len(file_transport_internet_dtls_config_proto_rawDesc)), NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_dtls_config_proto_goTypes, DependencyIndexes: file_transport_internet_dtls_config_proto_depIdxs, EnumInfos: file_transport_internet_dtls_config_proto_enumTypes, MessageInfos: file_transport_internet_dtls_config_proto_msgTypes, }.Build() File_transport_internet_dtls_config_proto = out.File file_transport_internet_dtls_config_proto_goTypes = nil file_transport_internet_dtls_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/dtls/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.dtls; option csharp_namespace = "V2Ray.Core.Transport.Internet.Dtls"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/dtls"; option java_package = "com.v2ray.core.transport.internet.dtls"; option java_multiple_files = true; import "common/protoext/extensions.proto"; enum DTLSMode { INVALID = 0; PSK = 1; } message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "dtls"; DTLSMode mode = 1; bytes psk = 2; uint32 mtu = 3; uint32 replay_protection_window = 4; } ================================================ FILE: transport/internet/dtls/dialer.go ================================================ package dtls import ( "context" "github.com/pion/dtls/v2" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func dialDTLS(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) { transportConfiguration := streamSettings.ProtocolSettings.(*Config) newError("dialing DTLS to ", dest).WriteToLog() transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) dialer := transportEnvironment.Dialer() rawConn, err := dialer.Dial(ctx, nil, dest, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err) } config := &dtls.Config{} config.MTU = int(transportConfiguration.Mtu) config.ReplayProtectionWindow = int(transportConfiguration.ReplayProtectionWindow) switch transportConfiguration.Mode { case DTLSMode_PSK: config.PSK = func(bytes []byte) ([]byte, error) { return transportConfiguration.Psk, nil } config.PSKIdentityHint = []byte("") config.CipherSuites = []dtls.CipherSuiteID{dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256} default: return nil, newError("unknow dtls mode") } return dtls.Client(rawConn, config) } func dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn, err := dialDTLS(ctx, dest, streamSettings) if err != nil { return nil, newError("failed to dial request to ", dest).Base(err) } return internet.Connection(conn), nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, dial)) } ================================================ FILE: transport/internet/dtls/dtls.go ================================================ package dtls import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const protocolName = "dtls" func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/dtls/errors.generated.go ================================================ package dtls import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/dtls/listener.go ================================================ package dtls import ( "context" "io" gonet "net" "sync" "time" "github.com/pion/dtls/v2" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) type Listener struct { config *Config sync.Mutex addConn internet.ConnHandler hub *udp.Hub sessions map[ConnectionID]*dTLSConnWrapped } func (l *Listener) Close() error { return l.hub.Close() } func (l *Listener) Addr() net.Addr { return l.hub.Addr() } type ConnectionID struct { Remote net.Address Port net.Port } func newDTLSServerConn(src net.Destination, parent *Listener) *dTLSConn { ctx := context.Background() ctx, finish := context.WithCancel(ctx) return &dTLSConn{ src: src, parent: parent, readChan: make(chan *buf.Buffer, 256), ctx: ctx, finish: finish, } } type dTLSConnWrapped struct { unencryptedConn *dTLSConn dTLSConn *dtls.Conn } type dTLSConn struct { src net.Destination parent *Listener readChan chan *buf.Buffer ctx context.Context finish func() } func (l *dTLSConn) Read(b []byte) (n int, err error) { select { case pack := <-l.readChan: n := copy(b, pack.Bytes()) defer pack.Release() if n < int(pack.Len()) { return n, io.ErrShortBuffer } return n, nil case <-l.ctx.Done(): return 0, l.ctx.Err() } } func (l *dTLSConn) Write(b []byte) (n int, err error) { return l.parent.hub.WriteTo(b, l.src) } func (l *dTLSConn) Close() error { l.finish() l.parent.Remove(l.src) return nil } func (l *dTLSConn) LocalAddr() gonet.Addr { return nil } func (l *dTLSConn) RemoteAddr() gonet.Addr { return &net.UDPAddr{ IP: l.src.Address.IP(), Port: int(l.src.Port.Value()), } } func (l *dTLSConn) SetDeadline(t time.Time) error { return nil } func (l *dTLSConn) SetReadDeadline(t time.Time) error { return nil } func (l *dTLSConn) SetWriteDeadline(t time.Time) error { return nil } func (l *dTLSConn) OnReceive(payload *buf.Buffer) { select { case l.readChan <- payload: default: } } func NewListener(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (*Listener, error) { transportConfiguration := streamSettings.ProtocolSettings.(*Config) hub, err := udp.ListenUDP(ctx, address, port, streamSettings, udp.HubCapacity(1024)) if err != nil { return nil, err } l := &Listener{ addConn: addConn, config: transportConfiguration, sessions: make(map[ConnectionID]*dTLSConnWrapped), } l.Lock() l.hub = hub l.Unlock() newError("listening on ", address, ":", port).WriteToLog() go l.handlePackets() return l, err } func (l *Listener) handlePackets() { receive := l.hub.Receive() for payload := range receive { l.OnReceive(payload.Payload, payload.Source) } } func newDTLSConnWrapped(unencryptedConnection *dTLSConn, transportConfiguration *Config) (*dtls.Conn, error) { config := &dtls.Config{} config.MTU = int(transportConfiguration.Mtu) config.ReplayProtectionWindow = int(transportConfiguration.ReplayProtectionWindow) switch transportConfiguration.Mode { case DTLSMode_PSK: config.PSK = func(bytes []byte) ([]byte, error) { return transportConfiguration.Psk, nil } config.PSKIdentityHint = []byte("") config.CipherSuites = []dtls.CipherSuiteID{dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256} default: newError("unknown dtls mode").WriteToLog() } dtlsConn, err := dtls.Server(unencryptedConnection, config) if err != nil { return nil, newError("unable to create dtls server conn").Base(err) } return dtlsConn, err } func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination) { id := ConnectionID{ Remote: src.Address, Port: src.Port, } l.Lock() defer l.Unlock() conn, found := l.sessions[id] if !found { var err error unEncryptedConn := newDTLSServerConn(src, l) conn = &dTLSConnWrapped{unencryptedConn: unEncryptedConn} l.sessions[id] = conn go func() { conn.dTLSConn, err = newDTLSConnWrapped(unEncryptedConn, l.config) if err != nil { newError("unable to accept new dtls connection").Base(err).WriteToLog() return } l.addConn(internet.Connection(conn.dTLSConn)) }() } conn.unencryptedConn.OnReceive(payload) } func (l *Listener) Remove(src net.Destination) { l.Lock() defer l.Unlock() id := ConnectionID{ Remote: src.Address, Port: src.Port, } delete(l.sessions, id) } func ListenDTLS(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { return NewListener(ctx, address, port, streamSettings, addConn) } func init() { common.Must(internet.RegisterTransportListener(protocolName, ListenDTLS)) } ================================================ FILE: transport/internet/errors.generated.go ================================================ package internet import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/filelocker.go ================================================ package internet import ( "os" ) // FileLocker is UDS access lock type FileLocker struct { path string file *os.File } ================================================ FILE: transport/internet/filelocker_other.go ================================================ //go:build !windows // +build !windows package internet import ( "os" "golang.org/x/sys/unix" ) // Acquire lock func (fl *FileLocker) Acquire() error { f, err := os.Create(fl.path) if err != nil { return err } if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { f.Close() return newError("failed to lock file: ", fl.path).Base(err) } fl.file = f return nil } // Release lock func (fl *FileLocker) Release() { if err := unix.Flock(int(fl.file.Fd()), unix.LOCK_UN); err != nil { newError("failed to unlock file: ", fl.path).Base(err).WriteToLog() } if err := fl.file.Close(); err != nil { newError("failed to close file: ", fl.path).Base(err).WriteToLog() } if err := os.Remove(fl.path); err != nil { newError("failed to remove file: ", fl.path).Base(err).WriteToLog() } } ================================================ FILE: transport/internet/filelocker_windows.go ================================================ package internet // Acquire lock func (fl *FileLocker) Acquire() error { return nil } // Release lock func (fl *FileLocker) Release() { return } ================================================ FILE: transport/internet/grpc/config.go ================================================ package grpc import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) const protocolName = "gun" func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/grpc/config.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 // protoc v6.33.1 // source: transport/internet/grpc/config.proto package grpc import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` IdleTimeout int32 `protobuf:"varint,3,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` HealthCheckTimeout int32 `protobuf:"varint,4,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` PermitWithoutStream bool `protobuf:"varint,5,opt,name=permit_without_stream,json=permitWithoutStream,proto3" json:"permit_without_stream,omitempty"` InitialWindowsSize int32 `protobuf:"varint,6,opt,name=initial_windows_size,json=initialWindowsSize,proto3" json:"initial_windows_size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_grpc_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_grpc_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_grpc_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetHost() string { if x != nil { return x.Host } return "" } func (x *Config) GetServiceName() string { if x != nil { return x.ServiceName } return "" } func (x *Config) GetIdleTimeout() int32 { if x != nil { return x.IdleTimeout } return 0 } func (x *Config) GetHealthCheckTimeout() int32 { if x != nil { return x.HealthCheckTimeout } return 0 } func (x *Config) GetPermitWithoutStream() bool { if x != nil { return x.PermitWithoutStream } return false } func (x *Config) GetInitialWindowsSize() int32 { if x != nil { return x.InitialWindowsSize } return 0 } var File_transport_internet_grpc_config_proto protoreflect.FileDescriptor const file_transport_internet_grpc_config_proto_rawDesc = "" + "\n" + "$transport/internet/grpc/config.proto\x12+v2ray.core.transport.internet.grpc.encoding\x1a common/protoext/extensions.proto\"\x9c\x02\n" + "\x06Config\x12\x12\n" + "\x04host\x18\x01 \x01(\tR\x04host\x12!\n" + "\fservice_name\x18\x02 \x01(\tR\vserviceName\x12!\n" + "\fidle_timeout\x18\x03 \x01(\x05R\vidleTimeout\x120\n" + "\x14health_check_timeout\x18\x04 \x01(\x05R\x12healthCheckTimeout\x122\n" + "\x15permit_without_stream\x18\x05 \x01(\bR\x13permitWithoutStream\x120\n" + "\x14initial_windows_size\x18\x06 \x01(\x05R\x12initialWindowsSize: \x82\xb5\x18\x1c\n" + "\ttransport\x12\x04grpc\x8a\xff)\x03gun\x90\xff)\x01B\x85\x01\n" + "&com.v2ray.core.transport.internet.grpcZ6github.com/v2fly/v2ray-core/v5/transport/internet/grpc\xaa\x02\"V2Ray.Core.Transport.Internet.Grpcb\x06proto3" var ( file_transport_internet_grpc_config_proto_rawDescOnce sync.Once file_transport_internet_grpc_config_proto_rawDescData []byte ) func file_transport_internet_grpc_config_proto_rawDescGZIP() []byte { file_transport_internet_grpc_config_proto_rawDescOnce.Do(func() { file_transport_internet_grpc_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_grpc_config_proto_rawDesc), len(file_transport_internet_grpc_config_proto_rawDesc))) }) return file_transport_internet_grpc_config_proto_rawDescData } var file_transport_internet_grpc_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_grpc_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.grpc.encoding.Config } var file_transport_internet_grpc_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_grpc_config_proto_init() } func file_transport_internet_grpc_config_proto_init() { if File_transport_internet_grpc_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_grpc_config_proto_rawDesc), len(file_transport_internet_grpc_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_grpc_config_proto_goTypes, DependencyIndexes: file_transport_internet_grpc_config_proto_depIdxs, MessageInfos: file_transport_internet_grpc_config_proto_msgTypes, }.Build() File_transport_internet_grpc_config_proto = out.File file_transport_internet_grpc_config_proto_goTypes = nil file_transport_internet_grpc_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/grpc/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.grpc.encoding; option csharp_namespace = "V2Ray.Core.Transport.Internet.Grpc"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/grpc"; option java_package = "com.v2ray.core.transport.internet.grpc"; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "grpc"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; option (v2ray.core.common.protoext.message_opt).transport_original_name = "gun"; string host = 1; string service_name = 2; int32 idle_timeout = 3; int32 health_check_timeout = 4; bool permit_without_stream = 5; int32 initial_windows_size = 6; } ================================================ FILE: transport/internet/grpc/dial.go ================================================ //go:build !confonly // +build !confonly package grpc import ( "context" gonet "net" "sync" "time" "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/keepalive" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/grpc/encoding" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn, err := dialgRPC(ctx, dest, streamSettings) if err != nil { return nil, newError("failed to dial Grpc").Base(err) } return internet.Connection(conn), nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, Dial)) } type transportConnectionState struct { scopedDialerMap map[net.Destination]*grpc.ClientConn scopedDialerAccess sync.Mutex } func (t *transportConnectionState) IsTransientStorageLifecycleReceiver() { } func (t *transportConnectionState) Close() error { t.scopedDialerAccess.Lock() defer t.scopedDialerAccess.Unlock() for _, conn := range t.scopedDialerMap { _ = conn.Close() } t.scopedDialerMap = nil return nil } type dialerCanceller func() func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) { grpcSettings := streamSettings.ProtocolSettings.(*Config) conn, canceller, err := getGrpcClient(ctx, dest, streamSettings) if err != nil { return nil, newError("Cannot dial grpc").Base(err) } client := encoding.NewGunServiceClient(conn) gunService, err := client.(encoding.GunServiceClientX).TunCustomName(ctx, grpcSettings.ServiceName) if err != nil { canceller() return nil, newError("Cannot dial grpc").Base(err) } return encoding.NewGunConn(gunService, nil), nil } func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*grpc.ClientConn, dialerCanceller, error) { transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) state, err := transportEnvironment.TransientStorage().Get(ctx, "grpc-transport-connection-state") grpcSettings := streamSettings.ProtocolSettings.(*Config) tlsConfig := tls.ConfigFromStreamSettings(streamSettings) transportCredentials := insecure.NewCredentials() if tlsConfig != nil { transportCredentials = credentials.NewTLS(tlsConfig.GetTLSConfig(tls.WithDestination(dest))) } dialOptions := []grpc.DialOption{ grpc.WithTransportCredentials(transportCredentials), grpc.WithConnectParams(grpc.ConnectParams{ Backoff: backoff.Config{ BaseDelay: 500 * time.Millisecond, Multiplier: 1.5, Jitter: 0.2, MaxDelay: 19 * time.Second, }, MinConnectTimeout: 5 * time.Second, }), grpc.WithContextDialer(func(ctxGrpc context.Context, s string) (gonet.Conn, error) { rawHost, rawPort, err := net.SplitHostPort(s) if err != nil { return nil, err } if len(rawPort) == 0 { rawPort = "443" } port, err := net.PortFromString(rawPort) if err != nil { return nil, err } address := net.ParseAddress(rawHost) detachedContext := core.ToBackgroundDetachedContext(ctx) return internet.DialSystem(detachedContext, net.TCPDestination(address, port), streamSettings.SocketSettings) }), grpc.WithDisableServiceConfig(), } if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 || grpcSettings.PermitWithoutStream { dialOptions = append(dialOptions, grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: time.Second * time.Duration(grpcSettings.IdleTimeout), Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout), PermitWithoutStream: grpcSettings.PermitWithoutStream, })) } if grpcSettings.InitialWindowsSize > 0 { dialOptions = append(dialOptions, grpc.WithInitialWindowSize(grpcSettings.InitialWindowsSize)) } if err != nil { state = &transportConnectionState{} transportEnvironment.TransientStorage().Put(ctx, "grpc-transport-connection-state", state) state, err = transportEnvironment.TransientStorage().Get(ctx, "grpc-transport-connection-state") if err != nil { return nil, nil, newError("failed to get grpc transport connection state").Base(err) } } stateTyped := state.(*transportConnectionState) stateTyped.scopedDialerAccess.Lock() defer stateTyped.scopedDialerAccess.Unlock() if stateTyped.scopedDialerMap == nil { stateTyped.scopedDialerMap = make(map[net.Destination]*grpc.ClientConn) } canceller := func() { stateTyped.scopedDialerAccess.Lock() defer stateTyped.scopedDialerAccess.Unlock() delete(stateTyped.scopedDialerMap, dest) } if client, found := stateTyped.scopedDialerMap[dest]; found && client.GetState() != connectivity.Shutdown { return client, canceller, nil } conn, err := grpc.NewClient( dest.Address.String()+":"+dest.Port.String(), dialOptions..., ) canceller = func() { stateTyped.scopedDialerAccess.Lock() defer stateTyped.scopedDialerAccess.Unlock() delete(stateTyped.scopedDialerMap, dest) if err != nil { conn.Close() } } stateTyped.scopedDialerMap[dest] = conn return conn, canceller, err } ================================================ FILE: transport/internet/grpc/encoding/conn.go ================================================ //go:build !confonly // +build !confonly package encoding import ( "bytes" "context" "io" "net" "time" "google.golang.org/grpc/peer" ) // GunService is the abstract interface of GunService_TunClient and GunService_TunServer type GunService interface { Context() context.Context Send(*Hunk) error Recv() (*Hunk, error) } // GunConn implements net.Conn for gun tunnel type GunConn struct { service GunService reader io.Reader over context.CancelFunc local net.Addr remote net.Addr } // Read implements net.Conn.Read() func (c *GunConn) Read(b []byte) (n int, err error) { if c.reader == nil { h, err := c.service.Recv() if err != nil { return 0, newError("unable to read from gun tunnel").Base(err) } c.reader = bytes.NewReader(h.Data) } n, err = c.reader.Read(b) if err == io.EOF { c.reader = nil return n, nil } return n, err } // Write implements net.Conn.Write() func (c *GunConn) Write(b []byte) (n int, err error) { err = c.service.Send(&Hunk{Data: b}) if err != nil { return 0, newError("Unable to send data over gun").Base(err) } return len(b), nil } // Close implements net.Conn.Close() func (c *GunConn) Close() error { if c.over != nil { c.over() } return nil } // LocalAddr implements net.Conn.LocalAddr() func (c *GunConn) LocalAddr() net.Addr { return c.local } // RemoteAddr implements net.Conn.RemoteAddr() func (c *GunConn) RemoteAddr() net.Addr { return c.remote } // SetDeadline implements net.Conn.SetDeadline() func (*GunConn) SetDeadline(time.Time) error { return nil } // SetReadDeadline implements net.Conn.SetReadDeadline() func (*GunConn) SetReadDeadline(time.Time) error { return nil } // SetWriteDeadline implements net.Conn.SetWriteDeadline() func (*GunConn) SetWriteDeadline(time.Time) error { return nil } // NewGunConn creates GunConn which handles gun tunnel func NewGunConn(service GunService, over context.CancelFunc) *GunConn { conn := &GunConn{ service: service, reader: nil, over: over, } conn.local = &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, Port: 0, } pr, ok := peer.FromContext(service.Context()) if ok { conn.remote = pr.Addr } else { conn.remote = &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, Port: 0, } } return conn } ================================================ FILE: transport/internet/grpc/encoding/customSeviceName.go ================================================ //go:build !confonly // +build !confonly package encoding import ( "context" "google.golang.org/grpc" ) func ServerDesc(name string) grpc.ServiceDesc { return grpc.ServiceDesc{ ServiceName: name, HandlerType: (*GunServiceServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "Tun", Handler: _GunService_Tun_Handler, ServerStreams: true, ClientStreams: true, }, }, Metadata: "gun.proto", } } func (c *gunServiceClient) TunCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GunService_TunClient, error) { stream, err := c.cc.NewStream(ctx, &ServerDesc(name).Streams[0], "/"+name+"/Tun", opts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[Hunk, Hunk]{ClientStream: stream} return x, nil } type GunServiceClientX interface { TunCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GunService_TunClient, error) Tun(ctx context.Context, opts ...grpc.CallOption) (GunService_TunClient, error) } func RegisterGunServiceServerX(s *grpc.Server, srv GunServiceServer, name string) { desc := ServerDesc(name) s.RegisterService(&desc, srv) } ================================================ FILE: transport/internet/grpc/encoding/encoding.go ================================================ package encoding //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/grpc/encoding/errors.generated.go ================================================ package encoding import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/grpc/encoding/stream.pb.go ================================================ package encoding import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Hunk struct { state protoimpl.MessageState `protogen:"open.v1"` Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Hunk) Reset() { *x = Hunk{} mi := &file_transport_internet_grpc_encoding_stream_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Hunk) String() string { return protoimpl.X.MessageStringOf(x) } func (*Hunk) ProtoMessage() {} func (x *Hunk) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_grpc_encoding_stream_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Hunk.ProtoReflect.Descriptor instead. func (*Hunk) Descriptor() ([]byte, []int) { return file_transport_internet_grpc_encoding_stream_proto_rawDescGZIP(), []int{0} } func (x *Hunk) GetData() []byte { if x != nil { return x.Data } return nil } var File_transport_internet_grpc_encoding_stream_proto protoreflect.FileDescriptor const file_transport_internet_grpc_encoding_stream_proto_rawDesc = "" + "\n" + "-transport/internet/grpc/encoding/stream.proto\x12+v2ray.core.transport.internet.grpc.encoding\"\x1a\n" + "\x04Hunk\x12\x12\n" + "\x04data\x18\x01 \x01(\fR\x04data2}\n" + "\n" + "GunService\x12o\n" + "\x03Tun\x121.v2ray.core.transport.internet.grpc.encoding.Hunk\x1a1.v2ray.core.transport.internet.grpc.encoding.Hunk(\x010\x01B\xa0\x01\n" + "/com.v2ray.core.transport.internet.grpc.encodingZ?github.com/v2fly/v2ray-core/v5/transport/internet/grpc/encoding\xaa\x02+V2Ray.Core.Transport.Internet.Grpc.Encodingb\x06proto3" var ( file_transport_internet_grpc_encoding_stream_proto_rawDescOnce sync.Once file_transport_internet_grpc_encoding_stream_proto_rawDescData []byte ) func file_transport_internet_grpc_encoding_stream_proto_rawDescGZIP() []byte { file_transport_internet_grpc_encoding_stream_proto_rawDescOnce.Do(func() { file_transport_internet_grpc_encoding_stream_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_grpc_encoding_stream_proto_rawDesc), len(file_transport_internet_grpc_encoding_stream_proto_rawDesc))) }) return file_transport_internet_grpc_encoding_stream_proto_rawDescData } var file_transport_internet_grpc_encoding_stream_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_grpc_encoding_stream_proto_goTypes = []any{ (*Hunk)(nil), // 0: v2ray.core.transport.internet.grpc.encoding.Hunk } var file_transport_internet_grpc_encoding_stream_proto_depIdxs = []int32{ 0, // 0: v2ray.core.transport.internet.grpc.encoding.GunService.Tun:input_type -> v2ray.core.transport.internet.grpc.encoding.Hunk 0, // 1: v2ray.core.transport.internet.grpc.encoding.GunService.Tun:output_type -> v2ray.core.transport.internet.grpc.encoding.Hunk 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_grpc_encoding_stream_proto_init() } func file_transport_internet_grpc_encoding_stream_proto_init() { if File_transport_internet_grpc_encoding_stream_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_grpc_encoding_stream_proto_rawDesc), len(file_transport_internet_grpc_encoding_stream_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 1, }, GoTypes: file_transport_internet_grpc_encoding_stream_proto_goTypes, DependencyIndexes: file_transport_internet_grpc_encoding_stream_proto_depIdxs, MessageInfos: file_transport_internet_grpc_encoding_stream_proto_msgTypes, }.Build() File_transport_internet_grpc_encoding_stream_proto = out.File file_transport_internet_grpc_encoding_stream_proto_goTypes = nil file_transport_internet_grpc_encoding_stream_proto_depIdxs = nil } ================================================ FILE: transport/internet/grpc/encoding/stream.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.grpc.encoding; option csharp_namespace = "V2Ray.Core.Transport.Internet.Grpc.Encoding"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/grpc/encoding"; option java_package = "com.v2ray.core.transport.internet.grpc.encoding"; message Hunk { bytes data = 1; } service GunService { rpc Tun (stream Hunk) returns (stream Hunk); } ================================================ FILE: transport/internet/grpc/encoding/stream_grpc.pb.go ================================================ package encoding import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( GunService_Tun_FullMethodName = "/v2ray.core.transport.internet.grpc.encoding.GunService/Tun" ) // GunServiceClient is the client API for GunService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type GunServiceClient interface { Tun(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Hunk, Hunk], error) } type gunServiceClient struct { cc grpc.ClientConnInterface } func NewGunServiceClient(cc grpc.ClientConnInterface) GunServiceClient { return &gunServiceClient{cc} } func (c *gunServiceClient) Tun(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[Hunk, Hunk], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &GunService_ServiceDesc.Streams[0], GunService_Tun_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[Hunk, Hunk]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type GunService_TunClient = grpc.BidiStreamingClient[Hunk, Hunk] // GunServiceServer is the server API for GunService service. // All implementations must embed UnimplementedGunServiceServer // for forward compatibility. type GunServiceServer interface { Tun(grpc.BidiStreamingServer[Hunk, Hunk]) error mustEmbedUnimplementedGunServiceServer() } // UnimplementedGunServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedGunServiceServer struct{} func (UnimplementedGunServiceServer) Tun(grpc.BidiStreamingServer[Hunk, Hunk]) error { return status.Errorf(codes.Unimplemented, "method Tun not implemented") } func (UnimplementedGunServiceServer) mustEmbedUnimplementedGunServiceServer() {} func (UnimplementedGunServiceServer) testEmbeddedByValue() {} // UnsafeGunServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to GunServiceServer will // result in compilation errors. type UnsafeGunServiceServer interface { mustEmbedUnimplementedGunServiceServer() } func RegisterGunServiceServer(s grpc.ServiceRegistrar, srv GunServiceServer) { // If the following call pancis, it indicates UnimplementedGunServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&GunService_ServiceDesc, srv) } func _GunService_Tun_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(GunServiceServer).Tun(&grpc.GenericServerStream[Hunk, Hunk]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type GunService_TunServer = grpc.BidiStreamingServer[Hunk, Hunk] // GunService_ServiceDesc is the grpc.ServiceDesc for GunService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var GunService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "v2ray.core.transport.internet.grpc.encoding.GunService", HandlerType: (*GunServiceServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "Tun", Handler: _GunService_Tun_Handler, ServerStreams: true, ClientStreams: true, }, }, Metadata: "transport/internet/grpc/encoding/stream.proto", } ================================================ FILE: transport/internet/grpc/errors.generated.go ================================================ package grpc import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/grpc/grpc.go ================================================ //go:build !confonly // +build !confonly package grpc //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/grpc/hub.go ================================================ //go:build !confonly // +build !confonly package grpc import ( "context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/grpc/encoding" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) type Listener struct { encoding.UnimplementedGunServiceServer ctx context.Context handler internet.ConnHandler local net.Addr config *Config s *grpc.Server } func (l Listener) Tun(server encoding.GunService_TunServer) error { tunCtx, cancel := context.WithCancel(l.ctx) l.handler(encoding.NewGunConn(server, cancel)) <-tunCtx.Done() return nil } func (l Listener) Close() error { l.s.Stop() return nil } func (l Listener) Addr() net.Addr { return l.local } func Listen(ctx context.Context, address net.Address, port net.Port, settings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { grpcSettings := settings.ProtocolSettings.(*Config) var listener *Listener if port == net.Port(0) { // unix listener = &Listener{ handler: handler, local: &net.UnixAddr{ Name: address.Domain(), Net: "unix", }, config: grpcSettings, } } else { // tcp listener = &Listener{ handler: handler, local: &net.TCPAddr{ IP: address.IP(), Port: int(port), }, config: grpcSettings, } } listener.ctx = ctx config := tls.ConfigFromStreamSettings(settings) var s *grpc.Server if config == nil { s = grpc.NewServer() } else { // gRPC server may silently ignore TLS errors s = grpc.NewServer(grpc.Creds(credentials.NewTLS(config.GetTLSConfig(tls.WithNextProto("h2"))))) } listener.s = s if settings.SocketSettings != nil && settings.SocketSettings.AcceptProxyProtocol { newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) } go func() { var streamListener net.Listener var err error if port == net.Port(0) { // unix streamListener, err = internet.ListenSystem(ctx, &net.UnixAddr{ Name: address.Domain(), Net: "unix", }, settings.SocketSettings) if err != nil { newError("failed to listen on ", address).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx)) return } } else { // tcp streamListener, err = internet.ListenSystem(ctx, &net.TCPAddr{ IP: address.IP(), Port: int(port), }, settings.SocketSettings) if err != nil { newError("failed to listen on ", address, ":", port).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx)) return } } encoding.RegisterGunServiceServerX(s, listener, grpcSettings.ServiceName) if err = s.Serve(streamListener); err != nil { newError("Listener for grpc ended").Base(err).WriteToLog() } }() return listener, nil } func init() { common.Must(internet.RegisterTransportListener(protocolName, Listen)) } ================================================ FILE: transport/internet/header.go ================================================ package internet import ( "context" "net" "github.com/v2fly/v2ray-core/v5/common" ) type PacketHeader interface { Size() int32 Serialize([]byte) } func CreatePacketHeader(config interface{}) (PacketHeader, error) { header, err := common.CreateObject(context.Background(), config) if err != nil { return nil, err } if h, ok := header.(PacketHeader); ok { return h, nil } return nil, newError("not a packet header") } type ConnectionAuthenticator interface { Client(net.Conn) net.Conn Server(net.Conn) net.Conn } func CreateConnectionAuthenticator(config interface{}) (ConnectionAuthenticator, error) { auth, err := common.CreateObject(context.Background(), config) if err != nil { return nil, err } if a, ok := auth.(ConnectionAuthenticator); ok { return a, nil } return nil, newError("not a ConnectionAuthenticator") } ================================================ FILE: transport/internet/header_test.go ================================================ package internet_test import ( "testing" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/noop" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/srtp" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/utp" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wechat" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wireguard" ) func TestAllHeadersLoadable(t *testing.T) { testCases := []struct { Input interface{} Size int32 }{ { Input: new(noop.Config), Size: 0, }, { Input: new(srtp.Config), Size: 4, }, { Input: new(utp.Config), Size: 4, }, { Input: new(wechat.VideoConfig), Size: 13, }, { Input: new(wireguard.WireguardConfig), Size: 4, }, } for _, testCase := range testCases { header, err := CreatePacketHeader(testCase.Input) common.Must(err) if header.Size() != testCase.Size { t.Error("expected size ", testCase.Size, " but got ", header.Size()) } } } ================================================ FILE: transport/internet/headers/http/config.go ================================================ package http import ( "strings" "github.com/v2fly/v2ray-core/v5/common/dice" ) func pickString(arr []string) string { n := len(arr) switch n { case 0: return "" case 1: return arr[0] default: return arr[dice.Roll(n)] } } func (v *RequestConfig) PickURI() string { return pickString(v.Uri) } func (v *RequestConfig) PickHeaders() []string { n := len(v.Header) if n == 0 { return nil } headers := make([]string, n) for idx, headerConfig := range v.Header { headerName := headerConfig.Name headerValue := pickString(headerConfig.Value) headers[idx] = headerName + ": " + headerValue } return headers } func (v *RequestConfig) GetVersionValue() string { if v == nil || v.Version == nil { return "1.1" } return v.Version.Value } func (v *RequestConfig) GetMethodValue() string { if v == nil || v.Method == nil { return "GET" } return v.Method.Value } func (v *RequestConfig) GetFullVersion() string { return "HTTP/" + v.GetVersionValue() } func (v *ResponseConfig) HasHeader(header string) bool { cHeader := strings.ToLower(header) for _, tHeader := range v.Header { if strings.EqualFold(tHeader.Name, cHeader) { return true } } return false } func (v *ResponseConfig) PickHeaders() []string { n := len(v.Header) if n == 0 { return nil } headers := make([]string, n) for idx, headerConfig := range v.Header { headerName := headerConfig.Name headerValue := pickString(headerConfig.Value) headers[idx] = headerName + ": " + headerValue } return headers } func (v *ResponseConfig) GetVersionValue() string { if v == nil || v.Version == nil { return "1.1" } return v.Version.Value } func (v *ResponseConfig) GetFullVersion() string { return "HTTP/" + v.GetVersionValue() } func (v *ResponseConfig) GetStatusValue() *Status { if v == nil || v.Status == nil { return &Status{ Code: "200", Reason: "OK", } } return v.Status } ================================================ FILE: transport/internet/headers/http/config.pb.go ================================================ package http import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Header struct { state protoimpl.MessageState `protogen:"open.v1"` // "Accept", "Cookie", etc Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Each entry must be valid in one piece. Random entry will be chosen if // multiple entries present. Value []string `protobuf:"bytes,2,rep,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Header) Reset() { *x = Header{} mi := &file_transport_internet_headers_http_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Header) String() string { return protoimpl.X.MessageStringOf(x) } func (*Header) ProtoMessage() {} func (x *Header) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Header.ProtoReflect.Descriptor instead. func (*Header) Descriptor() ([]byte, []int) { return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{0} } func (x *Header) GetName() string { if x != nil { return x.Name } return "" } func (x *Header) GetValue() []string { if x != nil { return x.Value } return nil } // HTTP version. Default value "1.1". type Version struct { state protoimpl.MessageState `protogen:"open.v1"` Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Version) Reset() { *x = Version{} mi := &file_transport_internet_headers_http_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Version) String() string { return protoimpl.X.MessageStringOf(x) } func (*Version) ProtoMessage() {} func (x *Version) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Version.ProtoReflect.Descriptor instead. func (*Version) Descriptor() ([]byte, []int) { return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{1} } func (x *Version) GetValue() string { if x != nil { return x.Value } return "" } // HTTP method. Default value "GET". type Method struct { state protoimpl.MessageState `protogen:"open.v1"` Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Method) Reset() { *x = Method{} mi := &file_transport_internet_headers_http_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Method) String() string { return protoimpl.X.MessageStringOf(x) } func (*Method) ProtoMessage() {} func (x *Method) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Method.ProtoReflect.Descriptor instead. func (*Method) Descriptor() ([]byte, []int) { return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{2} } func (x *Method) GetValue() string { if x != nil { return x.Value } return "" } type RequestConfig struct { state protoimpl.MessageState `protogen:"open.v1"` // Full HTTP version like "1.1". Version *Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // GET, POST, CONNECT etc Method *Method `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` // URI like "/login.php" Uri []string `protobuf:"bytes,3,rep,name=uri,proto3" json:"uri,omitempty"` Header []*Header `protobuf:"bytes,4,rep,name=header,proto3" json:"header,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RequestConfig) Reset() { *x = RequestConfig{} mi := &file_transport_internet_headers_http_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RequestConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*RequestConfig) ProtoMessage() {} func (x *RequestConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RequestConfig.ProtoReflect.Descriptor instead. func (*RequestConfig) Descriptor() ([]byte, []int) { return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{3} } func (x *RequestConfig) GetVersion() *Version { if x != nil { return x.Version } return nil } func (x *RequestConfig) GetMethod() *Method { if x != nil { return x.Method } return nil } func (x *RequestConfig) GetUri() []string { if x != nil { return x.Uri } return nil } func (x *RequestConfig) GetHeader() []*Header { if x != nil { return x.Header } return nil } type Status struct { state protoimpl.MessageState `protogen:"open.v1"` // Status code. Default "200". Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` // Statue reason. Default "OK". Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Status) Reset() { *x = Status{} mi := &file_transport_internet_headers_http_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Status) String() string { return protoimpl.X.MessageStringOf(x) } func (*Status) ProtoMessage() {} func (x *Status) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Status.ProtoReflect.Descriptor instead. func (*Status) Descriptor() ([]byte, []int) { return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{4} } func (x *Status) GetCode() string { if x != nil { return x.Code } return "" } func (x *Status) GetReason() string { if x != nil { return x.Reason } return "" } type ResponseConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Version *Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` Status *Status `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` Header []*Header `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ResponseConfig) Reset() { *x = ResponseConfig{} mi := &file_transport_internet_headers_http_config_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ResponseConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResponseConfig) ProtoMessage() {} func (x *ResponseConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResponseConfig.ProtoReflect.Descriptor instead. func (*ResponseConfig) Descriptor() ([]byte, []int) { return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{5} } func (x *ResponseConfig) GetVersion() *Version { if x != nil { return x.Version } return nil } func (x *ResponseConfig) GetStatus() *Status { if x != nil { return x.Status } return nil } func (x *ResponseConfig) GetHeader() []*Header { if x != nil { return x.Header } return nil } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // Settings for authenticating requests. If not set, client side will not send // authenication header, and server side will bypass authentication. Request *RequestConfig `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // Settings for authenticating responses. If not set, client side will bypass // authentication, and server side will not send authentication header. Response *ResponseConfig `protobuf:"bytes,2,opt,name=response,proto3" json:"response,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_headers_http_config_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_http_config_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_headers_http_config_proto_rawDescGZIP(), []int{6} } func (x *Config) GetRequest() *RequestConfig { if x != nil { return x.Request } return nil } func (x *Config) GetResponse() *ResponseConfig { if x != nil { return x.Response } return nil } var File_transport_internet_headers_http_config_proto protoreflect.FileDescriptor const file_transport_internet_headers_http_config_proto_rawDesc = "" + "\n" + ",transport/internet/headers/http/config.proto\x12*v2ray.core.transport.internet.headers.http\"2\n" + "\x06Header\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + "\x05value\x18\x02 \x03(\tR\x05value\"\x1f\n" + "\aVersion\x12\x14\n" + "\x05value\x18\x01 \x01(\tR\x05value\"\x1e\n" + "\x06Method\x12\x14\n" + "\x05value\x18\x01 \x01(\tR\x05value\"\x88\x02\n" + "\rRequestConfig\x12M\n" + "\aversion\x18\x01 \x01(\v23.v2ray.core.transport.internet.headers.http.VersionR\aversion\x12J\n" + "\x06method\x18\x02 \x01(\v22.v2ray.core.transport.internet.headers.http.MethodR\x06method\x12\x10\n" + "\x03uri\x18\x03 \x03(\tR\x03uri\x12J\n" + "\x06header\x18\x04 \x03(\v22.v2ray.core.transport.internet.headers.http.HeaderR\x06header\"4\n" + "\x06Status\x12\x12\n" + "\x04code\x18\x01 \x01(\tR\x04code\x12\x16\n" + "\x06reason\x18\x02 \x01(\tR\x06reason\"\xf7\x01\n" + "\x0eResponseConfig\x12M\n" + "\aversion\x18\x01 \x01(\v23.v2ray.core.transport.internet.headers.http.VersionR\aversion\x12J\n" + "\x06status\x18\x02 \x01(\v22.v2ray.core.transport.internet.headers.http.StatusR\x06status\x12J\n" + "\x06header\x18\x03 \x03(\v22.v2ray.core.transport.internet.headers.http.HeaderR\x06header\"\xb5\x01\n" + "\x06Config\x12S\n" + "\arequest\x18\x01 \x01(\v29.v2ray.core.transport.internet.headers.http.RequestConfigR\arequest\x12V\n" + "\bresponse\x18\x02 \x01(\v2:.v2ray.core.transport.internet.headers.http.ResponseConfigR\bresponseB\x9f\x01\n" + ".com.v2ray.core.transport.internet.headers.httpP\x01Z>github.com/v2fly/v2ray-core/v5/transport/internet/headers/http\xaa\x02*V2Ray.Core.Transport.Internet.Headers.Httpb\x06proto3" var ( file_transport_internet_headers_http_config_proto_rawDescOnce sync.Once file_transport_internet_headers_http_config_proto_rawDescData []byte ) func file_transport_internet_headers_http_config_proto_rawDescGZIP() []byte { file_transport_internet_headers_http_config_proto_rawDescOnce.Do(func() { file_transport_internet_headers_http_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_headers_http_config_proto_rawDesc), len(file_transport_internet_headers_http_config_proto_rawDesc))) }) return file_transport_internet_headers_http_config_proto_rawDescData } var file_transport_internet_headers_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_transport_internet_headers_http_config_proto_goTypes = []any{ (*Header)(nil), // 0: v2ray.core.transport.internet.headers.http.Header (*Version)(nil), // 1: v2ray.core.transport.internet.headers.http.Version (*Method)(nil), // 2: v2ray.core.transport.internet.headers.http.Method (*RequestConfig)(nil), // 3: v2ray.core.transport.internet.headers.http.RequestConfig (*Status)(nil), // 4: v2ray.core.transport.internet.headers.http.Status (*ResponseConfig)(nil), // 5: v2ray.core.transport.internet.headers.http.ResponseConfig (*Config)(nil), // 6: v2ray.core.transport.internet.headers.http.Config } var file_transport_internet_headers_http_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.transport.internet.headers.http.RequestConfig.version:type_name -> v2ray.core.transport.internet.headers.http.Version 2, // 1: v2ray.core.transport.internet.headers.http.RequestConfig.method:type_name -> v2ray.core.transport.internet.headers.http.Method 0, // 2: v2ray.core.transport.internet.headers.http.RequestConfig.header:type_name -> v2ray.core.transport.internet.headers.http.Header 1, // 3: v2ray.core.transport.internet.headers.http.ResponseConfig.version:type_name -> v2ray.core.transport.internet.headers.http.Version 4, // 4: v2ray.core.transport.internet.headers.http.ResponseConfig.status:type_name -> v2ray.core.transport.internet.headers.http.Status 0, // 5: v2ray.core.transport.internet.headers.http.ResponseConfig.header:type_name -> v2ray.core.transport.internet.headers.http.Header 3, // 6: v2ray.core.transport.internet.headers.http.Config.request:type_name -> v2ray.core.transport.internet.headers.http.RequestConfig 5, // 7: v2ray.core.transport.internet.headers.http.Config.response:type_name -> v2ray.core.transport.internet.headers.http.ResponseConfig 8, // [8:8] is the sub-list for method output_type 8, // [8:8] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name 8, // [8:8] is the sub-list for extension extendee 0, // [0:8] is the sub-list for field type_name } func init() { file_transport_internet_headers_http_config_proto_init() } func file_transport_internet_headers_http_config_proto_init() { if File_transport_internet_headers_http_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_headers_http_config_proto_rawDesc), len(file_transport_internet_headers_http_config_proto_rawDesc)), NumEnums: 0, NumMessages: 7, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_headers_http_config_proto_goTypes, DependencyIndexes: file_transport_internet_headers_http_config_proto_depIdxs, MessageInfos: file_transport_internet_headers_http_config_proto_msgTypes, }.Build() File_transport_internet_headers_http_config_proto = out.File file_transport_internet_headers_http_config_proto_goTypes = nil file_transport_internet_headers_http_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/headers/http/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.headers.http; option csharp_namespace = "V2Ray.Core.Transport.Internet.Headers.Http"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http"; option java_package = "com.v2ray.core.transport.internet.headers.http"; option java_multiple_files = true; message Header { // "Accept", "Cookie", etc string name = 1; // Each entry must be valid in one piece. Random entry will be chosen if // multiple entries present. repeated string value = 2; } // HTTP version. Default value "1.1". message Version { string value = 1; } // HTTP method. Default value "GET". message Method { string value = 1; } message RequestConfig { // Full HTTP version like "1.1". Version version = 1; // GET, POST, CONNECT etc Method method = 2; // URI like "/login.php" repeated string uri = 3; repeated Header header = 4; } message Status { // Status code. Default "200". string code = 1; // Statue reason. Default "OK". string reason = 2; } message ResponseConfig { Version version = 1; Status status = 2; repeated Header header = 3; } message Config { // Settings for authenticating requests. If not set, client side will not send // authenication header, and server side will bypass authentication. RequestConfig request = 1; // Settings for authenticating responses. If not set, client side will bypass // authentication, and server side will not send authentication header. ResponseConfig response = 2; } ================================================ FILE: transport/internet/headers/http/errors.generated.go ================================================ package http import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/headers/http/http.go ================================================ package http //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "bufio" "bytes" "context" "io" "net" "net/http" "strings" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" ) const ( // CRLF is the line ending in HTTP header CRLF = "\r\n" // ENDING is the double line ending between HTTP header and body. ENDING = CRLF + CRLF // max length of HTTP header. Safety precaution for DDoS attack. maxHeaderLength = 8192 ) var ( ErrHeaderToLong = newError("Header too long.") ErrHeaderMisMatch = newError("Header Mismatch.") ) type Reader interface { Read(io.Reader) (*buf.Buffer, error) } type Writer interface { Write(io.Writer) error } type NoOpReader struct{} func (NoOpReader) Read(io.Reader) (*buf.Buffer, error) { return nil, nil } type NoOpWriter struct{} func (NoOpWriter) Write(io.Writer) error { return nil } type HeaderReader struct { req *http.Request expectedHeader *RequestConfig } func (h *HeaderReader) ExpectThisRequest(expectedHeader *RequestConfig) *HeaderReader { h.expectedHeader = expectedHeader return h } func (h *HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) { buffer := buf.New() totalBytes := int32(0) endingDetected := false var headerBuf bytes.Buffer for totalBytes < maxHeaderLength { _, err := buffer.ReadFrom(reader) if err != nil { buffer.Release() return nil, err } if n := bytes.Index(buffer.Bytes(), []byte(ENDING)); n != -1 { headerBuf.Write(buffer.BytesRange(0, int32(n+len(ENDING)))) buffer.Advance(int32(n + len(ENDING))) endingDetected = true break } lenEnding := int32(len(ENDING)) if buffer.Len() >= lenEnding { totalBytes += buffer.Len() - lenEnding headerBuf.Write(buffer.BytesRange(0, buffer.Len()-lenEnding)) leftover := buffer.BytesFrom(-lenEnding) buffer.Clear() copy(buffer.Extend(lenEnding), leftover) if _, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes()))); err != io.ErrUnexpectedEOF { return nil, err } } } if !endingDetected { buffer.Release() return nil, ErrHeaderToLong } if h.expectedHeader == nil { if buffer.IsEmpty() { buffer.Release() return nil, nil } return buffer, nil } // Parse the request if req, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes()))); err != nil { return nil, err } else { // nolint: revive h.req = req } // Check req path := h.req.URL.Path hasThisURI := false for _, u := range h.expectedHeader.Uri { if u == path { hasThisURI = true } } if !hasThisURI { return nil, ErrHeaderMisMatch } if buffer.IsEmpty() { buffer.Release() return nil, nil } return buffer, nil } type HeaderWriter struct { header *buf.Buffer } func NewHeaderWriter(header *buf.Buffer) *HeaderWriter { return &HeaderWriter{ header: header, } } func (w *HeaderWriter) Write(writer io.Writer) error { if w.header == nil { return nil } err := buf.WriteAllBytes(writer, w.header.Bytes()) w.header.Release() w.header = nil return err } type Conn struct { net.Conn readBuffer *buf.Buffer oneTimeReader Reader oneTimeWriter Writer errorWriter Writer errorMismatchWriter Writer errorTooLongWriter Writer errReason error } func NewConn(conn net.Conn, reader Reader, writer Writer, errorWriter Writer, errorMismatchWriter Writer, errorTooLongWriter Writer) *Conn { return &Conn{ Conn: conn, oneTimeReader: reader, oneTimeWriter: writer, errorWriter: errorWriter, errorMismatchWriter: errorMismatchWriter, errorTooLongWriter: errorTooLongWriter, } } func (c *Conn) Read(b []byte) (int, error) { if c.oneTimeReader != nil { buffer, err := c.oneTimeReader.Read(c.Conn) if err != nil { c.errReason = err return 0, err } c.readBuffer = buffer c.oneTimeReader = nil } if !c.readBuffer.IsEmpty() { nBytes, _ := c.readBuffer.Read(b) if c.readBuffer.IsEmpty() { c.readBuffer.Release() c.readBuffer = nil } return nBytes, nil } return c.Conn.Read(b) } // Write implements io.Writer. func (c *Conn) Write(b []byte) (int, error) { if c.oneTimeWriter != nil { err := c.oneTimeWriter.Write(c.Conn) c.oneTimeWriter = nil if err != nil { return 0, err } } return c.Conn.Write(b) } // Close implements net.Conn.Close(). func (c *Conn) Close() error { if c.oneTimeWriter != nil && c.errorWriter != nil { // Connection is being closed but header wasn't sent. This means the client request // is probably not valid. Sending back a server error header in this case. // Write response based on error reason switch c.errReason { case ErrHeaderMisMatch: c.errorMismatchWriter.Write(c.Conn) case ErrHeaderToLong: c.errorTooLongWriter.Write(c.Conn) default: c.errorWriter.Write(c.Conn) } } return c.Conn.Close() } func formResponseHeader(config *ResponseConfig) *HeaderWriter { header := buf.New() common.Must2(header.WriteString(strings.Join([]string{config.GetFullVersion(), config.GetStatusValue().Code, config.GetStatusValue().Reason}, " "))) common.Must2(header.WriteString(CRLF)) headers := config.PickHeaders() for _, h := range headers { common.Must2(header.WriteString(h)) common.Must2(header.WriteString(CRLF)) } if !config.HasHeader("Date") { common.Must2(header.WriteString("Date: ")) common.Must2(header.WriteString(time.Now().Format(http.TimeFormat))) common.Must2(header.WriteString(CRLF)) } common.Must2(header.WriteString(CRLF)) return &HeaderWriter{ header: header, } } type Authenticator struct { config *Config } func (a Authenticator) GetClientWriter() *HeaderWriter { header := buf.New() config := a.config.Request common.Must2(header.WriteString(strings.Join([]string{config.GetMethodValue(), config.PickURI(), config.GetFullVersion()}, " "))) common.Must2(header.WriteString(CRLF)) headers := config.PickHeaders() for _, h := range headers { common.Must2(header.WriteString(h)) common.Must2(header.WriteString(CRLF)) } common.Must2(header.WriteString(CRLF)) return &HeaderWriter{ header: header, } } func (a Authenticator) GetServerWriter() *HeaderWriter { return formResponseHeader(a.config.Response) } func (a Authenticator) Client(conn net.Conn) net.Conn { if a.config.Request == nil && a.config.Response == nil { return conn } var reader Reader = NoOpReader{} if a.config.Request != nil { reader = new(HeaderReader) } var writer Writer = NoOpWriter{} if a.config.Response != nil { writer = a.GetClientWriter() } return NewConn(conn, reader, writer, NoOpWriter{}, NoOpWriter{}, NoOpWriter{}) } func (a Authenticator) Server(conn net.Conn) net.Conn { if a.config.Request == nil && a.config.Response == nil { return conn } return NewConn(conn, new(HeaderReader).ExpectThisRequest(a.config.Request), a.GetServerWriter(), formResponseHeader(resp400), formResponseHeader(resp404), formResponseHeader(resp400)) } func NewAuthenticator(ctx context.Context, config *Config) (Authenticator, error) { return Authenticator{ config: config, }, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewAuthenticator(ctx, config.(*Config)) })) } ================================================ FILE: transport/internet/headers/http/http_test.go ================================================ package http_test import ( "bufio" "bytes" "context" "crypto/rand" "strings" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" . "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http" ) func TestReaderWriter(t *testing.T) { cache := buf.New() b := buf.New() common.Must2(b.WriteString("abcd" + ENDING)) writer := NewHeaderWriter(b) err := writer.Write(cache) common.Must(err) if v := cache.Len(); v != 8 { t.Error("cache len: ", v) } _, err = cache.Write([]byte{'e', 'f', 'g'}) common.Must(err) reader := &HeaderReader{} _, err = reader.Read(cache) if err != nil && !strings.HasPrefix(err.Error(), "malformed HTTP request") { t.Error("unknown error ", err) } } func TestRequestHeader(t *testing.T) { auth, err := NewAuthenticator(context.Background(), &Config{ Request: &RequestConfig{ Uri: []string{"/"}, Header: []*Header{ { Name: "Test", Value: []string{"Value"}, }, }, }, }) common.Must(err) cache := buf.New() err = auth.GetClientWriter().Write(cache) common.Must(err) if cache.String() != "GET / HTTP/1.1\r\nTest: Value\r\n\r\n" { t.Error("cache: ", cache.String()) } } func TestLongRequestHeader(t *testing.T) { payload := make([]byte, buf.Size+2) common.Must2(rand.Read(payload[:buf.Size-2])) copy(payload[buf.Size-2:], ENDING) payload = append(payload, []byte("abcd")...) reader := HeaderReader{} _, err := reader.Read(bytes.NewReader(payload)) if err != nil && !(strings.HasPrefix(err.Error(), "invalid") || strings.HasPrefix(err.Error(), "malformed")) { t.Error("unknown error ", err) } } func TestConnection(t *testing.T) { auth, err := NewAuthenticator(context.Background(), &Config{ Request: &RequestConfig{ Method: &Method{Value: "Post"}, Uri: []string{"/testpath"}, Header: []*Header{ { Name: "Host", Value: []string{"www.v2fly.org", "www.google.com"}, }, { Name: "User-Agent", Value: []string{"Test-Agent"}, }, }, }, Response: &ResponseConfig{ Version: &Version{ Value: "1.1", }, Status: &Status{ Code: "404", Reason: "Not Found", }, }, }) common.Must(err) listener, err := net.Listen("tcp", "127.0.0.1:0") common.Must(err) go func() { conn, err := listener.Accept() common.Must(err) authConn := auth.Server(conn) b := make([]byte, 256) for { n, err := authConn.Read(b) if err != nil { break } _, err = authConn.Write(b[:n]) common.Must(err) } }() conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr)) common.Must(err) authConn := auth.Client(conn) defer authConn.Close() authConn.Write([]byte("Test payload")) authConn.Write([]byte("Test payload 2")) expectedResponse := "Test payloadTest payload 2" actualResponse := make([]byte, 256) deadline := time.Now().Add(time.Second * 5) totalBytes := 0 for { n, err := authConn.Read(actualResponse[totalBytes:]) common.Must(err) totalBytes += n if totalBytes >= len(expectedResponse) || time.Now().After(deadline) { break } } if string(actualResponse[:totalBytes]) != expectedResponse { t.Error("response: ", string(actualResponse[:totalBytes])) } } func TestConnectionInvPath(t *testing.T) { auth, err := NewAuthenticator(context.Background(), &Config{ Request: &RequestConfig{ Method: &Method{Value: "Post"}, Uri: []string{"/testpath"}, Header: []*Header{ { Name: "Host", Value: []string{"www.v2fly.org", "www.google.com"}, }, { Name: "User-Agent", Value: []string{"Test-Agent"}, }, }, }, Response: &ResponseConfig{ Version: &Version{ Value: "1.1", }, Status: &Status{ Code: "404", Reason: "Not Found", }, }, }) common.Must(err) authR, err := NewAuthenticator(context.Background(), &Config{ Request: &RequestConfig{ Method: &Method{Value: "Post"}, Uri: []string{"/testpathErr"}, Header: []*Header{ { Name: "Host", Value: []string{"www.v2fly.org", "www.google.com"}, }, { Name: "User-Agent", Value: []string{"Test-Agent"}, }, }, }, Response: &ResponseConfig{ Version: &Version{ Value: "1.1", }, Status: &Status{ Code: "404", Reason: "Not Found", }, }, }) common.Must(err) listener, err := net.Listen("tcp", "127.0.0.1:0") common.Must(err) go func() { conn, err := listener.Accept() common.Must(err) authConn := auth.Server(conn) b := make([]byte, 256) for { n, err := authConn.Read(b) if err != nil { authConn.Close() break } _, err = authConn.Write(b[:n]) common.Must(err) } }() conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr)) common.Must(err) authConn := authR.Client(conn) defer authConn.Close() authConn.Write([]byte("Test payload")) authConn.Write([]byte("Test payload 2")) expectedResponse := "Test payloadTest payload 2" actualResponse := make([]byte, 256) deadline := time.Now().Add(time.Second * 5) totalBytes := 0 for { n, err := authConn.Read(actualResponse[totalBytes:]) if err == nil { t.Error("Error Expected", err) } else { return } totalBytes += n if totalBytes >= len(expectedResponse) || time.Now().After(deadline) { break } } } func TestConnectionInvReq(t *testing.T) { auth, err := NewAuthenticator(context.Background(), &Config{ Request: &RequestConfig{ Method: &Method{Value: "Post"}, Uri: []string{"/testpath"}, Header: []*Header{ { Name: "Host", Value: []string{"www.v2fly.org", "www.google.com"}, }, { Name: "User-Agent", Value: []string{"Test-Agent"}, }, }, }, Response: &ResponseConfig{ Version: &Version{ Value: "1.1", }, Status: &Status{ Code: "404", Reason: "Not Found", }, }, }) common.Must(err) listener, err := net.Listen("tcp", "127.0.0.1:0") common.Must(err) go func() { conn, err := listener.Accept() common.Must(err) authConn := auth.Server(conn) b := make([]byte, 256) for { n, err := authConn.Read(b) if err != nil { authConn.Close() break } _, err = authConn.Write(b[:n]) common.Must(err) } }() conn, err := net.DialTCP("tcp", nil, listener.Addr().(*net.TCPAddr)) common.Must(err) conn.Write([]byte("ABCDEFGHIJKMLN\r\n\r\n")) l, _, err := bufio.NewReader(conn).ReadLine() common.Must(err) if !strings.HasPrefix(string(l), "HTTP/1.1 400 Bad Request") { t.Error("Resp to non http conn", string(l)) } } ================================================ FILE: transport/internet/headers/http/linkedreadRequest.go ================================================ package http import ( "bufio" "net/http" _ "unsafe" ) //go:linkname readRequest net/http.readRequest func readRequest(b *bufio.Reader) (req *http.Request, err error) ================================================ FILE: transport/internet/headers/http/resp.go ================================================ package http var resp400 = &ResponseConfig{ Version: &Version{ Value: "1.1", }, Status: &Status{ Code: "400", Reason: "Bad Request", }, Header: []*Header{ { Name: "Connection", Value: []string{"close"}, }, { Name: "Cache-Control", Value: []string{"private"}, }, { Name: "Content-Length", Value: []string{"0"}, }, }, } var resp404 = &ResponseConfig{ Version: &Version{ Value: "1.1", }, Status: &Status{ Code: "404", Reason: "Not Found", }, Header: []*Header{ { Name: "Connection", Value: []string{"close"}, }, { Name: "Cache-Control", Value: []string{"private"}, }, { Name: "Content-Length", Value: []string{"0"}, }, }, } ================================================ FILE: transport/internet/headers/noop/config.pb.go ================================================ package noop import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_headers_noop_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_noop_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_headers_noop_config_proto_rawDescGZIP(), []int{0} } type ConnectionConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConnectionConfig) Reset() { *x = ConnectionConfig{} mi := &file_transport_internet_headers_noop_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConnectionConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConnectionConfig) ProtoMessage() {} func (x *ConnectionConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_noop_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConnectionConfig.ProtoReflect.Descriptor instead. func (*ConnectionConfig) Descriptor() ([]byte, []int) { return file_transport_internet_headers_noop_config_proto_rawDescGZIP(), []int{1} } var File_transport_internet_headers_noop_config_proto protoreflect.FileDescriptor const file_transport_internet_headers_noop_config_proto_rawDesc = "" + "\n" + ",transport/internet/headers/noop/config.proto\x12*v2ray.core.transport.internet.headers.noop\"\b\n" + "\x06Config\"\x12\n" + "\x10ConnectionConfigB\x9f\x01\n" + ".com.v2ray.core.transport.internet.headers.noopP\x01Z>github.com/v2fly/v2ray-core/v5/transport/internet/headers/noop\xaa\x02*V2Ray.Core.Transport.Internet.Headers.Noopb\x06proto3" var ( file_transport_internet_headers_noop_config_proto_rawDescOnce sync.Once file_transport_internet_headers_noop_config_proto_rawDescData []byte ) func file_transport_internet_headers_noop_config_proto_rawDescGZIP() []byte { file_transport_internet_headers_noop_config_proto_rawDescOnce.Do(func() { file_transport_internet_headers_noop_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_headers_noop_config_proto_rawDesc), len(file_transport_internet_headers_noop_config_proto_rawDesc))) }) return file_transport_internet_headers_noop_config_proto_rawDescData } var file_transport_internet_headers_noop_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_transport_internet_headers_noop_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.headers.noop.Config (*ConnectionConfig)(nil), // 1: v2ray.core.transport.internet.headers.noop.ConnectionConfig } var file_transport_internet_headers_noop_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_headers_noop_config_proto_init() } func file_transport_internet_headers_noop_config_proto_init() { if File_transport_internet_headers_noop_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_headers_noop_config_proto_rawDesc), len(file_transport_internet_headers_noop_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_headers_noop_config_proto_goTypes, DependencyIndexes: file_transport_internet_headers_noop_config_proto_depIdxs, MessageInfos: file_transport_internet_headers_noop_config_proto_msgTypes, }.Build() File_transport_internet_headers_noop_config_proto = out.File file_transport_internet_headers_noop_config_proto_goTypes = nil file_transport_internet_headers_noop_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/headers/noop/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.headers.noop; option csharp_namespace = "V2Ray.Core.Transport.Internet.Headers.Noop"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/headers/noop"; option java_package = "com.v2ray.core.transport.internet.headers.noop"; option java_multiple_files = true; message Config {} message ConnectionConfig {} ================================================ FILE: transport/internet/headers/noop/noop.go ================================================ package noop import ( "context" "net" "github.com/v2fly/v2ray-core/v5/common" ) type Header struct{} func (Header) Size() int32 { return 0 } // Serialize implements PacketHeader. func (Header) Serialize([]byte) {} func NewHeader(context.Context, interface{}) (interface{}, error) { return Header{}, nil } type ConnectionHeader struct{} func (ConnectionHeader) Client(conn net.Conn) net.Conn { return conn } func (ConnectionHeader) Server(conn net.Conn) net.Conn { return conn } func NewConnectionHeader(context.Context, interface{}) (interface{}, error) { return ConnectionHeader{}, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), NewHeader)) common.Must(common.RegisterConfig((*ConnectionConfig)(nil), NewConnectionHeader)) } ================================================ FILE: transport/internet/headers/srtp/config.pb.go ================================================ package srtp import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` Padding bool `protobuf:"varint,2,opt,name=padding,proto3" json:"padding,omitempty"` Extension bool `protobuf:"varint,3,opt,name=extension,proto3" json:"extension,omitempty"` CsrcCount uint32 `protobuf:"varint,4,opt,name=csrc_count,json=csrcCount,proto3" json:"csrc_count,omitempty"` Marker bool `protobuf:"varint,5,opt,name=marker,proto3" json:"marker,omitempty"` PayloadType uint32 `protobuf:"varint,6,opt,name=payload_type,json=payloadType,proto3" json:"payload_type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_headers_srtp_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_srtp_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_headers_srtp_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetVersion() uint32 { if x != nil { return x.Version } return 0 } func (x *Config) GetPadding() bool { if x != nil { return x.Padding } return false } func (x *Config) GetExtension() bool { if x != nil { return x.Extension } return false } func (x *Config) GetCsrcCount() uint32 { if x != nil { return x.CsrcCount } return 0 } func (x *Config) GetMarker() bool { if x != nil { return x.Marker } return false } func (x *Config) GetPayloadType() uint32 { if x != nil { return x.PayloadType } return 0 } var File_transport_internet_headers_srtp_config_proto protoreflect.FileDescriptor const file_transport_internet_headers_srtp_config_proto_rawDesc = "" + "\n" + ",transport/internet/headers/srtp/config.proto\x12*v2ray.core.transport.internet.headers.srtp\"\xb4\x01\n" + "\x06Config\x12\x18\n" + "\aversion\x18\x01 \x01(\rR\aversion\x12\x18\n" + "\apadding\x18\x02 \x01(\bR\apadding\x12\x1c\n" + "\textension\x18\x03 \x01(\bR\textension\x12\x1d\n" + "\n" + "csrc_count\x18\x04 \x01(\rR\tcsrcCount\x12\x16\n" + "\x06marker\x18\x05 \x01(\bR\x06marker\x12!\n" + "\fpayload_type\x18\x06 \x01(\rR\vpayloadTypeB\x9f\x01\n" + ".com.v2ray.core.transport.internet.headers.srtpP\x01Z>github.com/v2fly/v2ray-core/v5/transport/internet/headers/srtp\xaa\x02*V2Ray.Core.Transport.Internet.Headers.Srtpb\x06proto3" var ( file_transport_internet_headers_srtp_config_proto_rawDescOnce sync.Once file_transport_internet_headers_srtp_config_proto_rawDescData []byte ) func file_transport_internet_headers_srtp_config_proto_rawDescGZIP() []byte { file_transport_internet_headers_srtp_config_proto_rawDescOnce.Do(func() { file_transport_internet_headers_srtp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_headers_srtp_config_proto_rawDesc), len(file_transport_internet_headers_srtp_config_proto_rawDesc))) }) return file_transport_internet_headers_srtp_config_proto_rawDescData } var file_transport_internet_headers_srtp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_headers_srtp_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.headers.srtp.Config } var file_transport_internet_headers_srtp_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_headers_srtp_config_proto_init() } func file_transport_internet_headers_srtp_config_proto_init() { if File_transport_internet_headers_srtp_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_headers_srtp_config_proto_rawDesc), len(file_transport_internet_headers_srtp_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_headers_srtp_config_proto_goTypes, DependencyIndexes: file_transport_internet_headers_srtp_config_proto_depIdxs, MessageInfos: file_transport_internet_headers_srtp_config_proto_msgTypes, }.Build() File_transport_internet_headers_srtp_config_proto = out.File file_transport_internet_headers_srtp_config_proto_goTypes = nil file_transport_internet_headers_srtp_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/headers/srtp/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.headers.srtp; option csharp_namespace = "V2Ray.Core.Transport.Internet.Headers.Srtp"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/headers/srtp"; option java_package = "com.v2ray.core.transport.internet.headers.srtp"; option java_multiple_files = true; message Config { uint32 version = 1; bool padding = 2; bool extension = 3; uint32 csrc_count = 4; bool marker = 5; uint32 payload_type = 6; } ================================================ FILE: transport/internet/headers/srtp/srtp.go ================================================ package srtp import ( "context" "encoding/binary" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" ) type SRTP struct { header uint16 number uint16 } func (*SRTP) Size() int32 { return 4 } // Serialize implements PacketHeader. func (s *SRTP) Serialize(b []byte) { s.number++ binary.BigEndian.PutUint16(b, s.header) binary.BigEndian.PutUint16(b[2:], s.number) } // New returns a new SRTP instance based on the given config. func New(ctx context.Context, config interface{}) (interface{}, error) { return &SRTP{ header: 0xB5E8, number: dice.RollUint16(), }, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), New)) } ================================================ FILE: transport/internet/headers/srtp/srtp_test.go ================================================ package srtp_test import ( "context" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/transport/internet/headers/srtp" ) func TestSRTPWrite(t *testing.T) { content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'} srtpRaw, err := New(context.Background(), &Config{}) common.Must(err) srtp := srtpRaw.(*SRTP) payload := buf.New() srtp.Serialize(payload.Extend(srtp.Size())) payload.Write(content) expectedLen := int32(len(content)) + srtp.Size() if payload.Len() != expectedLen { t.Error("expected ", expectedLen, " of bytes, but got ", payload.Len()) } } ================================================ FILE: transport/internet/headers/tls/config.pb.go ================================================ package tls import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type PacketConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PacketConfig) Reset() { *x = PacketConfig{} mi := &file_transport_internet_headers_tls_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PacketConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*PacketConfig) ProtoMessage() {} func (x *PacketConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_tls_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PacketConfig.ProtoReflect.Descriptor instead. func (*PacketConfig) Descriptor() ([]byte, []int) { return file_transport_internet_headers_tls_config_proto_rawDescGZIP(), []int{0} } var File_transport_internet_headers_tls_config_proto protoreflect.FileDescriptor const file_transport_internet_headers_tls_config_proto_rawDesc = "" + "\n" + "+transport/internet/headers/tls/config.proto\x12)v2ray.core.transport.internet.headers.tls\"\x0e\n" + "\fPacketConfigB\x9c\x01\n" + "-com.v2ray.core.transport.internet.headers.tlsP\x01Z=github.com/v2fly/v2ray-core/v5/transport/internet/headers/tls\xaa\x02)V2Ray.Core.Transport.Internet.Headers.Tlsb\x06proto3" var ( file_transport_internet_headers_tls_config_proto_rawDescOnce sync.Once file_transport_internet_headers_tls_config_proto_rawDescData []byte ) func file_transport_internet_headers_tls_config_proto_rawDescGZIP() []byte { file_transport_internet_headers_tls_config_proto_rawDescOnce.Do(func() { file_transport_internet_headers_tls_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_headers_tls_config_proto_rawDesc), len(file_transport_internet_headers_tls_config_proto_rawDesc))) }) return file_transport_internet_headers_tls_config_proto_rawDescData } var file_transport_internet_headers_tls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_headers_tls_config_proto_goTypes = []any{ (*PacketConfig)(nil), // 0: v2ray.core.transport.internet.headers.tls.PacketConfig } var file_transport_internet_headers_tls_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_headers_tls_config_proto_init() } func file_transport_internet_headers_tls_config_proto_init() { if File_transport_internet_headers_tls_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_headers_tls_config_proto_rawDesc), len(file_transport_internet_headers_tls_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_headers_tls_config_proto_goTypes, DependencyIndexes: file_transport_internet_headers_tls_config_proto_depIdxs, MessageInfos: file_transport_internet_headers_tls_config_proto_msgTypes, }.Build() File_transport_internet_headers_tls_config_proto = out.File file_transport_internet_headers_tls_config_proto_goTypes = nil file_transport_internet_headers_tls_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/headers/tls/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.headers.tls; option csharp_namespace = "V2Ray.Core.Transport.Internet.Headers.Tls"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/headers/tls"; option java_package = "com.v2ray.core.transport.internet.headers.tls"; option java_multiple_files = true; message PacketConfig {} ================================================ FILE: transport/internet/headers/tls/dtls.go ================================================ package tls import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" ) // DTLS writes header as DTLS. See https://tools.ietf.org/html/rfc6347 type DTLS struct { epoch uint16 length uint16 sequence uint32 } // Size implements PacketHeader. func (*DTLS) Size() int32 { return 1 + 2 + 2 + 6 + 2 } // Serialize implements PacketHeader. func (d *DTLS) Serialize(b []byte) { b[0] = 23 // application data b[1] = 254 b[2] = 253 b[3] = byte(d.epoch >> 8) b[4] = byte(d.epoch) b[5] = 0 b[6] = 0 b[7] = byte(d.sequence >> 24) b[8] = byte(d.sequence >> 16) b[9] = byte(d.sequence >> 8) b[10] = byte(d.sequence) d.sequence++ b[11] = byte(d.length >> 8) b[12] = byte(d.length) d.length += 17 if d.length > 100 { d.length -= 50 } } // New creates a new UTP header for the given config. func New(ctx context.Context, config interface{}) (interface{}, error) { return &DTLS{ epoch: dice.RollUint16(), sequence: 0, length: 17, }, nil } func init() { common.Must(common.RegisterConfig((*PacketConfig)(nil), New)) } ================================================ FILE: transport/internet/headers/tls/dtls_test.go ================================================ package tls_test import ( "context" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/transport/internet/headers/tls" ) func TestDTLSWrite(t *testing.T) { content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'} dtlsRaw, err := New(context.Background(), &PacketConfig{}) common.Must(err) dtls := dtlsRaw.(*DTLS) payload := buf.New() dtls.Serialize(payload.Extend(dtls.Size())) payload.Write(content) if payload.Len() != int32(len(content))+dtls.Size() { t.Error("payload len: ", payload.Len(), " want ", int32(len(content))+dtls.Size()) } } ================================================ FILE: transport/internet/headers/utp/config.pb.go ================================================ package utp import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_headers_utp_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_utp_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_headers_utp_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetVersion() uint32 { if x != nil { return x.Version } return 0 } var File_transport_internet_headers_utp_config_proto protoreflect.FileDescriptor const file_transport_internet_headers_utp_config_proto_rawDesc = "" + "\n" + "+transport/internet/headers/utp/config.proto\x12)v2ray.core.transport.internet.headers.utp\"\"\n" + "\x06Config\x12\x18\n" + "\aversion\x18\x01 \x01(\rR\aversionB\x9c\x01\n" + "-com.v2ray.core.transport.internet.headers.utpP\x01Z=github.com/v2fly/v2ray-core/v5/transport/internet/headers/utp\xaa\x02)V2Ray.Core.Transport.Internet.Headers.Utpb\x06proto3" var ( file_transport_internet_headers_utp_config_proto_rawDescOnce sync.Once file_transport_internet_headers_utp_config_proto_rawDescData []byte ) func file_transport_internet_headers_utp_config_proto_rawDescGZIP() []byte { file_transport_internet_headers_utp_config_proto_rawDescOnce.Do(func() { file_transport_internet_headers_utp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_headers_utp_config_proto_rawDesc), len(file_transport_internet_headers_utp_config_proto_rawDesc))) }) return file_transport_internet_headers_utp_config_proto_rawDescData } var file_transport_internet_headers_utp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_headers_utp_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.headers.utp.Config } var file_transport_internet_headers_utp_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_headers_utp_config_proto_init() } func file_transport_internet_headers_utp_config_proto_init() { if File_transport_internet_headers_utp_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_headers_utp_config_proto_rawDesc), len(file_transport_internet_headers_utp_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_headers_utp_config_proto_goTypes, DependencyIndexes: file_transport_internet_headers_utp_config_proto_depIdxs, MessageInfos: file_transport_internet_headers_utp_config_proto_msgTypes, }.Build() File_transport_internet_headers_utp_config_proto = out.File file_transport_internet_headers_utp_config_proto_goTypes = nil file_transport_internet_headers_utp_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/headers/utp/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.headers.utp; option csharp_namespace = "V2Ray.Core.Transport.Internet.Headers.Utp"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/headers/utp"; option java_package = "com.v2ray.core.transport.internet.headers.utp"; option java_multiple_files = true; message Config { uint32 version = 1; } ================================================ FILE: transport/internet/headers/utp/utp.go ================================================ package utp import ( "context" "encoding/binary" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" ) type UTP struct { header byte extension byte connectionID uint16 } func (*UTP) Size() int32 { return 4 } // Serialize implements PacketHeader. func (u *UTP) Serialize(b []byte) { binary.BigEndian.PutUint16(b, u.connectionID) b[2] = u.header b[3] = u.extension } // New creates a new UTP header for the given config. func New(ctx context.Context, config interface{}) (interface{}, error) { return &UTP{ header: 1, extension: 0, connectionID: dice.RollUint16(), }, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), New)) } ================================================ FILE: transport/internet/headers/utp/utp_test.go ================================================ package utp_test import ( "context" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/transport/internet/headers/utp" ) func TestUTPWrite(t *testing.T) { content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'} utpRaw, err := New(context.Background(), &Config{}) common.Must(err) utp := utpRaw.(*UTP) payload := buf.New() utp.Serialize(payload.Extend(utp.Size())) payload.Write(content) if payload.Len() != int32(len(content))+utp.Size() { t.Error("unexpected payload length: ", payload.Len()) } } ================================================ FILE: transport/internet/headers/wechat/config.pb.go ================================================ package wechat import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type VideoConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VideoConfig) Reset() { *x = VideoConfig{} mi := &file_transport_internet_headers_wechat_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VideoConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*VideoConfig) ProtoMessage() {} func (x *VideoConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_wechat_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VideoConfig.ProtoReflect.Descriptor instead. func (*VideoConfig) Descriptor() ([]byte, []int) { return file_transport_internet_headers_wechat_config_proto_rawDescGZIP(), []int{0} } var File_transport_internet_headers_wechat_config_proto protoreflect.FileDescriptor const file_transport_internet_headers_wechat_config_proto_rawDesc = "" + "\n" + ".transport/internet/headers/wechat/config.proto\x12,v2ray.core.transport.internet.headers.wechat\"\r\n" + "\vVideoConfigB\xa5\x01\n" + "0com.v2ray.core.transport.internet.headers.wechatP\x01Z@github.com/v2fly/v2ray-core/v5/transport/internet/headers/wechat\xaa\x02,V2Ray.Core.Transport.Internet.Headers.Wechatb\x06proto3" var ( file_transport_internet_headers_wechat_config_proto_rawDescOnce sync.Once file_transport_internet_headers_wechat_config_proto_rawDescData []byte ) func file_transport_internet_headers_wechat_config_proto_rawDescGZIP() []byte { file_transport_internet_headers_wechat_config_proto_rawDescOnce.Do(func() { file_transport_internet_headers_wechat_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_headers_wechat_config_proto_rawDesc), len(file_transport_internet_headers_wechat_config_proto_rawDesc))) }) return file_transport_internet_headers_wechat_config_proto_rawDescData } var file_transport_internet_headers_wechat_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_headers_wechat_config_proto_goTypes = []any{ (*VideoConfig)(nil), // 0: v2ray.core.transport.internet.headers.wechat.VideoConfig } var file_transport_internet_headers_wechat_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_headers_wechat_config_proto_init() } func file_transport_internet_headers_wechat_config_proto_init() { if File_transport_internet_headers_wechat_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_headers_wechat_config_proto_rawDesc), len(file_transport_internet_headers_wechat_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_headers_wechat_config_proto_goTypes, DependencyIndexes: file_transport_internet_headers_wechat_config_proto_depIdxs, MessageInfos: file_transport_internet_headers_wechat_config_proto_msgTypes, }.Build() File_transport_internet_headers_wechat_config_proto = out.File file_transport_internet_headers_wechat_config_proto_goTypes = nil file_transport_internet_headers_wechat_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/headers/wechat/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.headers.wechat; option csharp_namespace = "V2Ray.Core.Transport.Internet.Headers.Wechat"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wechat"; option java_package = "com.v2ray.core.transport.internet.headers.wechat"; option java_multiple_files = true; message VideoConfig {} ================================================ FILE: transport/internet/headers/wechat/wechat.go ================================================ package wechat import ( "context" "encoding/binary" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" ) type VideoChat struct { sn uint32 } func (vc *VideoChat) Size() int32 { return 13 } // Serialize implements PacketHeader. func (vc *VideoChat) Serialize(b []byte) { vc.sn++ b[0] = 0xa1 b[1] = 0x08 binary.BigEndian.PutUint32(b[2:], vc.sn) // b[2:6] b[6] = 0x00 b[7] = 0x10 b[8] = 0x11 b[9] = 0x18 b[10] = 0x30 b[11] = 0x22 b[12] = 0x30 } // NewVideoChat returns a new VideoChat instance based on given config. func NewVideoChat(ctx context.Context, config interface{}) (interface{}, error) { return &VideoChat{ sn: uint32(dice.RollUint16()), }, nil } func init() { common.Must(common.RegisterConfig((*VideoConfig)(nil), NewVideoChat)) } ================================================ FILE: transport/internet/headers/wechat/wechat_test.go ================================================ package wechat_test import ( "context" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wechat" ) func TestUTPWrite(t *testing.T) { videoRaw, err := NewVideoChat(context.Background(), &VideoConfig{}) common.Must(err) video := videoRaw.(*VideoChat) payload := buf.New() video.Serialize(payload.Extend(video.Size())) if payload.Len() != video.Size() { t.Error("expected payload size ", video.Size(), " but got ", payload.Len()) } } ================================================ FILE: transport/internet/headers/wireguard/config.pb.go ================================================ package wireguard import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type WireguardConfig struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WireguardConfig) Reset() { *x = WireguardConfig{} mi := &file_transport_internet_headers_wireguard_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *WireguardConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*WireguardConfig) ProtoMessage() {} func (x *WireguardConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_headers_wireguard_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use WireguardConfig.ProtoReflect.Descriptor instead. func (*WireguardConfig) Descriptor() ([]byte, []int) { return file_transport_internet_headers_wireguard_config_proto_rawDescGZIP(), []int{0} } var File_transport_internet_headers_wireguard_config_proto protoreflect.FileDescriptor const file_transport_internet_headers_wireguard_config_proto_rawDesc = "" + "\n" + "1transport/internet/headers/wireguard/config.proto\x12/v2ray.core.transport.internet.headers.wireguard\"\x11\n" + "\x0fWireguardConfigB\xae\x01\n" + "3com.v2ray.core.transport.internet.headers.wireguardP\x01ZCgithub.com/v2fly/v2ray-core/v5/transport/internet/headers/wireguard\xaa\x02/V2Ray.Core.Transport.Internet.Headers.Wireguardb\x06proto3" var ( file_transport_internet_headers_wireguard_config_proto_rawDescOnce sync.Once file_transport_internet_headers_wireguard_config_proto_rawDescData []byte ) func file_transport_internet_headers_wireguard_config_proto_rawDescGZIP() []byte { file_transport_internet_headers_wireguard_config_proto_rawDescOnce.Do(func() { file_transport_internet_headers_wireguard_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_headers_wireguard_config_proto_rawDesc), len(file_transport_internet_headers_wireguard_config_proto_rawDesc))) }) return file_transport_internet_headers_wireguard_config_proto_rawDescData } var file_transport_internet_headers_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_headers_wireguard_config_proto_goTypes = []any{ (*WireguardConfig)(nil), // 0: v2ray.core.transport.internet.headers.wireguard.WireguardConfig } var file_transport_internet_headers_wireguard_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_headers_wireguard_config_proto_init() } func file_transport_internet_headers_wireguard_config_proto_init() { if File_transport_internet_headers_wireguard_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_headers_wireguard_config_proto_rawDesc), len(file_transport_internet_headers_wireguard_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_headers_wireguard_config_proto_goTypes, DependencyIndexes: file_transport_internet_headers_wireguard_config_proto_depIdxs, MessageInfos: file_transport_internet_headers_wireguard_config_proto_msgTypes, }.Build() File_transport_internet_headers_wireguard_config_proto = out.File file_transport_internet_headers_wireguard_config_proto_goTypes = nil file_transport_internet_headers_wireguard_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/headers/wireguard/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.headers.wireguard; option csharp_namespace = "V2Ray.Core.Transport.Internet.Headers.Wireguard"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wireguard"; option java_package = "com.v2ray.core.transport.internet.headers.wireguard"; option java_multiple_files = true; message WireguardConfig {} ================================================ FILE: transport/internet/headers/wireguard/wireguard.go ================================================ package wireguard import ( "context" "github.com/v2fly/v2ray-core/v5/common" ) type Wireguard struct{} func (Wireguard) Size() int32 { return 4 } // Serialize implements PacketHeader. func (Wireguard) Serialize(b []byte) { b[0] = 0x04 b[1] = 0x00 b[2] = 0x00 b[3] = 0x00 } // NewWireguard returns a new VideoChat instance based on given config. func NewWireguard(ctx context.Context, config interface{}) (interface{}, error) { return Wireguard{}, nil } func init() { common.Must(common.RegisterConfig((*WireguardConfig)(nil), NewWireguard)) } ================================================ FILE: transport/internet/http/config.go ================================================ package http import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/transport/internet" ) const protocolName = "http" func (c *Config) getHosts() []string { if len(c.Host) == 0 { return []string{"www.example.com"} } return c.Host } func (c *Config) isValidHost(host string) bool { hosts := c.getHosts() for _, h := range hosts { if h == host { return true } } return false } func (c *Config) getRandomHost() string { hosts := c.getHosts() return hosts[dice.Roll(len(hosts))] } func (c *Config) getNormalizedPath() string { if c.Path == "" { return "/" } if c.Path[0] != '/' { return "/" + c.Path } return c.Path } func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/http/config.pb.go ================================================ package http import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" http "github.com/v2fly/v2ray-core/v5/transport/internet/headers/http" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"` Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` Header []*http.Header `protobuf:"bytes,4,rep,name=header,proto3" json:"header,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_http_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_http_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_http_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetHost() []string { if x != nil { return x.Host } return nil } func (x *Config) GetPath() string { if x != nil { return x.Path } return "" } func (x *Config) GetMethod() string { if x != nil { return x.Method } return "" } func (x *Config) GetHeader() []*http.Header { if x != nil { return x.Header } return nil } var File_transport_internet_http_config_proto protoreflect.FileDescriptor const file_transport_internet_http_config_proto_rawDesc = "" + "\n" + "$transport/internet/http/config.proto\x12\"v2ray.core.transport.internet.http\x1a common/protoext/extensions.proto\x1a,transport/internet/headers/http/config.proto\"\xb1\x01\n" + "\x06Config\x12\x12\n" + "\x04host\x18\x01 \x03(\tR\x04host\x12\x12\n" + "\x04path\x18\x02 \x01(\tR\x04path\x12\x16\n" + "\x06method\x18\x03 \x01(\tR\x06method\x12J\n" + "\x06header\x18\x04 \x03(\v22.v2ray.core.transport.internet.headers.http.HeaderR\x06header:\x1b\x82\xb5\x18\x17\n" + "\ttransport\x12\x02h2\x8a\xff)\x04httpB\x87\x01\n" + "&com.v2ray.core.transport.internet.httpP\x01Z6github.com/v2fly/v2ray-core/v5/transport/internet/http\xaa\x02\"V2Ray.Core.Transport.Internet.Httpb\x06proto3" var ( file_transport_internet_http_config_proto_rawDescOnce sync.Once file_transport_internet_http_config_proto_rawDescData []byte ) func file_transport_internet_http_config_proto_rawDescGZIP() []byte { file_transport_internet_http_config_proto_rawDescOnce.Do(func() { file_transport_internet_http_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_http_config_proto_rawDesc), len(file_transport_internet_http_config_proto_rawDesc))) }) return file_transport_internet_http_config_proto_rawDescData } var file_transport_internet_http_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_http_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.http.Config (*http.Header)(nil), // 1: v2ray.core.transport.internet.headers.http.Header } var file_transport_internet_http_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.transport.internet.http.Config.header:type_name -> v2ray.core.transport.internet.headers.http.Header 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_http_config_proto_init() } func file_transport_internet_http_config_proto_init() { if File_transport_internet_http_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_http_config_proto_rawDesc), len(file_transport_internet_http_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_http_config_proto_goTypes, DependencyIndexes: file_transport_internet_http_config_proto_depIdxs, MessageInfos: file_transport_internet_http_config_proto_msgTypes, }.Build() File_transport_internet_http_config_proto = out.File file_transport_internet_http_config_proto_goTypes = nil file_transport_internet_http_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/http/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.http; option csharp_namespace = "V2Ray.Core.Transport.Internet.Http"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/http"; option java_package = "com.v2ray.core.transport.internet.http"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "transport/internet/headers/http/config.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "h2"; option (v2ray.core.common.protoext.message_opt).transport_original_name = "http"; repeated string host = 1; string path = 2; string method = 3; repeated v2ray.core.transport.internet.headers.http.Header header = 4; } ================================================ FILE: transport/internet/http/dialer.go ================================================ package http import ( "context" gotls "crypto/tls" gonet "net" "net/http" "net/url" "sync" "golang.org/x/net/http2" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/security" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) var ( globalDialerMap map[net.Destination]*http.Client globalDialerAccess sync.Mutex ) type dialerCanceller func() func getHTTPClient(ctx context.Context, dest net.Destination, securityEngine *security.Engine, streamSettings *internet.MemoryStreamConfig) (*http.Client, dialerCanceller) { globalDialerAccess.Lock() defer globalDialerAccess.Unlock() canceller := func() { globalDialerAccess.Lock() defer globalDialerAccess.Unlock() delete(globalDialerMap, dest) } if globalDialerMap == nil { globalDialerMap = make(map[net.Destination]*http.Client) } if client, found := globalDialerMap[dest]; found { return client, canceller } transport := &http2.Transport{ DialTLSContext: func(_ context.Context, network, addr string, tlsConfig *gotls.Config) (gonet.Conn, error) { rawHost, rawPort, err := net.SplitHostPort(addr) if err != nil { return nil, err } if len(rawPort) == 0 { rawPort = "443" } port, err := net.PortFromString(rawPort) if err != nil { return nil, err } address := net.ParseAddress(rawHost) detachedContext := core.ToBackgroundDetachedContext(ctx) pconn, err := internet.DialSystem(detachedContext, net.TCPDestination(address, port), streamSettings.SocketSettings) if err != nil { return nil, err } cn, err := (*securityEngine).Client(pconn, security.OptionWithDestination{Dest: dest}) if err != nil { return nil, err } protocol := "" if connAPLNGetter, ok := cn.(security.ConnectionApplicationProtocol); ok { connectionALPN, err := connAPLNGetter.GetConnectionApplicationProtocol() if err != nil { return nil, newError("failed to get connection ALPN").Base(err).AtWarning() } protocol = connectionALPN } if protocol != http2.NextProtoTLS { return nil, newError("http2: unexpected ALPN protocol " + protocol + "; want q" + http2.NextProtoTLS).AtError() } return cn, nil }, } client := &http.Client{ Transport: transport, } globalDialerMap[dest] = client return client, canceller } // Dial dials a new TCP connection to the given destination. func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { httpSettings := streamSettings.ProtocolSettings.(*Config) securityEngine, err := security.CreateSecurityEngineFromSettings(ctx, streamSettings) if err != nil { return nil, newError("unable to create security engine").Base(err) } if securityEngine == nil { return nil, newError("TLS must be enabled for http transport.").AtWarning() } client, canceller := getHTTPClient(ctx, dest, &securityEngine, streamSettings) opts := pipe.OptionsFromContext(ctx) preader, pwriter := pipe.New(opts...) breader := &buf.BufferedReader{Reader: preader} httpMethod := "PUT" if httpSettings.Method != "" { httpMethod = httpSettings.Method } httpHeaders := make(http.Header) for _, httpHeader := range httpSettings.Header { for _, httpHeaderValue := range httpHeader.Value { httpHeaders.Set(httpHeader.Name, httpHeaderValue) } } request := &http.Request{ Method: httpMethod, Host: httpSettings.getRandomHost(), Body: breader, URL: &url.URL{ Scheme: "https", Host: dest.NetAddr(), Path: httpSettings.getNormalizedPath(), }, Proto: "HTTP/2", ProtoMajor: 2, ProtoMinor: 0, Header: httpHeaders, } // Disable any compression method from server. request.Header.Set("Accept-Encoding", "identity") response, err := client.Do(request) // nolint: bodyclose if err != nil { canceller() return nil, newError("failed to dial to ", dest).Base(err).AtWarning() } if response.StatusCode != 200 { return nil, newError("unexpected status", response.StatusCode).AtWarning() } bwriter := buf.NewBufferedWriter(pwriter) common.Must(bwriter.SetBuffered(false)) return net.NewConnection( net.ConnectionOutput(response.Body), net.ConnectionInput(bwriter), net.ConnectionOnClose(common.ChainedClosable{breader, bwriter, response.Body}), ), nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, Dial)) } ================================================ FILE: transport/internet/http/errors.generated.go ================================================ package http import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/http/http.go ================================================ package http //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/http/http_test.go ================================================ package http_test import ( "context" "crypto/rand" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/transport/internet" . "github.com/v2fly/v2ray-core/v5/transport/internet/http" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func TestHTTPConnection(t *testing.T) { port := tcp.PickPort() listener, err := Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ ProtocolName: "http", ProtocolSettings: &Config{}, SecurityType: "tls", SecuritySettings: &tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("www.v2fly.org")))}, }, }, func(conn internet.Connection) { go func() { defer conn.Close() b := buf.New() defer b.Release() for { if _, err := b.ReadFrom(conn); err != nil { return } _, err := conn.Write(b.Bytes()) common.Must(err) } }() }) common.Must(err) defer listener.Close() time.Sleep(time.Second) dctx := context.Background() conn, err := Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ ProtocolName: "http", ProtocolSettings: &Config{}, SecurityType: "tls", SecuritySettings: &tls.Config{ ServerName: "www.v2fly.org", AllowInsecure: true, }, }) common.Must(err) defer conn.Close() const N = 1024 b1 := make([]byte, N) common.Must2(rand.Read(b1)) b2 := buf.New() nBytes, err := conn.Write(b1) common.Must(err) if nBytes != N { t.Error("write: ", nBytes) } b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } nBytes, err = conn.Write(b1) common.Must(err) if nBytes != N { t.Error("write: ", nBytes) } b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } } ================================================ FILE: transport/internet/http/hub.go ================================================ package http import ( "context" "io" "net/http" "strings" "time" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" http_proto "github.com/v2fly/v2ray-core/v5/common/protocol/http" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) type Listener struct { server *http.Server handler internet.ConnHandler local net.Addr config *Config } func (l *Listener) Addr() net.Addr { return l.local } func (l *Listener) Close() error { return l.server.Close() } type flushWriter struct { w io.Writer d *done.Instance } func (fw flushWriter) Write(p []byte) (n int, err error) { if fw.d.Done() { return 0, io.ErrClosedPipe } n, err = fw.w.Write(p) if f, ok := fw.w.(http.Flusher); ok { f.Flush() } return } func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) { host := request.Host if len(l.config.Host) != 0 && !l.config.isValidHost(host) { writer.WriteHeader(404) return } path := l.config.getNormalizedPath() if !strings.HasPrefix(request.URL.Path, path) { writer.WriteHeader(404) return } writer.Header().Set("Cache-Control", "no-store") for _, httpHeader := range l.config.Header { for _, httpHeaderValue := range httpHeader.Value { writer.Header().Set(httpHeader.Name, httpHeaderValue) } } writer.WriteHeader(200) if f, ok := writer.(http.Flusher); ok { f.Flush() } remoteAddr := l.Addr() dest, err := net.ParseDestination(request.RemoteAddr) if err != nil { newError("failed to parse request remote addr: ", request.RemoteAddr).Base(err).WriteToLog() } else { remoteAddr = &net.TCPAddr{ IP: dest.Address.IP(), Port: int(dest.Port), } } forwardedAddress := http_proto.ParseXForwardedFor(request.Header) if len(forwardedAddress) > 0 && forwardedAddress[0].Family().IsIP() { remoteAddr = &net.TCPAddr{ IP: forwardedAddress[0].IP(), Port: 0, } } done := done.New() conn := net.NewConnection( net.ConnectionOutput(request.Body), net.ConnectionInput(flushWriter{w: writer, d: done}), net.ConnectionOnClose(common.ChainedClosable{done, request.Body}), net.ConnectionLocalAddr(l.Addr()), net.ConnectionRemoteAddr(remoteAddr), ) l.handler(conn) <-done.Wait() } func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { httpSettings := streamSettings.ProtocolSettings.(*Config) var listener *Listener if port == net.Port(0) { // unix listener = &Listener{ handler: handler, local: &net.UnixAddr{ Name: address.Domain(), Net: "unix", }, config: httpSettings, } } else { // tcp listener = &Listener{ handler: handler, local: &net.TCPAddr{ IP: address.IP(), Port: int(port), }, config: httpSettings, } } var server *http.Server config := tls.ConfigFromStreamSettings(streamSettings) if config == nil { h2s := &http2.Server{} server = &http.Server{ Addr: serial.Concat(address, ":", port), Handler: h2c.NewHandler(listener, h2s), ReadHeaderTimeout: time.Second * 4, } } else { server = &http.Server{ Addr: serial.Concat(address, ":", port), TLSConfig: config.GetTLSConfig(tls.WithNextProto("h2")), Handler: listener, ReadHeaderTimeout: time.Second * 4, } } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) } listener.server = server go func() { var streamListener net.Listener var err error if port == net.Port(0) { // unix streamListener, err = internet.ListenSystem(ctx, &net.UnixAddr{ Name: address.Domain(), Net: "unix", }, streamSettings.SocketSettings) if err != nil { newError("failed to listen on ", address).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx)) return } } else { // tcp streamListener, err = internet.ListenSystem(ctx, &net.TCPAddr{ IP: address.IP(), Port: int(port), }, streamSettings.SocketSettings) if err != nil { newError("failed to listen on ", address, ":", port).Base(err).AtError().WriteToLog(session.ExportIDToError(ctx)) return } } if config == nil { err = server.Serve(streamListener) if err != nil { newError("stopping serving H2C").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } else { err = server.ServeTLS(streamListener, "", "") if err != nil { newError("stopping serving TLS").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } }() return listener, nil } func init() { common.Must(internet.RegisterTransportListener(protocolName, Listen)) } ================================================ FILE: transport/internet/httpupgrade/config.go ================================================ package httpupgrade func (c *Config) GetNormalizedPath() string { path := c.Path if path == "" { return "/" } if path[0] != '/' { return "/" + path } return path } ================================================ FILE: transport/internet/httpupgrade/config.pb.go ================================================ package httpupgrade import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Header struct { state protoimpl.MessageState `protogen:"open.v1"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Header) Reset() { *x = Header{} mi := &file_transport_internet_httpupgrade_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Header) String() string { return protoimpl.X.MessageStringOf(x) } func (*Header) ProtoMessage() {} func (x *Header) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_httpupgrade_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Header.ProtoReflect.Descriptor instead. func (*Header) Descriptor() ([]byte, []int) { return file_transport_internet_httpupgrade_config_proto_rawDescGZIP(), []int{0} } func (x *Header) GetKey() string { if x != nil { return x.Key } return "" } func (x *Header) GetValue() string { if x != nil { return x.Value } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` MaxEarlyData int32 `protobuf:"varint,3,opt,name=max_early_data,json=maxEarlyData,proto3" json:"max_early_data,omitempty"` EarlyDataHeaderName string `protobuf:"bytes,4,opt,name=early_data_header_name,json=earlyDataHeaderName,proto3" json:"early_data_header_name,omitempty"` Header []*Header `protobuf:"bytes,5,rep,name=header,proto3" json:"header,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_httpupgrade_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_httpupgrade_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_httpupgrade_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetPath() string { if x != nil { return x.Path } return "" } func (x *Config) GetHost() string { if x != nil { return x.Host } return "" } func (x *Config) GetMaxEarlyData() int32 { if x != nil { return x.MaxEarlyData } return 0 } func (x *Config) GetEarlyDataHeaderName() string { if x != nil { return x.EarlyDataHeaderName } return "" } func (x *Config) GetHeader() []*Header { if x != nil { return x.Header } return nil } var File_transport_internet_httpupgrade_config_proto protoreflect.FileDescriptor const file_transport_internet_httpupgrade_config_proto_rawDesc = "" + "\n" + "+transport/internet/httpupgrade/config.proto\x121v2ray.core.transport.internet.request.httpupgrade\x1a common/protoext/extensions.proto\"0\n" + "\x06Header\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value\"\x80\x02\n" + "\x06Config\x12\x12\n" + "\x04path\x18\x01 \x01(\tR\x04path\x12\x12\n" + "\x04host\x18\x02 \x01(\tR\x04host\x12$\n" + "\x0emax_early_data\x18\x03 \x01(\x05R\fmaxEarlyData\x123\n" + "\x16early_data_header_name\x18\x04 \x01(\tR\x13earlyDataHeaderName\x12Q\n" + "\x06header\x18\x05 \x03(\v29.v2ray.core.transport.internet.request.httpupgrade.HeaderR\x06header: \x82\xb5\x18\x1c\n" + "\ttransport\x12\vhttpupgrade\x90\xff)\x01B\x9c\x01\n" + "-com.v2ray.core.transport.internet.httpupgradeP\x01Z=github.com/v2fly/v2ray-core/v5/transport/internet/httpupgrade\xaa\x02)V2Ray.Core.Transport.Internet.HttpUpgradeb\x06proto3" var ( file_transport_internet_httpupgrade_config_proto_rawDescOnce sync.Once file_transport_internet_httpupgrade_config_proto_rawDescData []byte ) func file_transport_internet_httpupgrade_config_proto_rawDescGZIP() []byte { file_transport_internet_httpupgrade_config_proto_rawDescOnce.Do(func() { file_transport_internet_httpupgrade_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_httpupgrade_config_proto_rawDesc), len(file_transport_internet_httpupgrade_config_proto_rawDesc))) }) return file_transport_internet_httpupgrade_config_proto_rawDescData } var file_transport_internet_httpupgrade_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_transport_internet_httpupgrade_config_proto_goTypes = []any{ (*Header)(nil), // 0: v2ray.core.transport.internet.request.httpupgrade.Header (*Config)(nil), // 1: v2ray.core.transport.internet.request.httpupgrade.Config } var file_transport_internet_httpupgrade_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.transport.internet.request.httpupgrade.Config.header:type_name -> v2ray.core.transport.internet.request.httpupgrade.Header 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_httpupgrade_config_proto_init() } func file_transport_internet_httpupgrade_config_proto_init() { if File_transport_internet_httpupgrade_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_httpupgrade_config_proto_rawDesc), len(file_transport_internet_httpupgrade_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_httpupgrade_config_proto_goTypes, DependencyIndexes: file_transport_internet_httpupgrade_config_proto_depIdxs, MessageInfos: file_transport_internet_httpupgrade_config_proto_msgTypes, }.Build() File_transport_internet_httpupgrade_config_proto = out.File file_transport_internet_httpupgrade_config_proto_goTypes = nil file_transport_internet_httpupgrade_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/httpupgrade/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.request.httpupgrade; option csharp_namespace = "V2Ray.Core.Transport.Internet.HttpUpgrade"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/httpupgrade"; option java_package = "com.v2ray.core.transport.internet.httpupgrade"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Header { string key = 1; string value = 2; } message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "httpupgrade"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; string path = 1; string host = 2; int32 max_early_data = 3; string early_data_header_name = 4; repeated Header header = 5; } ================================================ FILE: transport/internet/httpupgrade/connection.go ================================================ package httpupgrade import ( "context" "io" "time" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" ) type connection struct { conn net.Conn reader io.Reader remoteAddr net.Addr shouldWait bool delayedDialFinish context.Context finishedDial context.CancelFunc dialer delayedDialer } type delayedDialer func(earlyData []byte) (conn net.Conn, earlyReply io.Reader, err error) func newConnectionWithPendingRead(conn net.Conn, remoteAddr net.Addr, earlyReplyReader io.Reader) *connection { return &connection{ conn: conn, remoteAddr: remoteAddr, reader: earlyReplyReader, } } func newConnectionWithDelayedDial(dialer delayedDialer) *connection { ctx, cancel := context.WithCancel(context.Background()) return &connection{ shouldWait: true, delayedDialFinish: ctx, finishedDial: cancel, dialer: dialer, } } // Read implements net.Conn.Read() func (c *connection) Read(b []byte) (int, error) { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { return 0, newError("unable to read delayed dial websocket connection as it do not exist") } } if c.reader != nil { n, err := c.reader.Read(b) if err == io.EOF { c.reader = nil return c.conn.Read(b) } return n, err } return c.conn.Read(b) } // Write implements io.Writer. func (c *connection) Write(b []byte) (int, error) { if c.shouldWait { var err error var earlyReply io.Reader c.conn, earlyReply, err = c.dialer(b) if earlyReply != nil { c.reader = earlyReply } c.finishedDial() if err != nil { return 0, newError("Unable to proceed with delayed write").Base(err) } c.remoteAddr = c.conn.RemoteAddr() c.shouldWait = false return len(b), nil } return c.conn.Write(b) } func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { mb = buf.Compact(mb) mb, err := buf.WriteMultiBuffer(c, mb) buf.ReleaseMulti(mb) return err } func (c *connection) Close() error { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { return newError("unable to close delayed dial websocket connection as it do not exist") } } var closeErrors []interface{} if err := c.conn.Close(); err != nil { closeErrors = append(closeErrors, err) } if len(closeErrors) > 0 { return newError("failed to close connection").Base(newError(serial.Concat(closeErrors...))) } return nil } func (c *connection) LocalAddr() net.Addr { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { newError("websocket transport is not materialized when LocalAddr() is called").AtWarning().WriteToLog() return &net.UnixAddr{ Name: "@placeholder", Net: "unix", } } } return c.conn.LocalAddr() } func (c *connection) RemoteAddr() net.Addr { return c.remoteAddr } func (c *connection) SetDeadline(t time.Time) error { if err := c.SetReadDeadline(t); err != nil { return err } return c.SetWriteDeadline(t) } func (c *connection) SetReadDeadline(t time.Time) error { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { newError("httpupgrade transport is not materialized when SetReadDeadline() is called").AtWarning().WriteToLog() return nil } } return c.conn.SetReadDeadline(t) } func (c *connection) SetWriteDeadline(t time.Time) error { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { newError("httpupgrade transport is not materialized when SetWriteDeadline() is called").AtWarning().WriteToLog() return nil } } return c.conn.SetWriteDeadline(t) } ================================================ FILE: transport/internet/httpupgrade/dialer.go ================================================ package httpupgrade import ( "bufio" "context" "encoding/base64" "io" "net/http" "strings" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/transportcommon" ) func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) { transportConfiguration := streamSettings.ProtocolSettings.(*Config) dialer := func(earlyData []byte) (net.Conn, io.Reader, error) { conn, err := transportcommon.DialWithSecuritySettings(ctx, dest, streamSettings) if err != nil { return nil, nil, newError("failed to dial request to ", dest).Base(err) } req, err := http.NewRequest("GET", transportConfiguration.GetNormalizedPath(), nil) if err != nil { return nil, nil, err } req.Header.Set("Connection", "upgrade") req.Header.Set("Upgrade", "websocket") req.Host = transportConfiguration.Host if transportConfiguration.Header != nil { for _, value := range transportConfiguration.Header { req.Header.Set(value.Key, value.Value) } } earlyDataSize := len(earlyData) if earlyDataSize > int(transportConfiguration.MaxEarlyData) { earlyDataSize = int(transportConfiguration.MaxEarlyData) } if len(earlyData) > 0 { if transportConfiguration.EarlyDataHeaderName == "" { return nil, nil, newError("EarlyDataHeaderName is not set") } req.Header.Set(transportConfiguration.EarlyDataHeaderName, base64.URLEncoding.EncodeToString(earlyData)) } err = req.Write(conn) if err != nil { return nil, nil, err } if earlyData != nil && len(earlyData[earlyDataSize:]) > 0 { _, err = conn.Write(earlyData[earlyDataSize:]) if err != nil { return nil, nil, newError("failed to finish write early data").Base(err) } } bufferedConn := bufio.NewReader(conn) resp, err := http.ReadResponse(bufferedConn, req) // nolint:bodyclose if err != nil { return nil, nil, err } if resp.Status == "101 Switching Protocols" && strings.ToLower(resp.Header.Get("Upgrade")) == "websocket" && strings.ToLower(resp.Header.Get("Connection")) == "upgrade" { earlyReplyReader := io.LimitReader(bufferedConn, int64(bufferedConn.Buffered())) return conn, earlyReplyReader, nil } return nil, nil, newError("unrecognized reply") } if transportConfiguration.MaxEarlyData == 0 { conn, earlyReplyReader, err := dialer(nil) if err != nil { return nil, err } remoteAddr := conn.RemoteAddr() return newConnectionWithPendingRead(conn, remoteAddr, earlyReplyReader), nil } return newConnectionWithDelayedDial(dialer), nil } func dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn, err := dialhttpUpgrade(ctx, dest, streamSettings) if err != nil { return nil, newError("failed to dial request to ", dest).Base(err) } return internet.Connection(conn), nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, dial)) } ================================================ FILE: transport/internet/httpupgrade/errors.generated.go ================================================ package httpupgrade import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/httpupgrade/httpupgrade.go ================================================ package httpupgrade import ( "context" "github.com/v2fly/v2ray-core/v5/common" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const protocolName = "httpupgrade" func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return nil, newError("httpupgrade is a transport protocol.") })) } ================================================ FILE: transport/internet/httpupgrade/hub.go ================================================ package httpupgrade import ( "bufio" "bytes" "context" "encoding/base64" "net/http" "strings" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/transportcommon" ) type server struct { config *Config addConn internet.ConnHandler innnerListener net.Listener } func (s *server) Close() error { return s.innnerListener.Close() } func (s *server) Addr() net.Addr { return nil } func (s *server) Handle(conn net.Conn) { upgradedConn, err := s.upgrade(conn) if err != nil { conn.Close() newError("failed to handle request").Base(err).WriteToLog() return } s.addConn(upgradedConn) } // upgrade execute a fake websocket upgrade process and return the available connection func (s *server) upgrade(conn net.Conn) (internet.Connection, error) { connReader := bufio.NewReader(conn) req, err := http.ReadRequest(connReader) if err != nil { return nil, err } connection := strings.ToLower(req.Header.Get("Connection")) upgrade := strings.ToLower(req.Header.Get("Upgrade")) if connection != "upgrade" || upgrade != "websocket" { _ = conn.Close() return nil, newError("unrecognized request") } resp := &http.Response{ Status: "101 Switching Protocols", StatusCode: 101, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: http.Header{}, } resp.Header.Set("Connection", "upgrade") resp.Header.Set("Upgrade", "websocket") err = resp.Write(conn) if err != nil { _ = conn.Close() return nil, err } if s.config.MaxEarlyData != 0 { if s.config.EarlyDataHeaderName == "" { return nil, newError("EarlyDataHeaderName is not set") } earlyData := req.Header.Get(s.config.EarlyDataHeaderName) if earlyData != "" { earlyDataBytes, err := base64.URLEncoding.DecodeString(earlyData) if err != nil { return nil, err } return newConnectionWithPendingRead(conn, conn.RemoteAddr(), bytes.NewReader(earlyDataBytes)), nil } } return internet.Connection(conn), nil } func (s *server) keepAccepting() { for { conn, err := s.innnerListener.Accept() if err != nil { return } go s.Handle(conn) } } func listenHTTPUpgrade(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { transportConfiguration := streamSettings.ProtocolSettings.(*Config) serverInstance := &server{config: transportConfiguration, addConn: addConn} listener, err := transportcommon.ListenWithSecuritySettings(ctx, address, port, streamSettings) if err != nil { return nil, newError("failed to listen on ", address, ":", port).Base(err) } serverInstance.innnerListener = listener go serverInstance.keepAccepting() return serverInstance, nil } func init() { common.Must(internet.RegisterTransportListener(protocolName, listenHTTPUpgrade)) } ================================================ FILE: transport/internet/hysteria2/config.pb.go ================================================ package hysteria2 import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Congestion struct { state protoimpl.MessageState `protogen:"open.v1"` Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` UpMbps uint64 `protobuf:"varint,2,opt,name=up_mbps,json=upMbps,proto3" json:"up_mbps,omitempty"` DownMbps uint64 `protobuf:"varint,3,opt,name=down_mbps,json=downMbps,proto3" json:"down_mbps,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Congestion) Reset() { *x = Congestion{} mi := &file_transport_internet_hysteria2_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Congestion) String() string { return protoimpl.X.MessageStringOf(x) } func (*Congestion) ProtoMessage() {} func (x *Congestion) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_hysteria2_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Congestion.ProtoReflect.Descriptor instead. func (*Congestion) Descriptor() ([]byte, []int) { return file_transport_internet_hysteria2_config_proto_rawDescGZIP(), []int{0} } func (x *Congestion) GetType() string { if x != nil { return x.Type } return "" } func (x *Congestion) GetUpMbps() uint64 { if x != nil { return x.UpMbps } return 0 } func (x *Congestion) GetDownMbps() uint64 { if x != nil { return x.DownMbps } return 0 } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` Congestion *Congestion `protobuf:"bytes,4,opt,name=congestion,proto3" json:"congestion,omitempty"` IgnoreClientBandwidth bool `protobuf:"varint,5,opt,name=ignore_client_bandwidth,json=ignoreClientBandwidth,proto3" json:"ignore_client_bandwidth,omitempty"` UseUdpExtension bool `protobuf:"varint,6,opt,name=use_udp_extension,json=useUdpExtension,proto3" json:"use_udp_extension,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_hysteria2_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_hysteria2_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_hysteria2_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetPassword() string { if x != nil { return x.Password } return "" } func (x *Config) GetCongestion() *Congestion { if x != nil { return x.Congestion } return nil } func (x *Config) GetIgnoreClientBandwidth() bool { if x != nil { return x.IgnoreClientBandwidth } return false } func (x *Config) GetUseUdpExtension() bool { if x != nil { return x.UseUdpExtension } return false } var File_transport_internet_hysteria2_config_proto protoreflect.FileDescriptor const file_transport_internet_hysteria2_config_proto_rawDesc = "" + "\n" + ")transport/internet/hysteria2/config.proto\x12'v2ray.core.transport.internet.hysteria2\x1a common/protoext/extensions.proto\"V\n" + "\n" + "Congestion\x12\x12\n" + "\x04type\x18\x01 \x01(\tR\x04type\x12\x17\n" + "\aup_mbps\x18\x02 \x01(\x04R\x06upMbps\x12\x1b\n" + "\tdown_mbps\x18\x03 \x01(\x04R\bdownMbps\"\xf9\x01\n" + "\x06Config\x12\x1a\n" + "\bpassword\x18\x03 \x01(\tR\bpassword\x12S\n" + "\n" + "congestion\x18\x04 \x01(\v23.v2ray.core.transport.internet.hysteria2.CongestionR\n" + "congestion\x126\n" + "\x17ignore_client_bandwidth\x18\x05 \x01(\bR\x15ignoreClientBandwidth\x12*\n" + "\x11use_udp_extension\x18\x06 \x01(\bR\x0fuseUdpExtension:\x1a\x82\xb5\x18\x16\n" + "\ttransport\x12\thysteria2B\x96\x01\n" + "+com.v2ray.core.transport.internet.hysteria2P\x01Z;github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2\xaa\x02'V2Ray.Core.Transport.Internet.Hysteria2b\x06proto3" var ( file_transport_internet_hysteria2_config_proto_rawDescOnce sync.Once file_transport_internet_hysteria2_config_proto_rawDescData []byte ) func file_transport_internet_hysteria2_config_proto_rawDescGZIP() []byte { file_transport_internet_hysteria2_config_proto_rawDescOnce.Do(func() { file_transport_internet_hysteria2_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_hysteria2_config_proto_rawDesc), len(file_transport_internet_hysteria2_config_proto_rawDesc))) }) return file_transport_internet_hysteria2_config_proto_rawDescData } var file_transport_internet_hysteria2_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_transport_internet_hysteria2_config_proto_goTypes = []any{ (*Congestion)(nil), // 0: v2ray.core.transport.internet.hysteria2.Congestion (*Config)(nil), // 1: v2ray.core.transport.internet.hysteria2.Config } var file_transport_internet_hysteria2_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.transport.internet.hysteria2.Config.congestion:type_name -> v2ray.core.transport.internet.hysteria2.Congestion 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_hysteria2_config_proto_init() } func file_transport_internet_hysteria2_config_proto_init() { if File_transport_internet_hysteria2_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_hysteria2_config_proto_rawDesc), len(file_transport_internet_hysteria2_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_hysteria2_config_proto_goTypes, DependencyIndexes: file_transport_internet_hysteria2_config_proto_depIdxs, MessageInfos: file_transport_internet_hysteria2_config_proto_msgTypes, }.Build() File_transport_internet_hysteria2_config_proto = out.File file_transport_internet_hysteria2_config_proto_goTypes = nil file_transport_internet_hysteria2_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/hysteria2/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.hysteria2; option csharp_namespace = "V2Ray.Core.Transport.Internet.Hysteria2"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2"; option java_package = "com.v2ray.core.transport.internet.hysteria2"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Congestion{ string type = 1; uint64 up_mbps = 2; uint64 down_mbps = 3; } message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "hysteria2"; string password = 3; Congestion congestion = 4; bool ignore_client_bandwidth = 5; bool use_udp_extension = 6; } ================================================ FILE: transport/internet/hysteria2/conn.go ================================================ package hysteria2 import ( "time" hyClient "github.com/v2fly/hysteria/core/v2/client" "github.com/v2fly/hysteria/core/v2/international/protocol" "github.com/v2fly/hysteria/core/v2/international/utils" hyServer "github.com/v2fly/hysteria/core/v2/server" "github.com/v2fly/v2ray-core/v5/common/net" ) const ( CanNotUseUDPExtension = "Only hysteria2 proxy protocol can use udpExtension." Hy2MustNeedTLS = "Hysteria2 based on QUIC that requires TLS." ) type HyConn struct { IsUDPExtension bool IsServer bool ClientUDPSession hyClient.HyUDPConn ServerUDPSession *hyServer.UdpSessionEntry stream *utils.QStream local net.Addr remote net.Addr } func (c *HyConn) Read(b []byte) (int, error) { if c.IsUDPExtension { n, data, _, err := c.ReadPacket() copy(b, data) return n, err } return c.stream.Read(b) } func (c *HyConn) Write(b []byte) (int, error) { if c.IsUDPExtension { dest, _ := net.ParseDestination("udp:v2fly.org:6666") return c.WritePacket(b, dest) } return c.stream.Write(b) } func (c *HyConn) WritePacket(b []byte, dest net.Destination) (int, error) { if !c.IsUDPExtension { return 0, newError(CanNotUseUDPExtension) } if c.IsServer { msg := &protocol.UDPMessage{ SessionID: c.ServerUDPSession.ID, PacketID: 0, FragID: 0, FragCount: 1, Addr: dest.NetAddr(), Data: b, } c.ServerUDPSession.SendCh <- msg return len(b), nil } return len(b), c.ClientUDPSession.Send(b, dest.NetAddr()) } func (c *HyConn) ReadPacket() (int, []byte, *net.Destination, error) { if !c.IsUDPExtension { return 0, nil, nil, newError(CanNotUseUDPExtension) } if c.IsServer { msg, ok := <-c.ServerUDPSession.ReceiveCh if !ok { return 0, nil, nil, newError("UDP session receive channel closed") } dest, err := net.ParseDestination("udp:" + msg.Addr) return len(msg.Data), msg.Data, &dest, err } data, address, err := c.ClientUDPSession.Receive() if err != nil { return 0, nil, nil, err } dest, err := net.ParseDestination("udp:" + address) if err != nil { return 0, nil, nil, err } return len(data), data, &dest, nil } func (c *HyConn) Close() error { if c.IsUDPExtension { if !c.IsServer && c.ClientUDPSession == nil || (c.IsServer && c.ServerUDPSession == nil) { return newError(CanNotUseUDPExtension) } if c.IsServer { c.ServerUDPSession.CloseWithErr(nil) return nil } return c.ClientUDPSession.Close() } return c.stream.Close() } func (c *HyConn) LocalAddr() net.Addr { return c.local } func (c *HyConn) RemoteAddr() net.Addr { return c.remote } func (c *HyConn) SetDeadline(t time.Time) error { if c.IsUDPExtension { return nil } return c.stream.SetDeadline(t) } func (c *HyConn) SetReadDeadline(t time.Time) error { if c.IsUDPExtension { return nil } return c.stream.SetReadDeadline(t) } func (c *HyConn) SetWriteDeadline(t time.Time) error { if c.IsUDPExtension { return nil } return c.stream.SetWriteDeadline(t) } ================================================ FILE: transport/internet/hysteria2/dialer.go ================================================ package hysteria2 import ( "context" "sync" "github.com/apernet/quic-go/quicvarint" hyClient "github.com/v2fly/hysteria/core/v2/client" hyProtocol "github.com/v2fly/hysteria/core/v2/international/protocol" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) type dialerConf struct { net.Destination *internet.MemoryStreamConfig } var ( RunningClient map[dialerConf](hyClient.Client) ClientMutex sync.Mutex MBps uint64 = 1000000 / 8 // MByte ) func GetClientTLSConfig(dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*hyClient.TLSConfig, error) { config := tls.ConfigFromStreamSettings(streamSettings) if config == nil { return nil, newError(Hy2MustNeedTLS) } tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) return &hyClient.TLSConfig{ RootCAs: tlsConfig.RootCAs, ServerName: tlsConfig.ServerName, InsecureSkipVerify: tlsConfig.InsecureSkipVerify, VerifyPeerCertificate: tlsConfig.VerifyPeerCertificate, }, nil } func ResolveAddress(dest net.Destination) (net.Addr, error) { var destAddr *net.UDPAddr if dest.Address.Family().IsIP() { destAddr = &net.UDPAddr{ IP: dest.Address.IP(), Port: int(dest.Port), } } else { addr, err := net.ResolveUDPAddr("udp", dest.NetAddr()) if err != nil { return nil, err } destAddr = addr } return destAddr, nil } type connFactory struct { hyClient.ConnFactory NewFunc func(addr net.Addr) (net.PacketConn, error) } func (f *connFactory) New(addr net.Addr) (net.PacketConn, error) { return f.NewFunc(addr) } func NewHyClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) (hyClient.Client, error) { tlsConfig, err := GetClientTLSConfig(dest, streamSettings) if err != nil { return nil, err } serverAddr, err := ResolveAddress(dest) if err != nil { return nil, err } config := streamSettings.ProtocolSettings.(*Config) client, _, err := hyClient.NewClient(&hyClient.Config{ Auth: config.GetPassword(), TLSConfig: *tlsConfig, ServerAddr: serverAddr, ConnFactory: &connFactory{ NewFunc: func(addr net.Addr) (net.PacketConn, error) { rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{ IP: []byte{0, 0, 0, 0}, Port: 0, }, streamSettings.SocketSettings) if err != nil { return nil, err } return rawConn.(*net.UDPConn), nil }, }, BandwidthConfig: hyClient.BandwidthConfig{MaxTx: config.Congestion.GetUpMbps() * MBps, MaxRx: config.GetCongestion().GetDownMbps() * MBps}, }) if err != nil { return nil, err } return client, nil } func CloseHyClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) error { ClientMutex.Lock() defer ClientMutex.Unlock() client, found := RunningClient[dialerConf{dest, streamSettings}] if found { delete(RunningClient, dialerConf{dest, streamSettings}) return client.Close() } return nil } func GetHyClient(dest net.Destination, streamSettings *internet.MemoryStreamConfig) (hyClient.Client, error) { var err error var client hyClient.Client ClientMutex.Lock() client, found := RunningClient[dialerConf{dest, streamSettings}] ClientMutex.Unlock() if !found || !CheckHyClientHealthy(client) { if found { // retry CloseHyClient(dest, streamSettings) } client, err = NewHyClient(dest, streamSettings) if err != nil { return nil, err } ClientMutex.Lock() RunningClient[dialerConf{dest, streamSettings}] = client ClientMutex.Unlock() } return client, nil } func CheckHyClientHealthy(client hyClient.Client) bool { quicConn := client.GetQuicConn() if quicConn == nil { return false } select { case <-quicConn.Context().Done(): return false default: } return true } func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { config := streamSettings.ProtocolSettings.(*Config) client, err := GetHyClient(dest, streamSettings) if err != nil { CloseHyClient(dest, streamSettings) return nil, err } quicConn := client.GetQuicConn() conn := &HyConn{ local: quicConn.LocalAddr(), remote: quicConn.RemoteAddr(), } outbound := session.OutboundFromContext(ctx) network := net.Network_TCP if outbound != nil { network = outbound.Target.Network } if network == net.Network_UDP && config.GetUseUdpExtension() { // only hysteria2 can use udpExtension conn.IsUDPExtension = true conn.IsServer = false conn.ClientUDPSession, err = client.UDP() if err != nil { CloseHyClient(dest, streamSettings) return nil, err } return conn, nil } conn.stream, err = client.OpenStream() if err != nil { CloseHyClient(dest, streamSettings) return nil, err } // write TCP frame type frameSize := quicvarint.Len(hyProtocol.FrameTypeTCPRequest) buf := make([]byte, frameSize) hyProtocol.VarintPut(buf, hyProtocol.FrameTypeTCPRequest) _, err = conn.stream.Write(buf) if err != nil { CloseHyClient(dest, streamSettings) return nil, err } return conn, nil } func init() { RunningClient = make(map[dialerConf]hyClient.Client) common.Must(internet.RegisterTransportDialer(protocolName, Dial)) } ================================================ FILE: transport/internet/hysteria2/errors.generated.go ================================================ package hysteria2 import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/hysteria2/hub.go ================================================ package hysteria2 import ( "context" "github.com/apernet/quic-go" "github.com/apernet/quic-go/http3" "github.com/v2fly/hysteria/core/v2/international/utils" hyServer "github.com/v2fly/hysteria/core/v2/server" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) // Listener is an internet.Listener that listens for TCP connections. type Listener struct { hyServer hyServer.Server rawConn net.PacketConn addConn internet.ConnHandler } // Addr implements internet.Listener.Addr. func (l *Listener) Addr() net.Addr { return l.rawConn.LocalAddr() } // Close implements internet.Listener.Close. func (l *Listener) Close() error { return l.hyServer.Close() } func (l *Listener) StreamHijacker(ft http3.FrameType, conn *quic.Conn, stream *utils.QStream, err error) (bool, error) { // err always == nil tcpConn := &HyConn{ stream: stream, local: conn.LocalAddr(), remote: conn.RemoteAddr(), } l.addConn(tcpConn) return true, nil } func (l *Listener) UDPHijacker(entry *hyServer.UdpSessionEntry, originalAddr string) { addr, err := net.ResolveUDPAddr("udp", originalAddr) if err != nil { return } udpConn := &HyConn{ IsUDPExtension: true, IsServer: true, ServerUDPSession: entry, remote: addr, local: l.rawConn.LocalAddr(), } l.addConn(udpConn) } // Listen creates a new Listener based on configurations. func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { tlsConfig, err := GetServerTLSConfig(streamSettings) if err != nil { return nil, err } if address.Family().IsDomain() { return nil, nil } config := streamSettings.ProtocolSettings.(*Config) rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{ IP: address.IP(), Port: int(port), }, streamSettings.SocketSettings) if err != nil { return nil, err } listener := &Listener{ rawConn: rawConn, addConn: handler, } hyServer, err := hyServer.NewServer(&hyServer.Config{ Conn: rawConn, TLSConfig: *tlsConfig, DisableUDP: !config.GetUseUdpExtension(), Authenticator: &Authenticator{Password: config.GetPassword()}, StreamHijacker: listener.StreamHijacker, // acceptStreams BandwidthConfig: hyServer.BandwidthConfig{MaxTx: config.Congestion.GetUpMbps() * MBps, MaxRx: config.GetCongestion().GetDownMbps() * MBps}, UdpSessionHijacker: listener.UDPHijacker, // acceptUDPSession IgnoreClientBandwidth: config.GetIgnoreClientBandwidth(), }) if err != nil { return nil, err } listener.hyServer = hyServer go hyServer.Serve() return listener, nil } func GetServerTLSConfig(streamSettings *internet.MemoryStreamConfig) (*hyServer.TLSConfig, error) { config := tls.ConfigFromStreamSettings(streamSettings) if config == nil { return nil, newError(Hy2MustNeedTLS) } tlsConfig := config.GetTLSConfig() return &hyServer.TLSConfig{Certificates: tlsConfig.Certificates, GetCertificate: tlsConfig.GetCertificate}, nil } type Authenticator struct { Password string } func (a *Authenticator) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) { if auth == a.Password || a.Password == "" { return true, "user" } return false, "" } func init() { common.Must(internet.RegisterTransportListener(protocolName, Listen)) } ================================================ FILE: transport/internet/hysteria2/hy2_transport_test.go ================================================ package hysteria2_test import ( "context" "crypto/rand" "fmt" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/hysteria2" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func TestTCP(t *testing.T) { port := udp.PickPort() listener, err := hysteria2.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ ProtocolName: "hysteria2", ProtocolSettings: &hysteria2.Config{Password: "123"}, SecurityType: "tls", SecuritySettings: &tls.Config{ Certificate: []*tls.Certificate{ tls.ParseCertificate( cert.MustGenerate(nil, cert.DNSNames("www.v2fly.org"), ), ), }, }, }, func(conn internet.Connection) { go func() { defer conn.Close() b := buf.New() defer b.Release() for { b.Clear() if _, err := b.ReadFrom(conn); err != nil { fmt.Println(err) return } common.Must2(conn.Write(b.Bytes())) } }() }) common.Must(err) defer listener.Close() time.Sleep(time.Second) dctx := context.Background() conn, err := hysteria2.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ ProtocolName: "hysteria2", ProtocolSettings: &hysteria2.Config{Password: "123"}, SecurityType: "tls", SecuritySettings: &tls.Config{ ServerName: "www.v2fly.org", AllowInsecure: true, }, }) common.Must(err) defer conn.Close() const N = 1000 b1 := make([]byte, N) common.Must2(rand.Read(b1)) b2 := buf.New() common.Must2(conn.Write(b1)) b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } } func TestUDP(t *testing.T) { port := udp.PickPort() listener, err := hysteria2.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ ProtocolName: "hysteria2", ProtocolSettings: &hysteria2.Config{Password: "123", UseUdpExtension: true}, SecurityType: "tls", SecuritySettings: &tls.Config{ Certificate: []*tls.Certificate{ tls.ParseCertificate( cert.MustGenerate(nil, cert.DNSNames("www.v2fly.org"), ), ), }, }, }, func(conn internet.Connection) { fmt.Println("incoming") go func() { defer conn.Close() b := buf.New() defer b.Release() for { b.Clear() if _, err := b.ReadFrom(conn); err != nil { fmt.Println(err) return } common.Must2(conn.Write(b.Bytes())) } }() }) common.Must(err) defer listener.Close() time.Sleep(time.Second) address, err := net.ParseDestination("udp:127.0.0.1:1180") common.Must(err) dctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: address}) conn, err := hysteria2.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ ProtocolName: "hysteria2", ProtocolSettings: &hysteria2.Config{Password: "123", UseUdpExtension: true}, SecurityType: "tls", SecuritySettings: &tls.Config{ ServerName: "www.v2fly.org", AllowInsecure: true, }, }) common.Must(err) defer conn.Close() const N = 1000 b1 := make([]byte, N) common.Must2(rand.Read(b1)) common.Must2(conn.Write(b1)) b2 := buf.New() b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } } ================================================ FILE: transport/internet/hysteria2/hysteria2.go ================================================ package hysteria2 import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const ( protocolName = "hysteria2" ) func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/internet.go ================================================ package internet //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/kcp/config.go ================================================ package kcp import ( "crypto/cipher" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" ) const protocolName = "mkcp" // GetMTUValue returns the value of MTU settings. func (c *Config) GetMTUValue() uint32 { if c == nil || c.Mtu == nil { return 1350 } return c.Mtu.Value } // GetTTIValue returns the value of TTI settings. func (c *Config) GetTTIValue() uint32 { if c == nil || c.Tti == nil { return 50 } return c.Tti.Value } // GetUplinkCapacityValue returns the value of UplinkCapacity settings. func (c *Config) GetUplinkCapacityValue() uint32 { if c == nil || c.UplinkCapacity == nil { return 5 } return c.UplinkCapacity.Value } // GetDownlinkCapacityValue returns the value of DownlinkCapacity settings. func (c *Config) GetDownlinkCapacityValue() uint32 { if c == nil || c.DownlinkCapacity == nil { return 20 } return c.DownlinkCapacity.Value } // GetWriteBufferSize returns the size of WriterBuffer in bytes. func (c *Config) GetWriteBufferSize() uint32 { if c == nil || c.WriteBuffer == nil { return 2 * 1024 * 1024 } return c.WriteBuffer.Size } // GetReadBufferSize returns the size of ReadBuffer in bytes. func (c *Config) GetReadBufferSize() uint32 { if c == nil || c.ReadBuffer == nil { return 2 * 1024 * 1024 } return c.ReadBuffer.Size } // GetSecurity returns the security settings. func (c *Config) GetSecurity() (cipher.AEAD, error) { if c.Seed != nil { return NewAEADAESGCMBasedOnSeed(c.Seed.Seed), nil } return NewSimpleAuthenticator(), nil } func (c *Config) GetPackerHeader() (internet.PacketHeader, error) { if c.HeaderConfig != nil { rawConfig, err := serial.GetInstanceOf(c.HeaderConfig) if err != nil { return nil, err } return internet.CreatePacketHeader(rawConfig) } return nil, nil } func (c *Config) GetSendingInFlightSize() uint32 { size := c.GetUplinkCapacityValue() * 1024 * 1024 / c.GetMTUValue() / (1000 / c.GetTTIValue()) if size < 8 { size = 8 } return size } func (c *Config) GetSendingBufferSize() uint32 { return c.GetWriteBufferSize() / c.GetMTUValue() } func (c *Config) GetReceivingInFlightSize() uint32 { size := c.GetDownlinkCapacityValue() * 1024 * 1024 / c.GetMTUValue() / (1000 / c.GetTTIValue()) if size < 8 { size = 8 } return size } func (c *Config) GetReceivingBufferSize() uint32 { return c.GetReadBufferSize() / c.GetMTUValue() } func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/kcp/config.pb.go ================================================ package kcp import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Maximum Transmission Unit, in bytes. type MTU struct { state protoimpl.MessageState `protogen:"open.v1"` Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MTU) Reset() { *x = MTU{} mi := &file_transport_internet_kcp_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MTU) String() string { return protoimpl.X.MessageStringOf(x) } func (*MTU) ProtoMessage() {} func (x *MTU) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MTU.ProtoReflect.Descriptor instead. func (*MTU) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{0} } func (x *MTU) GetValue() uint32 { if x != nil { return x.Value } return 0 } // Transmission Time Interview, in milli-sec. type TTI struct { state protoimpl.MessageState `protogen:"open.v1"` Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TTI) Reset() { *x = TTI{} mi := &file_transport_internet_kcp_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TTI) String() string { return protoimpl.X.MessageStringOf(x) } func (*TTI) ProtoMessage() {} func (x *TTI) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TTI.ProtoReflect.Descriptor instead. func (*TTI) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{1} } func (x *TTI) GetValue() uint32 { if x != nil { return x.Value } return 0 } // Uplink capacity, in MB. type UplinkCapacity struct { state protoimpl.MessageState `protogen:"open.v1"` Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UplinkCapacity) Reset() { *x = UplinkCapacity{} mi := &file_transport_internet_kcp_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UplinkCapacity) String() string { return protoimpl.X.MessageStringOf(x) } func (*UplinkCapacity) ProtoMessage() {} func (x *UplinkCapacity) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UplinkCapacity.ProtoReflect.Descriptor instead. func (*UplinkCapacity) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{2} } func (x *UplinkCapacity) GetValue() uint32 { if x != nil { return x.Value } return 0 } // Downlink capacity, in MB. type DownlinkCapacity struct { state protoimpl.MessageState `protogen:"open.v1"` Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DownlinkCapacity) Reset() { *x = DownlinkCapacity{} mi := &file_transport_internet_kcp_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DownlinkCapacity) String() string { return protoimpl.X.MessageStringOf(x) } func (*DownlinkCapacity) ProtoMessage() {} func (x *DownlinkCapacity) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DownlinkCapacity.ProtoReflect.Descriptor instead. func (*DownlinkCapacity) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{3} } func (x *DownlinkCapacity) GetValue() uint32 { if x != nil { return x.Value } return 0 } type WriteBuffer struct { state protoimpl.MessageState `protogen:"open.v1"` // Buffer size in bytes. Size uint32 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WriteBuffer) Reset() { *x = WriteBuffer{} mi := &file_transport_internet_kcp_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *WriteBuffer) String() string { return protoimpl.X.MessageStringOf(x) } func (*WriteBuffer) ProtoMessage() {} func (x *WriteBuffer) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use WriteBuffer.ProtoReflect.Descriptor instead. func (*WriteBuffer) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{4} } func (x *WriteBuffer) GetSize() uint32 { if x != nil { return x.Size } return 0 } type ReadBuffer struct { state protoimpl.MessageState `protogen:"open.v1"` // Buffer size in bytes. Size uint32 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ReadBuffer) Reset() { *x = ReadBuffer{} mi := &file_transport_internet_kcp_config_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ReadBuffer) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReadBuffer) ProtoMessage() {} func (x *ReadBuffer) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReadBuffer.ProtoReflect.Descriptor instead. func (*ReadBuffer) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{5} } func (x *ReadBuffer) GetSize() uint32 { if x != nil { return x.Size } return 0 } type ConnectionReuse struct { state protoimpl.MessageState `protogen:"open.v1"` Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConnectionReuse) Reset() { *x = ConnectionReuse{} mi := &file_transport_internet_kcp_config_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConnectionReuse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConnectionReuse) ProtoMessage() {} func (x *ConnectionReuse) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConnectionReuse.ProtoReflect.Descriptor instead. func (*ConnectionReuse) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{6} } func (x *ConnectionReuse) GetEnable() bool { if x != nil { return x.Enable } return false } // Maximum Transmission Unit, in bytes. type EncryptionSeed struct { state protoimpl.MessageState `protogen:"open.v1"` Seed string `protobuf:"bytes,1,opt,name=seed,proto3" json:"seed,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EncryptionSeed) Reset() { *x = EncryptionSeed{} mi := &file_transport_internet_kcp_config_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EncryptionSeed) String() string { return protoimpl.X.MessageStringOf(x) } func (*EncryptionSeed) ProtoMessage() {} func (x *EncryptionSeed) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EncryptionSeed.ProtoReflect.Descriptor instead. func (*EncryptionSeed) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{7} } func (x *EncryptionSeed) GetSeed() string { if x != nil { return x.Seed } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Mtu *MTU `protobuf:"bytes,1,opt,name=mtu,proto3" json:"mtu,omitempty"` Tti *TTI `protobuf:"bytes,2,opt,name=tti,proto3" json:"tti,omitempty"` UplinkCapacity *UplinkCapacity `protobuf:"bytes,3,opt,name=uplink_capacity,json=uplinkCapacity,proto3" json:"uplink_capacity,omitempty"` DownlinkCapacity *DownlinkCapacity `protobuf:"bytes,4,opt,name=downlink_capacity,json=downlinkCapacity,proto3" json:"downlink_capacity,omitempty"` Congestion bool `protobuf:"varint,5,opt,name=congestion,proto3" json:"congestion,omitempty"` WriteBuffer *WriteBuffer `protobuf:"bytes,6,opt,name=write_buffer,json=writeBuffer,proto3" json:"write_buffer,omitempty"` ReadBuffer *ReadBuffer `protobuf:"bytes,7,opt,name=read_buffer,json=readBuffer,proto3" json:"read_buffer,omitempty"` HeaderConfig *anypb.Any `protobuf:"bytes,8,opt,name=header_config,json=headerConfig,proto3" json:"header_config,omitempty"` Seed *EncryptionSeed `protobuf:"bytes,10,opt,name=seed,proto3" json:"seed,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_kcp_config_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_kcp_config_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_kcp_config_proto_rawDescGZIP(), []int{8} } func (x *Config) GetMtu() *MTU { if x != nil { return x.Mtu } return nil } func (x *Config) GetTti() *TTI { if x != nil { return x.Tti } return nil } func (x *Config) GetUplinkCapacity() *UplinkCapacity { if x != nil { return x.UplinkCapacity } return nil } func (x *Config) GetDownlinkCapacity() *DownlinkCapacity { if x != nil { return x.DownlinkCapacity } return nil } func (x *Config) GetCongestion() bool { if x != nil { return x.Congestion } return false } func (x *Config) GetWriteBuffer() *WriteBuffer { if x != nil { return x.WriteBuffer } return nil } func (x *Config) GetReadBuffer() *ReadBuffer { if x != nil { return x.ReadBuffer } return nil } func (x *Config) GetHeaderConfig() *anypb.Any { if x != nil { return x.HeaderConfig } return nil } func (x *Config) GetSeed() *EncryptionSeed { if x != nil { return x.Seed } return nil } var File_transport_internet_kcp_config_proto protoreflect.FileDescriptor const file_transport_internet_kcp_config_proto_rawDesc = "" + "\n" + "#transport/internet/kcp/config.proto\x12!v2ray.core.transport.internet.kcp\x1a\x19google/protobuf/any.proto\x1a common/protoext/extensions.proto\"\x1b\n" + "\x03MTU\x12\x14\n" + "\x05value\x18\x01 \x01(\rR\x05value\"\x1b\n" + "\x03TTI\x12\x14\n" + "\x05value\x18\x01 \x01(\rR\x05value\"&\n" + "\x0eUplinkCapacity\x12\x14\n" + "\x05value\x18\x01 \x01(\rR\x05value\"(\n" + "\x10DownlinkCapacity\x12\x14\n" + "\x05value\x18\x01 \x01(\rR\x05value\"!\n" + "\vWriteBuffer\x12\x12\n" + "\x04size\x18\x01 \x01(\rR\x04size\" \n" + "\n" + "ReadBuffer\x12\x12\n" + "\x04size\x18\x01 \x01(\rR\x04size\")\n" + "\x0fConnectionReuse\x12\x16\n" + "\x06enable\x18\x01 \x01(\bR\x06enable\"$\n" + "\x0eEncryptionSeed\x12\x12\n" + "\x04seed\x18\x01 \x01(\tR\x04seed\"\xa7\x05\n" + "\x06Config\x128\n" + "\x03mtu\x18\x01 \x01(\v2&.v2ray.core.transport.internet.kcp.MTUR\x03mtu\x128\n" + "\x03tti\x18\x02 \x01(\v2&.v2ray.core.transport.internet.kcp.TTIR\x03tti\x12Z\n" + "\x0fuplink_capacity\x18\x03 \x01(\v21.v2ray.core.transport.internet.kcp.UplinkCapacityR\x0euplinkCapacity\x12`\n" + "\x11downlink_capacity\x18\x04 \x01(\v23.v2ray.core.transport.internet.kcp.DownlinkCapacityR\x10downlinkCapacity\x12\x1e\n" + "\n" + "congestion\x18\x05 \x01(\bR\n" + "congestion\x12Q\n" + "\fwrite_buffer\x18\x06 \x01(\v2..v2ray.core.transport.internet.kcp.WriteBufferR\vwriteBuffer\x12N\n" + "\vread_buffer\x18\a \x01(\v2-.v2ray.core.transport.internet.kcp.ReadBufferR\n" + "readBuffer\x129\n" + "\rheader_config\x18\b \x01(\v2\x14.google.protobuf.AnyR\fheaderConfig\x12E\n" + "\x04seed\x18\n" + " \x01(\v21.v2ray.core.transport.internet.kcp.EncryptionSeedR\x04seed: \x82\xb5\x18\x1c\n" + "\ttransport\x12\x03kcp\x8a\xff)\x04mkcp\x90\xff)\x01J\x04\b\t\x10\n" + "B\x84\x01\n" + "%com.v2ray.core.transport.internet.kcpP\x01Z5github.com/v2fly/v2ray-core/v5/transport/internet/kcp\xaa\x02!V2Ray.Core.Transport.Internet.Kcpb\x06proto3" var ( file_transport_internet_kcp_config_proto_rawDescOnce sync.Once file_transport_internet_kcp_config_proto_rawDescData []byte ) func file_transport_internet_kcp_config_proto_rawDescGZIP() []byte { file_transport_internet_kcp_config_proto_rawDescOnce.Do(func() { file_transport_internet_kcp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_kcp_config_proto_rawDesc), len(file_transport_internet_kcp_config_proto_rawDesc))) }) return file_transport_internet_kcp_config_proto_rawDescData } var file_transport_internet_kcp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_transport_internet_kcp_config_proto_goTypes = []any{ (*MTU)(nil), // 0: v2ray.core.transport.internet.kcp.MTU (*TTI)(nil), // 1: v2ray.core.transport.internet.kcp.TTI (*UplinkCapacity)(nil), // 2: v2ray.core.transport.internet.kcp.UplinkCapacity (*DownlinkCapacity)(nil), // 3: v2ray.core.transport.internet.kcp.DownlinkCapacity (*WriteBuffer)(nil), // 4: v2ray.core.transport.internet.kcp.WriteBuffer (*ReadBuffer)(nil), // 5: v2ray.core.transport.internet.kcp.ReadBuffer (*ConnectionReuse)(nil), // 6: v2ray.core.transport.internet.kcp.ConnectionReuse (*EncryptionSeed)(nil), // 7: v2ray.core.transport.internet.kcp.EncryptionSeed (*Config)(nil), // 8: v2ray.core.transport.internet.kcp.Config (*anypb.Any)(nil), // 9: google.protobuf.Any } var file_transport_internet_kcp_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.transport.internet.kcp.Config.mtu:type_name -> v2ray.core.transport.internet.kcp.MTU 1, // 1: v2ray.core.transport.internet.kcp.Config.tti:type_name -> v2ray.core.transport.internet.kcp.TTI 2, // 2: v2ray.core.transport.internet.kcp.Config.uplink_capacity:type_name -> v2ray.core.transport.internet.kcp.UplinkCapacity 3, // 3: v2ray.core.transport.internet.kcp.Config.downlink_capacity:type_name -> v2ray.core.transport.internet.kcp.DownlinkCapacity 4, // 4: v2ray.core.transport.internet.kcp.Config.write_buffer:type_name -> v2ray.core.transport.internet.kcp.WriteBuffer 5, // 5: v2ray.core.transport.internet.kcp.Config.read_buffer:type_name -> v2ray.core.transport.internet.kcp.ReadBuffer 9, // 6: v2ray.core.transport.internet.kcp.Config.header_config:type_name -> google.protobuf.Any 7, // 7: v2ray.core.transport.internet.kcp.Config.seed:type_name -> v2ray.core.transport.internet.kcp.EncryptionSeed 8, // [8:8] is the sub-list for method output_type 8, // [8:8] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name 8, // [8:8] is the sub-list for extension extendee 0, // [0:8] is the sub-list for field type_name } func init() { file_transport_internet_kcp_config_proto_init() } func file_transport_internet_kcp_config_proto_init() { if File_transport_internet_kcp_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_kcp_config_proto_rawDesc), len(file_transport_internet_kcp_config_proto_rawDesc)), NumEnums: 0, NumMessages: 9, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_kcp_config_proto_goTypes, DependencyIndexes: file_transport_internet_kcp_config_proto_depIdxs, MessageInfos: file_transport_internet_kcp_config_proto_msgTypes, }.Build() File_transport_internet_kcp_config_proto = out.File file_transport_internet_kcp_config_proto_goTypes = nil file_transport_internet_kcp_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/kcp/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.kcp; option csharp_namespace = "V2Ray.Core.Transport.Internet.Kcp"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/kcp"; option java_package = "com.v2ray.core.transport.internet.kcp"; option java_multiple_files = true; import "google/protobuf/any.proto"; import "common/protoext/extensions.proto"; // Maximum Transmission Unit, in bytes. message MTU { uint32 value = 1; } // Transmission Time Interview, in milli-sec. message TTI { uint32 value = 1; } // Uplink capacity, in MB. message UplinkCapacity { uint32 value = 1; } // Downlink capacity, in MB. message DownlinkCapacity { uint32 value = 1; } message WriteBuffer { // Buffer size in bytes. uint32 size = 1; } message ReadBuffer { // Buffer size in bytes. uint32 size = 1; } message ConnectionReuse { bool enable = 1; } // Maximum Transmission Unit, in bytes. message EncryptionSeed { string seed = 1; } message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "kcp"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; option (v2ray.core.common.protoext.message_opt).transport_original_name = "mkcp"; MTU mtu = 1; TTI tti = 2; UplinkCapacity uplink_capacity = 3; DownlinkCapacity downlink_capacity = 4; bool congestion = 5; WriteBuffer write_buffer = 6; ReadBuffer read_buffer = 7; google.protobuf.Any header_config = 8; reserved 9; EncryptionSeed seed = 10; } ================================================ FILE: transport/internet/kcp/connection.go ================================================ package kcp import ( "bytes" "io" "net" "runtime" "sync" "sync/atomic" "time" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/signal/semaphore" ) var ( ErrIOTimeout = newError("Read/Write timeout") ErrClosedListener = newError("Listener closed.") ErrClosedConnection = newError("Connection closed.") ) // State of the connection type State int32 // Is returns true if current State is one of the candidates. func (s State) Is(states ...State) bool { for _, state := range states { if s == state { return true } } return false } const ( StateActive State = 0 // Connection is active StateReadyToClose State = 1 // Connection is closed locally StatePeerClosed State = 2 // Connection is closed on remote StateTerminating State = 3 // Connection is ready to be destroyed locally StatePeerTerminating State = 4 // Connection is ready to be destroyed on remote StateTerminated State = 5 // Connection is destroyed. ) func nowMillisec() int64 { now := time.Now() return now.Unix()*1000 + int64(now.Nanosecond()/1000000) } type RoundTripInfo struct { sync.RWMutex variation uint32 srtt uint32 rto uint32 minRtt uint32 updatedTimestamp uint32 } func (info *RoundTripInfo) UpdatePeerRTO(rto uint32, current uint32) { info.Lock() defer info.Unlock() if current-info.updatedTimestamp < 3000 { return } info.updatedTimestamp = current info.rto = rto } func (info *RoundTripInfo) Update(rtt uint32, current uint32) { if rtt > 0x7FFFFFFF { return } info.Lock() defer info.Unlock() // https://tools.ietf.org/html/rfc6298 if info.srtt == 0 { info.srtt = rtt info.variation = rtt / 2 } else { delta := rtt - info.srtt if info.srtt > rtt { delta = info.srtt - rtt } info.variation = (3*info.variation + delta) / 4 info.srtt = (7*info.srtt + rtt) / 8 if info.srtt < info.minRtt { info.srtt = info.minRtt } } var rto uint32 if info.minRtt < 4*info.variation { rto = info.srtt + 4*info.variation } else { rto = info.srtt + info.variation } if rto > 10000 { rto = 10000 } info.rto = rto * 5 / 4 info.updatedTimestamp = current } func (info *RoundTripInfo) Timeout() uint32 { info.RLock() defer info.RUnlock() return info.rto } func (info *RoundTripInfo) SmoothedTime() uint32 { info.RLock() defer info.RUnlock() return info.srtt } type Updater struct { interval int64 shouldContinue func() bool shouldTerminate func() bool updateFunc func() notifier *semaphore.Instance } func NewUpdater(interval uint32, shouldContinue func() bool, shouldTerminate func() bool, updateFunc func()) *Updater { u := &Updater{ interval: int64(time.Duration(interval) * time.Millisecond), shouldContinue: shouldContinue, shouldTerminate: shouldTerminate, updateFunc: updateFunc, notifier: semaphore.New(1), } return u } func (u *Updater) WakeUp() { select { case <-u.notifier.Wait(): go u.run() default: } } func (u *Updater) run() { defer u.notifier.Signal() if u.shouldTerminate() { return } ticker := time.NewTicker(u.Interval()) for u.shouldContinue() { u.updateFunc() <-ticker.C } ticker.Stop() } func (u *Updater) Interval() time.Duration { return time.Duration(atomic.LoadInt64(&u.interval)) } func (u *Updater) SetInterval(d time.Duration) { atomic.StoreInt64(&u.interval, int64(d)) } type ConnMetadata struct { LocalAddr net.Addr RemoteAddr net.Addr Conversation uint16 } // Connection is a KCP connection over UDP. type Connection struct { meta ConnMetadata closer io.Closer rd time.Time wd time.Time // write deadline since int64 dataInput *signal.Notifier dataOutput *signal.Notifier Config *Config state State stateBeginTime uint32 lastIncomingTime uint32 lastPingTime uint32 mss uint32 roundTrip *RoundTripInfo receivingWorker *ReceivingWorker sendingWorker *SendingWorker output SegmentWriter dataUpdater *Updater pingUpdater *Updater } // NewConnection create a new KCP connection between local and remote. func NewConnection(meta ConnMetadata, writer PacketWriter, closer io.Closer, config *Config) *Connection { newError("#", meta.Conversation, " creating connection to ", meta.RemoteAddr).WriteToLog() conn := &Connection{ meta: meta, closer: closer, since: nowMillisec(), dataInput: signal.NewNotifier(), dataOutput: signal.NewNotifier(), Config: config, output: NewRetryableWriter(NewSegmentWriter(writer)), mss: config.GetMTUValue() - uint32(writer.Overhead()) - DataSegmentOverhead, roundTrip: &RoundTripInfo{ rto: 100, minRtt: config.GetTTIValue(), }, } conn.receivingWorker = NewReceivingWorker(conn) conn.sendingWorker = NewSendingWorker(conn) isTerminating := func() bool { return conn.State().Is(StateTerminating, StateTerminated) } isTerminated := func() bool { return conn.State() == StateTerminated } conn.dataUpdater = NewUpdater( config.GetTTIValue(), func() bool { return !isTerminating() && (conn.sendingWorker.UpdateNecessary() || conn.receivingWorker.UpdateNecessary()) }, isTerminating, conn.updateTask) conn.pingUpdater = NewUpdater( 5000, // 5 seconds func() bool { return !isTerminated() }, isTerminated, conn.updateTask) conn.pingUpdater.WakeUp() return conn } func (c *Connection) Elapsed() uint32 { return uint32(nowMillisec() - c.since) } // ReadMultiBuffer implements buf.Reader. func (c *Connection) ReadMultiBuffer() (buf.MultiBuffer, error) { if c == nil { return nil, io.EOF } for { if c.State().Is(StateReadyToClose, StateTerminating, StateTerminated) { return nil, io.EOF } mb := c.receivingWorker.ReadMultiBuffer() if !mb.IsEmpty() { c.dataUpdater.WakeUp() return mb, nil } if c.State() == StatePeerTerminating { return nil, io.EOF } if err := c.waitForDataInput(); err != nil { return nil, err } } } func (c *Connection) waitForDataInput() error { for i := 0; i < 16; i++ { select { case <-c.dataInput.Wait(): return nil default: runtime.Gosched() } } duration := time.Second * 16 if !c.rd.IsZero() { duration = time.Until(c.rd) if duration < 0 { return ErrIOTimeout } } timeout := time.NewTimer(duration) defer timeout.Stop() select { case <-c.dataInput.Wait(): case <-timeout.C: if !c.rd.IsZero() && c.rd.Before(time.Now()) { return ErrIOTimeout } } return nil } // Read implements the Conn Read method. func (c *Connection) Read(b []byte) (int, error) { if c == nil { return 0, io.EOF } for { if c.State().Is(StateReadyToClose, StateTerminating, StateTerminated) { return 0, io.EOF } nBytes := c.receivingWorker.Read(b) if nBytes > 0 { c.dataUpdater.WakeUp() return nBytes, nil } if err := c.waitForDataInput(); err != nil { return 0, err } } } func (c *Connection) waitForDataOutput() error { for i := 0; i < 16; i++ { select { case <-c.dataOutput.Wait(): return nil default: runtime.Gosched() } } duration := time.Second * 16 if !c.wd.IsZero() { duration = time.Until(c.wd) if duration < 0 { return ErrIOTimeout } } timeout := time.NewTimer(duration) defer timeout.Stop() select { case <-c.dataOutput.Wait(): case <-timeout.C: if !c.wd.IsZero() && c.wd.Before(time.Now()) { return ErrIOTimeout } } return nil } // Write implements io.Writer. func (c *Connection) Write(b []byte) (int, error) { reader := bytes.NewReader(b) if err := c.writeMultiBufferInternal(reader); err != nil { return 0, err } return len(b), nil } // WriteMultiBuffer implements buf.Writer. func (c *Connection) WriteMultiBuffer(mb buf.MultiBuffer) error { reader := &buf.MultiBufferContainer{ MultiBuffer: mb, } defer reader.Close() return c.writeMultiBufferInternal(reader) } func (c *Connection) writeMultiBufferInternal(reader io.Reader) error { updatePending := false defer func() { if updatePending { c.dataUpdater.WakeUp() } }() var b *buf.Buffer defer func() { b.Release() }() for { for { if c == nil || c.State() != StateActive { return io.ErrClosedPipe } if b == nil { b = buf.New() _, err := b.ReadFrom(io.LimitReader(reader, int64(c.mss))) if err != nil { return nil } } if !c.sendingWorker.Push(b) { break } updatePending = true b = nil } if updatePending { c.dataUpdater.WakeUp() updatePending = false } if err := c.waitForDataOutput(); err != nil { return err } } } func (c *Connection) SetState(state State) { current := c.Elapsed() atomic.StoreInt32((*int32)(&c.state), int32(state)) atomic.StoreUint32(&c.stateBeginTime, current) newError("#", c.meta.Conversation, " entering state ", state, " at ", current).AtDebug().WriteToLog() switch state { case StateReadyToClose: c.receivingWorker.CloseRead() case StatePeerClosed: c.sendingWorker.CloseWrite() case StateTerminating: c.receivingWorker.CloseRead() c.sendingWorker.CloseWrite() c.pingUpdater.SetInterval(time.Second) case StatePeerTerminating: c.sendingWorker.CloseWrite() c.pingUpdater.SetInterval(time.Second) case StateTerminated: c.receivingWorker.CloseRead() c.sendingWorker.CloseWrite() c.pingUpdater.SetInterval(time.Second) c.dataUpdater.WakeUp() c.pingUpdater.WakeUp() go c.Terminate() } } // Close closes the connection. func (c *Connection) Close() error { if c == nil { return ErrClosedConnection } c.dataInput.Signal() c.dataOutput.Signal() switch c.State() { case StateReadyToClose, StateTerminating, StateTerminated: return ErrClosedConnection case StateActive: c.SetState(StateReadyToClose) case StatePeerClosed: c.SetState(StateTerminating) case StatePeerTerminating: c.SetState(StateTerminated) } newError("#", c.meta.Conversation, " closing connection to ", c.meta.RemoteAddr).WriteToLog() return nil } // LocalAddr returns the local network address. The Addr returned is shared by all invocations of LocalAddr, so do not modify it. func (c *Connection) LocalAddr() net.Addr { if c == nil { return nil } return c.meta.LocalAddr } // RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it. func (c *Connection) RemoteAddr() net.Addr { if c == nil { return nil } return c.meta.RemoteAddr } // SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline. func (c *Connection) SetDeadline(t time.Time) error { if err := c.SetReadDeadline(t); err != nil { return err } return c.SetWriteDeadline(t) } // SetReadDeadline implements the Conn SetReadDeadline method. func (c *Connection) SetReadDeadline(t time.Time) error { if c == nil || c.State() != StateActive { return ErrClosedConnection } c.rd = t return nil } // SetWriteDeadline implements the Conn SetWriteDeadline method. func (c *Connection) SetWriteDeadline(t time.Time) error { if c == nil || c.State() != StateActive { return ErrClosedConnection } c.wd = t return nil } // kcp update, input loop func (c *Connection) updateTask() { c.flush() } func (c *Connection) Terminate() { if c == nil { return } newError("#", c.meta.Conversation, " terminating connection to ", c.RemoteAddr()).WriteToLog() // v.SetState(StateTerminated) c.dataInput.Signal() c.dataOutput.Signal() c.closer.Close() c.sendingWorker.Release() c.receivingWorker.Release() c.output.Release() } func (c *Connection) HandleOption(opt SegmentOption) { if (opt & SegmentOptionClose) == SegmentOptionClose { c.OnPeerClosed() } } func (c *Connection) OnPeerClosed() { switch c.State() { case StateReadyToClose: c.SetState(StateTerminating) case StateActive: c.SetState(StatePeerClosed) } } // Input when you received a low level packet (eg. UDP packet), call it func (c *Connection) Input(segments []Segment) { current := c.Elapsed() atomic.StoreUint32(&c.lastIncomingTime, current) for _, seg := range segments { if seg.Conversation() != c.meta.Conversation { break } switch seg := seg.(type) { case *DataSegment: c.HandleOption(seg.Option) c.receivingWorker.ProcessSegment(seg) if c.receivingWorker.IsDataAvailable() { c.dataInput.Signal() } c.dataUpdater.WakeUp() case *AckSegment: c.HandleOption(seg.Option) c.sendingWorker.ProcessSegment(current, seg, c.roundTrip.Timeout()) c.dataOutput.Signal() c.dataUpdater.WakeUp() case *CmdOnlySegment: c.HandleOption(seg.Option) if seg.Command() == CommandTerminate { switch c.State() { case StateActive, StatePeerClosed: c.SetState(StatePeerTerminating) case StateReadyToClose: c.SetState(StateTerminating) case StateTerminating: c.SetState(StateTerminated) } } if seg.Option == SegmentOptionClose || seg.Command() == CommandTerminate { c.dataInput.Signal() c.dataOutput.Signal() } c.sendingWorker.ProcessReceivingNext(seg.ReceivingNext) c.receivingWorker.ProcessSendingNext(seg.SendingNext) c.roundTrip.UpdatePeerRTO(seg.PeerRTO, current) seg.Release() default: } } } func (c *Connection) flush() { current := c.Elapsed() if c.State() == StateTerminated { return } if c.State() == StateActive && current-atomic.LoadUint32(&c.lastIncomingTime) >= 30000 { c.Close() } if c.State() == StateReadyToClose && c.sendingWorker.IsEmpty() { c.SetState(StateTerminating) } if c.State() == StateTerminating { newError("#", c.meta.Conversation, " sending terminating cmd.").AtDebug().WriteToLog() c.Ping(current, CommandTerminate) if current-atomic.LoadUint32(&c.stateBeginTime) > 8000 { c.SetState(StateTerminated) } return } if c.State() == StatePeerTerminating && current-atomic.LoadUint32(&c.stateBeginTime) > 4000 { c.SetState(StateTerminating) } if c.State() == StateReadyToClose && current-atomic.LoadUint32(&c.stateBeginTime) > 15000 { c.SetState(StateTerminating) } // flush acknowledges c.receivingWorker.Flush(current) c.sendingWorker.Flush(current) if current-atomic.LoadUint32(&c.lastPingTime) >= 3000 { c.Ping(current, CommandPing) } } func (c *Connection) State() State { return State(atomic.LoadInt32((*int32)(&c.state))) } func (c *Connection) Ping(current uint32, cmd Command) { seg := NewCmdOnlySegment() seg.Conv = c.meta.Conversation seg.Cmd = cmd seg.ReceivingNext = c.receivingWorker.NextNumber() seg.SendingNext = c.sendingWorker.FirstUnacknowledged() seg.PeerRTO = c.roundTrip.Timeout() if c.State() == StateReadyToClose { seg.Option = SegmentOptionClose } c.output.Write(seg) atomic.StoreUint32(&c.lastPingTime, current) seg.Release() } ================================================ FILE: transport/internet/kcp/connection_test.go ================================================ package kcp_test import ( "io" "testing" "time" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" ) type NoOpCloser int func (NoOpCloser) Close() error { return nil } func TestConnectionReadTimeout(t *testing.T) { conn := NewConnection(ConnMetadata{Conversation: 1}, &KCPPacketWriter{ Writer: buf.DiscardBytes, }, NoOpCloser(0), &Config{}) conn.SetReadDeadline(time.Now().Add(time.Second)) b := make([]byte, 1024) nBytes, err := conn.Read(b) if nBytes != 0 || err == nil { t.Error("unexpected read: ", nBytes, err) } conn.Terminate() } func TestConnectionInterface(t *testing.T) { _ = (io.Writer)(new(Connection)) _ = (io.Reader)(new(Connection)) _ = (buf.Reader)(new(Connection)) _ = (buf.Writer)(new(Connection)) } ================================================ FILE: transport/internet/kcp/crypt.go ================================================ package kcp import ( "crypto/cipher" "encoding/binary" "hash/fnv" "github.com/v2fly/v2ray-core/v5/common" ) // SimpleAuthenticator is a legacy AEAD used for KCP encryption. type SimpleAuthenticator struct{} // NewSimpleAuthenticator creates a new SimpleAuthenticator func NewSimpleAuthenticator() cipher.AEAD { return &SimpleAuthenticator{} } // NonceSize implements cipher.AEAD.NonceSize(). func (*SimpleAuthenticator) NonceSize() int { return 0 } // Overhead implements cipher.AEAD.NonceSize(). func (*SimpleAuthenticator) Overhead() int { return 6 } // Seal implements cipher.AEAD.Seal(). func (a *SimpleAuthenticator) Seal(dst, nonce, plain, extra []byte) []byte { dst = append(dst, 0, 0, 0, 0, 0, 0) // 4 bytes for hash, and then 2 bytes for length binary.BigEndian.PutUint16(dst[4:], uint16(len(plain))) dst = append(dst, plain...) fnvHash := fnv.New32a() common.Must2(fnvHash.Write(dst[4:])) fnvHash.Sum(dst[:0]) dstLen := len(dst) xtra := 4 - dstLen%4 if xtra != 4 { dst = append(dst, make([]byte, xtra)...) } xorfwd(dst) if xtra != 4 { dst = dst[:dstLen] } return dst } // Open implements cipher.AEAD.Open(). func (a *SimpleAuthenticator) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) { dst = append(dst, cipherText...) dstLen := len(dst) xtra := 4 - dstLen%4 if xtra != 4 { dst = append(dst, make([]byte, xtra)...) } xorbkd(dst) if xtra != 4 { dst = dst[:dstLen] } fnvHash := fnv.New32a() common.Must2(fnvHash.Write(dst[4:])) if binary.BigEndian.Uint32(dst[:4]) != fnvHash.Sum32() { return nil, newError("invalid auth") } length := binary.BigEndian.Uint16(dst[4:6]) if len(dst)-6 != int(length) { return nil, newError("invalid auth") } return dst[6:], nil } ================================================ FILE: transport/internet/kcp/crypt_test.go ================================================ package kcp_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" . "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" ) func TestSimpleAuthenticator(t *testing.T) { cache := make([]byte, 512) payload := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'} auth := NewSimpleAuthenticator() b := auth.Seal(cache[:0], nil, payload, nil) c, err := auth.Open(cache[:0], nil, b, nil) common.Must(err) if r := cmp.Diff(c, payload); r != "" { t.Error(r) } } func TestSimpleAuthenticator2(t *testing.T) { cache := make([]byte, 512) payload := []byte{'a', 'b'} auth := NewSimpleAuthenticator() b := auth.Seal(cache[:0], nil, payload, nil) c, err := auth.Open(cache[:0], nil, b, nil) common.Must(err) if r := cmp.Diff(c, payload); r != "" { t.Error(r) } } ================================================ FILE: transport/internet/kcp/cryptreal.go ================================================ package kcp import ( "crypto/aes" "crypto/cipher" "crypto/sha256" "github.com/v2fly/v2ray-core/v5/common" ) func NewAEADAESGCMBasedOnSeed(seed string) cipher.AEAD { hashedSeed := sha256.Sum256([]byte(seed)) aesBlock := common.Must2(aes.NewCipher(hashedSeed[:16])).(cipher.Block) return common.Must2(cipher.NewGCM(aesBlock)).(cipher.AEAD) } ================================================ FILE: transport/internet/kcp/dialer.go ================================================ package kcp import ( "context" "io" "sync/atomic" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) var globalConv = uint32(dice.RollUint16()) func fetchInput(_ context.Context, input io.Reader, reader PacketReader, conn *Connection) { cache := make(chan *buf.Buffer, 1024) go func() { for { payload := buf.New() if _, err := payload.ReadFrom(input); err != nil { payload.Release() close(cache) return } select { case cache <- payload: default: payload.Release() } } }() for payload := range cache { segments := reader.Read(payload.Bytes()) payload.Release() if len(segments) > 0 { conn.Input(segments) } } } // DialKCP dials a new KCP connections to the specific destination. func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { dest.Network = net.Network_UDP newError("dialing mKCP to ", dest).WriteToLog() transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) dialer := transportEnvironment.Dialer() rawConn, err := dialer.Dial(ctx, nil, dest, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err) } kcpSettings := streamSettings.ProtocolSettings.(*Config) header, err := kcpSettings.GetPackerHeader() if err != nil { return nil, newError("failed to create packet header").Base(err) } security, err := kcpSettings.GetSecurity() if err != nil { return nil, newError("failed to create security").Base(err) } reader := &KCPPacketReader{ Header: header, Security: security, } writer := &KCPPacketWriter{ Header: header, Security: security, Writer: rawConn, } conv := uint16(atomic.AddUint32(&globalConv, 1)) session := NewConnection(ConnMetadata{ LocalAddr: rawConn.LocalAddr(), RemoteAddr: rawConn.RemoteAddr(), Conversation: conv, }, writer, rawConn, kcpSettings) go fetchInput(ctx, rawConn, reader, session) var iConn internet.Connection = session if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { iConn = tls.Client(iConn, config.GetTLSConfig(tls.WithDestination(dest))) } return iConn, nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, DialKCP)) } ================================================ FILE: transport/internet/kcp/errors.generated.go ================================================ package kcp import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/kcp/io.go ================================================ package kcp import ( "crypto/cipher" "crypto/rand" "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type PacketReader interface { Read([]byte) []Segment } type PacketWriter interface { Overhead() int io.Writer } type KCPPacketReader struct { // nolint: revive Security cipher.AEAD Header internet.PacketHeader } func (r *KCPPacketReader) Read(b []byte) []Segment { if r.Header != nil { if int32(len(b)) <= r.Header.Size() { return nil } b = b[r.Header.Size():] } if r.Security != nil { nonceSize := r.Security.NonceSize() overhead := r.Security.Overhead() if len(b) <= nonceSize+overhead { return nil } out, err := r.Security.Open(b[nonceSize:nonceSize], b[:nonceSize], b[nonceSize:], nil) if err != nil { return nil } b = out } var result []Segment for len(b) > 0 { seg, x := ReadSegment(b) if seg == nil { break } result = append(result, seg) b = x } return result } type KCPPacketWriter struct { // nolint: revive Header internet.PacketHeader Security cipher.AEAD Writer io.Writer } func (w *KCPPacketWriter) Overhead() int { overhead := 0 if w.Header != nil { overhead += int(w.Header.Size()) } if w.Security != nil { overhead += w.Security.Overhead() } return overhead } func (w *KCPPacketWriter) Write(b []byte) (int, error) { bb := buf.StackNew() defer bb.Release() if w.Header != nil { w.Header.Serialize(bb.Extend(w.Header.Size())) } if w.Security != nil { nonceSize := w.Security.NonceSize() common.Must2(bb.ReadFullFrom(rand.Reader, int32(nonceSize))) nonce := bb.BytesFrom(int32(-nonceSize)) encrypted := bb.Extend(int32(w.Security.Overhead() + len(b))) w.Security.Seal(encrypted[:0], nonce, b, nil) } else { bb.Write(b) } _, err := w.Writer.Write(bb.Bytes()) return len(b), err } ================================================ FILE: transport/internet/kcp/io_test.go ================================================ package kcp_test import ( "testing" . "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" ) func TestKCPPacketReader(t *testing.T) { reader := KCPPacketReader{ Security: &SimpleAuthenticator{}, } testCases := []struct { Input []byte Output []Segment }{ { Input: []byte{}, Output: nil, }, { Input: []byte{1}, Output: nil, }, } for _, testCase := range testCases { seg := reader.Read(testCase.Input) if testCase.Output == nil && seg != nil { t.Errorf("Expect nothing returned, but actually %v", seg) } else if testCase.Output != nil && seg == nil { t.Errorf("Expect some output, but got nil") } } } ================================================ FILE: transport/internet/kcp/kcp.go ================================================ // Package kcp - A Fast and Reliable ARQ Protocol // // Acknowledgement: // // skywind3000@github for inventing the KCP protocol // xtaci@github for translating to Golang package kcp //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/kcp/kcp_test.go ================================================ package kcp_test import ( "context" "crypto/rand" "io" "testing" "time" "github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage" "github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl" "github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl" "github.com/google/go-cmp/cmp" "golang.org/x/sync/errgroup" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" . "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" ) func TestDialAndListen(t *testing.T) { ctx := context.Background() defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl() deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(ctx) rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(), defaultFilesystemImpl, deferredPersistentStorageImpl) proxyEnvironment := rootEnv.ProxyEnvironment("o") transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("kcp") if err != nil { t.Fatal(err) } ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment) listener, err := NewListener(ctx, net.LocalHostIP, net.Port(0), &internet.MemoryStreamConfig{ ProtocolName: "mkcp", ProtocolSettings: &Config{}, }, func(conn internet.Connection) { go func(c internet.Connection) { payload := make([]byte, 4096) for { nBytes, err := c.Read(payload) if err != nil { break } for idx, b := range payload[:nBytes] { payload[idx] = b ^ 'c' } c.Write(payload[:nBytes]) } c.Close() }(conn) }) common.Must(err) defer listener.Close() port := net.Port(listener.Addr().(*net.UDPAddr).Port) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(func() error { clientConn, err := DialKCP(ctx, net.UDPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ ProtocolName: "mkcp", ProtocolSettings: &Config{}, }) if err != nil { return err } defer clientConn.Close() clientSend := make([]byte, 1024*1024) rand.Read(clientSend) go clientConn.Write(clientSend) clientReceived := make([]byte, 1024*1024) common.Must2(io.ReadFull(clientConn, clientReceived)) clientExpected := make([]byte, 1024*1024) for idx, b := range clientSend { clientExpected[idx] = b ^ 'c' } if r := cmp.Diff(clientReceived, clientExpected); r != "" { return errors.New(r) } return nil }) } if err := errg.Wait(); err != nil { t.Fatal(err) } for i := 0; i < 60 && listener.ActiveConnections() > 0; i++ { time.Sleep(500 * time.Millisecond) } if v := listener.ActiveConnections(); v != 0 { t.Error("active connections: ", v) } } ================================================ FILE: transport/internet/kcp/listener.go ================================================ package kcp import ( "context" "crypto/cipher" gotls "crypto/tls" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" "github.com/v2fly/v2ray-core/v5/transport/internet/udp" ) type ConnectionID struct { Remote net.Address Port net.Port Conv uint16 } // Listener defines a server listening for connections type Listener struct { sync.Mutex sessions map[ConnectionID]*Connection hub *udp.Hub tlsConfig *gotls.Config config *Config reader PacketReader header internet.PacketHeader security cipher.AEAD addConn internet.ConnHandler } func NewListener(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (*Listener, error) { kcpSettings := streamSettings.ProtocolSettings.(*Config) header, err := kcpSettings.GetPackerHeader() if err != nil { return nil, newError("failed to create packet header").Base(err).AtError() } security, err := kcpSettings.GetSecurity() if err != nil { return nil, newError("failed to create security").Base(err).AtError() } l := &Listener{ header: header, security: security, reader: &KCPPacketReader{ Header: header, Security: security, }, sessions: make(map[ConnectionID]*Connection), config: kcpSettings, addConn: addConn, } if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { l.tlsConfig = config.GetTLSConfig() } hub, err := udp.ListenUDP(ctx, address, port, streamSettings, udp.HubCapacity(1024)) if err != nil { return nil, err } l.Lock() l.hub = hub l.Unlock() newError("listening on ", address, ":", port).WriteToLog() go l.handlePackets() return l, nil } func (l *Listener) handlePackets() { receive := l.hub.Receive() for payload := range receive { l.OnReceive(payload.Payload, payload.Source) } } func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination) { segments := l.reader.Read(payload.Bytes()) payload.Release() if len(segments) == 0 { newError("discarding invalid payload from ", src).WriteToLog() return } conv := segments[0].Conversation() cmd := segments[0].Command() id := ConnectionID{ Remote: src.Address, Port: src.Port, Conv: conv, } l.Lock() defer l.Unlock() conn, found := l.sessions[id] if !found { if cmd == CommandTerminate { return } writer := &Writer{ id: id, hub: l.hub, dest: src, listener: l, } remoteAddr := &net.UDPAddr{ IP: src.Address.IP(), Port: int(src.Port), } localAddr := l.hub.Addr() conn = NewConnection(ConnMetadata{ LocalAddr: localAddr, RemoteAddr: remoteAddr, Conversation: conv, }, &KCPPacketWriter{ Header: l.header, Security: l.security, Writer: writer, }, writer, l.config) var netConn internet.Connection = conn if l.tlsConfig != nil { netConn = tls.Server(conn, l.tlsConfig) } l.addConn(netConn) l.sessions[id] = conn } conn.Input(segments) } func (l *Listener) Remove(id ConnectionID) { l.Lock() delete(l.sessions, id) l.Unlock() } // Close stops listening on the UDP address. Already Accepted connections are not closed. func (l *Listener) Close() error { l.hub.Close() l.Lock() defer l.Unlock() for _, conn := range l.sessions { go conn.Terminate() } return nil } func (l *Listener) ActiveConnections() int { l.Lock() defer l.Unlock() return len(l.sessions) } // Addr returns the listener's network address, The Addr returned is shared by all invocations of Addr, so do not modify it. func (l *Listener) Addr() net.Addr { return l.hub.Addr() } type Writer struct { id ConnectionID dest net.Destination hub *udp.Hub listener *Listener } func (w *Writer) Write(payload []byte) (int, error) { return w.hub.WriteTo(payload, w.dest) } func (w *Writer) Close() error { w.listener.Remove(w.id) return nil } func ListenKCP(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { return NewListener(ctx, address, port, streamSettings, addConn) } func init() { common.Must(internet.RegisterTransportListener(protocolName, ListenKCP)) } ================================================ FILE: transport/internet/kcp/output.go ================================================ package kcp import ( "io" "sync" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/retry" ) type SegmentWriter interface { Write(seg Segment) error Release() } type SimpleSegmentWriter struct { sync.Mutex buffer *buf.Buffer writer io.Writer closed bool } func NewSegmentWriter(writer io.Writer) SegmentWriter { return &SimpleSegmentWriter{ writer: writer, buffer: buf.New(), } } func (w *SimpleSegmentWriter) Write(seg Segment) error { w.Lock() defer w.Unlock() if w.closed { return io.ErrClosedPipe } w.buffer.Clear() rawBytes := w.buffer.Extend(seg.ByteSize()) seg.Serialize(rawBytes) _, err := w.writer.Write(w.buffer.Bytes()) return err } func (w *SimpleSegmentWriter) Release() { w.Lock() defer w.Unlock() w.buffer.Release() w.closed = true } type RetryableWriter struct { writer SegmentWriter } func NewRetryableWriter(writer SegmentWriter) SegmentWriter { return &RetryableWriter{ writer: writer, } } func (w *RetryableWriter) Write(seg Segment) error { return retry.Timed(5, 100).On(func() error { return w.writer.Write(seg) }) } func (w *RetryableWriter) Release() { w.writer.Release() } ================================================ FILE: transport/internet/kcp/receiving.go ================================================ package kcp import ( "sync" "github.com/v2fly/v2ray-core/v5/common/buf" ) type ReceivingWindow struct { cache map[uint32]*DataSegment } func NewReceivingWindow() *ReceivingWindow { return &ReceivingWindow{ cache: make(map[uint32]*DataSegment), } } func (w *ReceivingWindow) Set(id uint32, value *DataSegment) bool { _, f := w.cache[id] if f { return false } w.cache[id] = value return true } func (w *ReceivingWindow) Has(id uint32) bool { _, f := w.cache[id] return f } func (w *ReceivingWindow) Remove(id uint32) *DataSegment { v, f := w.cache[id] if !f { return nil } delete(w.cache, id) return v } type AckList struct { writer SegmentWriter timestamps []uint32 numbers []uint32 nextFlush []uint32 flushCandidates []uint32 dirty bool } func NewAckList(writer SegmentWriter) *AckList { return &AckList{ writer: writer, timestamps: make([]uint32, 0, 128), numbers: make([]uint32, 0, 128), nextFlush: make([]uint32, 0, 128), flushCandidates: make([]uint32, 0, 128), } } func (l *AckList) Add(number uint32, timestamp uint32) { l.timestamps = append(l.timestamps, timestamp) l.numbers = append(l.numbers, number) l.nextFlush = append(l.nextFlush, 0) l.dirty = true } func (l *AckList) Clear(una uint32) { count := 0 for i := 0; i < len(l.numbers); i++ { if l.numbers[i] < una { continue } if i != count { l.numbers[count] = l.numbers[i] l.timestamps[count] = l.timestamps[i] l.nextFlush[count] = l.nextFlush[i] } count++ } if count < len(l.numbers) { l.numbers = l.numbers[:count] l.timestamps = l.timestamps[:count] l.nextFlush = l.nextFlush[:count] l.dirty = true } } func (l *AckList) Flush(current uint32, rto uint32) { l.flushCandidates = l.flushCandidates[:0] seg := NewAckSegment() for i := 0; i < len(l.numbers); i++ { if l.nextFlush[i] > current { if len(l.flushCandidates) < cap(l.flushCandidates) { l.flushCandidates = append(l.flushCandidates, l.numbers[i]) } continue } seg.PutNumber(l.numbers[i]) seg.PutTimestamp(l.timestamps[i]) timeout := rto / 2 if timeout < 20 { timeout = 20 } l.nextFlush[i] = current + timeout if seg.IsFull() { l.writer.Write(seg) seg.Release() seg = NewAckSegment() l.dirty = false } } if l.dirty || !seg.IsEmpty() { for _, number := range l.flushCandidates { if seg.IsFull() { break } seg.PutNumber(number) } l.writer.Write(seg) l.dirty = false } seg.Release() } type ReceivingWorker struct { sync.RWMutex conn *Connection leftOver buf.MultiBuffer window *ReceivingWindow acklist *AckList nextNumber uint32 windowSize uint32 } func NewReceivingWorker(kcp *Connection) *ReceivingWorker { worker := &ReceivingWorker{ conn: kcp, window: NewReceivingWindow(), windowSize: kcp.Config.GetReceivingInFlightSize(), } worker.acklist = NewAckList(worker) return worker } func (w *ReceivingWorker) Release() { w.Lock() buf.ReleaseMulti(w.leftOver) w.leftOver = nil w.Unlock() } func (w *ReceivingWorker) ProcessSendingNext(number uint32) { w.Lock() defer w.Unlock() w.acklist.Clear(number) } func (w *ReceivingWorker) ProcessSegment(seg *DataSegment) { w.Lock() defer w.Unlock() number := seg.Number idx := number - w.nextNumber if idx >= w.windowSize { return } w.acklist.Clear(seg.SendingNext) w.acklist.Add(number, seg.Timestamp) if !w.window.Set(seg.Number, seg) { seg.Release() } } func (w *ReceivingWorker) ReadMultiBuffer() buf.MultiBuffer { if w.leftOver != nil { mb := w.leftOver w.leftOver = nil return mb } mb := make(buf.MultiBuffer, 0, 32) w.Lock() defer w.Unlock() for { seg := w.window.Remove(w.nextNumber) if seg == nil { break } w.nextNumber++ mb = append(mb, seg.Detach()) seg.Release() } return mb } func (w *ReceivingWorker) Read(b []byte) int { mb := w.ReadMultiBuffer() if mb.IsEmpty() { return 0 } mb, nBytes := buf.SplitBytes(mb, b) if !mb.IsEmpty() { w.leftOver = mb } return nBytes } func (w *ReceivingWorker) IsDataAvailable() bool { w.RLock() defer w.RUnlock() return w.window.Has(w.nextNumber) } func (w *ReceivingWorker) NextNumber() uint32 { w.RLock() defer w.RUnlock() return w.nextNumber } func (w *ReceivingWorker) Flush(current uint32) { w.Lock() defer w.Unlock() w.acklist.Flush(current, w.conn.roundTrip.Timeout()) } func (w *ReceivingWorker) Write(seg Segment) error { ackSeg := seg.(*AckSegment) ackSeg.Conv = w.conn.meta.Conversation ackSeg.ReceivingNext = w.nextNumber ackSeg.ReceivingWindow = w.nextNumber + w.windowSize ackSeg.Option = 0 if w.conn.State() == StateReadyToClose { ackSeg.Option = SegmentOptionClose } return w.conn.output.Write(ackSeg) } func (*ReceivingWorker) CloseRead() { } func (w *ReceivingWorker) UpdateNecessary() bool { w.RLock() defer w.RUnlock() return len(w.acklist.numbers) > 0 } ================================================ FILE: transport/internet/kcp/segment.go ================================================ package kcp import ( "encoding/binary" "github.com/v2fly/v2ray-core/v5/common/buf" ) // Command is a KCP command that indicate the purpose of a Segment. type Command byte const ( // CommandACK indicates an AckSegment. CommandACK Command = 0 // CommandData indicates a DataSegment. CommandData Command = 1 // CommandTerminate indicates that peer terminates the connection. CommandTerminate Command = 2 // CommandPing indicates a ping. CommandPing Command = 3 ) type SegmentOption byte const ( SegmentOptionClose SegmentOption = 1 ) type Segment interface { Release() Conversation() uint16 Command() Command ByteSize() int32 Serialize([]byte) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) } const ( DataSegmentOverhead = 18 ) type DataSegment struct { Conv uint16 Option SegmentOption Timestamp uint32 Number uint32 SendingNext uint32 payload *buf.Buffer timeout uint32 transmit uint32 } func NewDataSegment() *DataSegment { return new(DataSegment) } func (s *DataSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { s.Conv = conv s.Option = opt if len(buf) < 15 { return false, nil } s.Timestamp = binary.BigEndian.Uint32(buf) buf = buf[4:] s.Number = binary.BigEndian.Uint32(buf) buf = buf[4:] s.SendingNext = binary.BigEndian.Uint32(buf) buf = buf[4:] dataLen := int(binary.BigEndian.Uint16(buf)) buf = buf[2:] if len(buf) < dataLen { return false, nil } s.Data().Clear() s.Data().Write(buf[:dataLen]) buf = buf[dataLen:] return true, buf } func (s *DataSegment) Conversation() uint16 { return s.Conv } func (*DataSegment) Command() Command { return CommandData } func (s *DataSegment) Detach() *buf.Buffer { r := s.payload s.payload = nil return r } func (s *DataSegment) Data() *buf.Buffer { if s.payload == nil { s.payload = buf.New() } return s.payload } func (s *DataSegment) Serialize(b []byte) { binary.BigEndian.PutUint16(b, s.Conv) b[2] = byte(CommandData) b[3] = byte(s.Option) binary.BigEndian.PutUint32(b[4:], s.Timestamp) binary.BigEndian.PutUint32(b[8:], s.Number) binary.BigEndian.PutUint32(b[12:], s.SendingNext) binary.BigEndian.PutUint16(b[16:], uint16(s.payload.Len())) copy(b[18:], s.payload.Bytes()) } func (s *DataSegment) ByteSize() int32 { return 2 + 1 + 1 + 4 + 4 + 4 + 2 + s.payload.Len() } func (s *DataSegment) Release() { s.payload.Release() s.payload = nil } type AckSegment struct { Conv uint16 Option SegmentOption ReceivingWindow uint32 ReceivingNext uint32 Timestamp uint32 NumberList []uint32 } const ackNumberLimit = 128 func NewAckSegment() *AckSegment { return new(AckSegment) } func (s *AckSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { s.Conv = conv s.Option = opt if len(buf) < 13 { return false, nil } s.ReceivingWindow = binary.BigEndian.Uint32(buf) buf = buf[4:] s.ReceivingNext = binary.BigEndian.Uint32(buf) buf = buf[4:] s.Timestamp = binary.BigEndian.Uint32(buf) buf = buf[4:] count := int(buf[0]) buf = buf[1:] if len(buf) < count*4 { return false, nil } for i := 0; i < count; i++ { s.PutNumber(binary.BigEndian.Uint32(buf)) buf = buf[4:] } return true, buf } func (s *AckSegment) Conversation() uint16 { return s.Conv } func (*AckSegment) Command() Command { return CommandACK } func (s *AckSegment) PutTimestamp(timestamp uint32) { if timestamp-s.Timestamp < 0x7FFFFFFF { s.Timestamp = timestamp } } func (s *AckSegment) PutNumber(number uint32) { s.NumberList = append(s.NumberList, number) } func (s *AckSegment) IsFull() bool { return len(s.NumberList) == ackNumberLimit } func (s *AckSegment) IsEmpty() bool { return len(s.NumberList) == 0 } func (s *AckSegment) ByteSize() int32 { return 2 + 1 + 1 + 4 + 4 + 4 + 1 + int32(len(s.NumberList)*4) } func (s *AckSegment) Serialize(b []byte) { binary.BigEndian.PutUint16(b, s.Conv) b[2] = byte(CommandACK) b[3] = byte(s.Option) binary.BigEndian.PutUint32(b[4:], s.ReceivingWindow) binary.BigEndian.PutUint32(b[8:], s.ReceivingNext) binary.BigEndian.PutUint32(b[12:], s.Timestamp) b[16] = byte(len(s.NumberList)) n := 17 for _, number := range s.NumberList { binary.BigEndian.PutUint32(b[n:], number) n += 4 } } func (s *AckSegment) Release() {} type CmdOnlySegment struct { Conv uint16 Cmd Command Option SegmentOption SendingNext uint32 ReceivingNext uint32 PeerRTO uint32 } func NewCmdOnlySegment() *CmdOnlySegment { return new(CmdOnlySegment) } func (s *CmdOnlySegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { s.Conv = conv s.Cmd = cmd s.Option = opt if len(buf) < 12 { return false, nil } s.SendingNext = binary.BigEndian.Uint32(buf) buf = buf[4:] s.ReceivingNext = binary.BigEndian.Uint32(buf) buf = buf[4:] s.PeerRTO = binary.BigEndian.Uint32(buf) buf = buf[4:] return true, buf } func (s *CmdOnlySegment) Conversation() uint16 { return s.Conv } func (s *CmdOnlySegment) Command() Command { return s.Cmd } func (*CmdOnlySegment) ByteSize() int32 { return 2 + 1 + 1 + 4 + 4 + 4 } func (s *CmdOnlySegment) Serialize(b []byte) { binary.BigEndian.PutUint16(b, s.Conv) b[2] = byte(s.Cmd) b[3] = byte(s.Option) binary.BigEndian.PutUint32(b[4:], s.SendingNext) binary.BigEndian.PutUint32(b[8:], s.ReceivingNext) binary.BigEndian.PutUint32(b[12:], s.PeerRTO) } func (*CmdOnlySegment) Release() {} func ReadSegment(buf []byte) (Segment, []byte) { if len(buf) < 4 { return nil, nil } conv := binary.BigEndian.Uint16(buf) buf = buf[2:] cmd := Command(buf[0]) opt := SegmentOption(buf[1]) buf = buf[2:] var seg Segment switch cmd { case CommandData: seg = NewDataSegment() case CommandACK: seg = NewAckSegment() default: seg = NewCmdOnlySegment() } valid, extra := seg.parse(conv, cmd, opt, buf) if !valid { return nil, nil } return seg, extra } ================================================ FILE: transport/internet/kcp/segment_test.go ================================================ package kcp_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" . "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" ) func TestBadSegment(t *testing.T) { seg, buf := ReadSegment(nil) if seg != nil { t.Error("non-nil seg") } if len(buf) != 0 { t.Error("buf len: ", len(buf)) } } func TestDataSegment(t *testing.T) { seg := &DataSegment{ Conv: 1, Timestamp: 3, Number: 4, SendingNext: 5, } seg.Data().Write([]byte{'a', 'b', 'c', 'd'}) nBytes := seg.ByteSize() bytes := make([]byte, nBytes) seg.Serialize(bytes) iseg, _ := ReadSegment(bytes) seg2 := iseg.(*DataSegment) if r := cmp.Diff(seg2, seg, cmpopts.IgnoreUnexported(DataSegment{})); r != "" { t.Error(r) } if r := cmp.Diff(seg2.Data().Bytes(), seg.Data().Bytes()); r != "" { t.Error(r) } } func Test1ByteDataSegment(t *testing.T) { seg := &DataSegment{ Conv: 1, Timestamp: 3, Number: 4, SendingNext: 5, } seg.Data().WriteByte('a') nBytes := seg.ByteSize() bytes := make([]byte, nBytes) seg.Serialize(bytes) iseg, _ := ReadSegment(bytes) seg2 := iseg.(*DataSegment) if r := cmp.Diff(seg2, seg, cmpopts.IgnoreUnexported(DataSegment{})); r != "" { t.Error(r) } if r := cmp.Diff(seg2.Data().Bytes(), seg.Data().Bytes()); r != "" { t.Error(r) } } func TestACKSegment(t *testing.T) { seg := &AckSegment{ Conv: 1, ReceivingWindow: 2, ReceivingNext: 3, Timestamp: 10, NumberList: []uint32{1, 3, 5, 7, 9}, } nBytes := seg.ByteSize() bytes := make([]byte, nBytes) seg.Serialize(bytes) iseg, _ := ReadSegment(bytes) seg2 := iseg.(*AckSegment) if r := cmp.Diff(seg2, seg); r != "" { t.Error(r) } } func TestCmdSegment(t *testing.T) { seg := &CmdOnlySegment{ Conv: 1, Cmd: CommandPing, Option: SegmentOptionClose, SendingNext: 11, ReceivingNext: 13, PeerRTO: 15, } nBytes := seg.ByteSize() bytes := make([]byte, nBytes) seg.Serialize(bytes) iseg, _ := ReadSegment(bytes) seg2 := iseg.(*CmdOnlySegment) if r := cmp.Diff(seg2, seg); r != "" { t.Error(r) } } ================================================ FILE: transport/internet/kcp/sending.go ================================================ package kcp import ( "container/list" "sync" "github.com/v2fly/v2ray-core/v5/common/buf" ) type SendingWindow struct { cache *list.List totalInFlightSize uint32 writer SegmentWriter onPacketLoss func(uint32) } func NewSendingWindow(writer SegmentWriter, onPacketLoss func(uint32)) *SendingWindow { window := &SendingWindow{ cache: list.New(), writer: writer, onPacketLoss: onPacketLoss, } return window } func (sw *SendingWindow) Release() { if sw == nil { return } for sw.cache.Len() > 0 { seg := sw.cache.Front().Value.(*DataSegment) seg.Release() sw.cache.Remove(sw.cache.Front()) } } func (sw *SendingWindow) Len() uint32 { return uint32(sw.cache.Len()) } func (sw *SendingWindow) IsEmpty() bool { return sw.cache.Len() == 0 } func (sw *SendingWindow) Push(number uint32, b *buf.Buffer) { seg := NewDataSegment() seg.Number = number seg.payload = b sw.cache.PushBack(seg) } func (sw *SendingWindow) FirstNumber() uint32 { return sw.cache.Front().Value.(*DataSegment).Number } func (sw *SendingWindow) Clear(una uint32) { for !sw.IsEmpty() { seg := sw.cache.Front().Value.(*DataSegment) if seg.Number >= una { break } seg.Release() sw.cache.Remove(sw.cache.Front()) } } func (sw *SendingWindow) HandleFastAck(number uint32, rto uint32) { if sw.IsEmpty() { return } sw.Visit(func(seg *DataSegment) bool { if number == seg.Number || number-seg.Number > 0x7FFFFFFF { return false } if seg.transmit > 0 && seg.timeout > rto/3 { seg.timeout -= rto / 3 } return true }) } func (sw *SendingWindow) Visit(visitor func(seg *DataSegment) bool) { if sw.IsEmpty() { return } for e := sw.cache.Front(); e != nil; e = e.Next() { seg := e.Value.(*DataSegment) if !visitor(seg) { break } } } func (sw *SendingWindow) Flush(current uint32, rto uint32, maxInFlightSize uint32) { if sw.IsEmpty() { return } var lost uint32 var inFlightSize uint32 sw.Visit(func(segment *DataSegment) bool { if current-segment.timeout >= 0x7FFFFFFF { return true } if segment.transmit == 0 { // First time sw.totalInFlightSize++ } else { lost++ } segment.timeout = current + rto segment.Timestamp = current segment.transmit++ sw.writer.Write(segment) inFlightSize++ return inFlightSize < maxInFlightSize }) if sw.onPacketLoss != nil && inFlightSize > 0 && sw.totalInFlightSize != 0 { rate := lost * 100 / sw.totalInFlightSize sw.onPacketLoss(rate) } } func (sw *SendingWindow) Remove(number uint32) bool { if sw.IsEmpty() { return false } for e := sw.cache.Front(); e != nil; e = e.Next() { seg := e.Value.(*DataSegment) if seg.Number > number { return false } else if seg.Number == number { if sw.totalInFlightSize > 0 { sw.totalInFlightSize-- } seg.Release() sw.cache.Remove(e) return true } } return false } type SendingWorker struct { sync.RWMutex conn *Connection window *SendingWindow firstUnacknowledged uint32 nextNumber uint32 remoteNextNumber uint32 controlWindow uint32 fastResend uint32 windowSize uint32 firstUnacknowledgedUpdated bool closed bool } func NewSendingWorker(kcp *Connection) *SendingWorker { worker := &SendingWorker{ conn: kcp, fastResend: 2, remoteNextNumber: 32, controlWindow: kcp.Config.GetSendingInFlightSize(), windowSize: kcp.Config.GetSendingBufferSize(), } worker.window = NewSendingWindow(worker, worker.OnPacketLoss) return worker } func (w *SendingWorker) Release() { w.Lock() w.window.Release() w.closed = true w.Unlock() } func (w *SendingWorker) ProcessReceivingNext(nextNumber uint32) { w.Lock() defer w.Unlock() w.ProcessReceivingNextWithoutLock(nextNumber) } func (w *SendingWorker) ProcessReceivingNextWithoutLock(nextNumber uint32) { w.window.Clear(nextNumber) w.FindFirstUnacknowledged() } func (w *SendingWorker) FindFirstUnacknowledged() { first := w.firstUnacknowledged if !w.window.IsEmpty() { w.firstUnacknowledged = w.window.FirstNumber() } else { w.firstUnacknowledged = w.nextNumber } if first != w.firstUnacknowledged { w.firstUnacknowledgedUpdated = true } } func (w *SendingWorker) processAck(number uint32) bool { // number < v.firstUnacknowledged || number >= v.nextNumber if number-w.firstUnacknowledged > 0x7FFFFFFF || number-w.nextNumber < 0x7FFFFFFF { return false } removed := w.window.Remove(number) if removed { w.FindFirstUnacknowledged() } return removed } func (w *SendingWorker) ProcessSegment(current uint32, seg *AckSegment, rto uint32) { defer seg.Release() w.Lock() defer w.Unlock() if w.closed { return } if w.remoteNextNumber < seg.ReceivingWindow { w.remoteNextNumber = seg.ReceivingWindow } w.ProcessReceivingNextWithoutLock(seg.ReceivingNext) if seg.IsEmpty() { return } var maxack uint32 var maxackRemoved bool for _, number := range seg.NumberList { removed := w.processAck(number) if maxack < number { maxack = number maxackRemoved = removed } } if maxackRemoved { w.window.HandleFastAck(maxack, rto) if current-seg.Timestamp < 10000 { w.conn.roundTrip.Update(current-seg.Timestamp, current) } } } func (w *SendingWorker) Push(b *buf.Buffer) bool { w.Lock() defer w.Unlock() if w.closed { return false } if w.window.Len() > w.windowSize { return false } w.window.Push(w.nextNumber, b) w.nextNumber++ return true } func (w *SendingWorker) Write(seg Segment) error { dataSeg := seg.(*DataSegment) dataSeg.Conv = w.conn.meta.Conversation dataSeg.SendingNext = w.firstUnacknowledged dataSeg.Option = 0 if w.conn.State() == StateReadyToClose { dataSeg.Option = SegmentOptionClose } return w.conn.output.Write(dataSeg) } func (w *SendingWorker) OnPacketLoss(lossRate uint32) { if !w.conn.Config.Congestion || w.conn.roundTrip.Timeout() == 0 { return } if lossRate >= 15 { w.controlWindow = 3 * w.controlWindow / 4 } else if lossRate <= 5 { w.controlWindow += w.controlWindow / 4 } if w.controlWindow < 16 { w.controlWindow = 16 } if w.controlWindow > 2*w.conn.Config.GetSendingInFlightSize() { w.controlWindow = 2 * w.conn.Config.GetSendingInFlightSize() } } func (w *SendingWorker) Flush(current uint32) { w.Lock() if w.closed { w.Unlock() return } cwnd := w.conn.Config.GetSendingInFlightSize() if cwnd > w.remoteNextNumber-w.firstUnacknowledged { cwnd = w.remoteNextNumber - w.firstUnacknowledged } if w.conn.Config.Congestion && cwnd > w.controlWindow { cwnd = w.controlWindow } cwnd *= 20 // magic if !w.window.IsEmpty() { w.window.Flush(current, w.conn.roundTrip.Timeout(), cwnd) w.firstUnacknowledgedUpdated = false } updated := w.firstUnacknowledgedUpdated w.firstUnacknowledgedUpdated = false w.Unlock() if updated { w.conn.Ping(current, CommandPing) } } func (w *SendingWorker) CloseWrite() { w.Lock() defer w.Unlock() w.window.Clear(0xFFFFFFFF) } func (w *SendingWorker) IsEmpty() bool { w.RLock() defer w.RUnlock() return w.window.IsEmpty() } func (w *SendingWorker) UpdateNecessary() bool { return !w.IsEmpty() } func (w *SendingWorker) FirstUnacknowledged() uint32 { w.RLock() defer w.RUnlock() return w.firstUnacknowledged } ================================================ FILE: transport/internet/kcp/xor.go ================================================ //go:build !amd64 // +build !amd64 package kcp // xorfwd performs XOR forwards in words, x[i] ^= x[i-4], i from 0 to len func xorfwd(x []byte) { for i := 4; i < len(x); i++ { x[i] ^= x[i-4] } } // xorbkd performs XOR backwords in words, x[i] ^= x[i-4], i from len to 0 func xorbkd(x []byte) { for i := len(x) - 1; i >= 4; i-- { x[i] ^= x[i-4] } } ================================================ FILE: transport/internet/kcp/xor_amd64.go ================================================ package kcp //go:noescape func xorfwd(x []byte) //go:noescape func xorbkd(x []byte) ================================================ FILE: transport/internet/kcp/xor_amd64.s ================================================ #include "textflag.h" // func xorfwd(x []byte) TEXT ·xorfwd(SB),NOSPLIT,$0 MOVQ x+0(FP), SI // x[i] MOVQ x_len+8(FP), CX // x.len MOVQ x+0(FP), DI ADDQ $4, DI // x[i+4] SUBQ $4, CX xorfwdloop: MOVL (SI), AX XORL AX, (DI) ADDQ $4, SI ADDQ $4, DI SUBQ $4, CX CMPL CX, $0 JE xorfwddone JMP xorfwdloop xorfwddone: RET // func xorbkd(x []byte) TEXT ·xorbkd(SB),NOSPLIT,$0 MOVQ x+0(FP), SI MOVQ x_len+8(FP), CX // x.len MOVQ x+0(FP), DI ADDQ CX, SI // x[-8] SUBQ $8, SI ADDQ CX, DI // x[-4] SUBQ $4, DI SUBQ $4, CX xorbkdloop: MOVL (SI), AX XORL AX, (DI) SUBQ $4, SI SUBQ $4, DI SUBQ $4, CX CMPL CX, $0 JE xorbkddone JMP xorbkdloop xorbkddone: RET ================================================ FILE: transport/internet/memory_settings.go ================================================ package internet // MemoryStreamConfig is a parsed form of StreamConfig. This is used to reduce number of Protobuf parsing. type MemoryStreamConfig struct { ProtocolName string ProtocolSettings interface{} SecurityType string SecuritySettings interface{} SocketSettings *SocketConfig } // ToMemoryStreamConfig converts a StreamConfig to MemoryStreamConfig. It returns a default non-nil MemoryStreamConfig for nil input. func ToMemoryStreamConfig(s *StreamConfig) (*MemoryStreamConfig, error) { ets, err := s.GetEffectiveTransportSettings() if err != nil { return nil, err } mss := &MemoryStreamConfig{ ProtocolName: s.GetEffectiveProtocol(), ProtocolSettings: ets, } if s != nil { mss.SocketSettings = s.SocketSettings } if s != nil && s.HasSecuritySettings() { ess, err := s.GetEffectiveSecuritySettings() if err != nil { return nil, err } mss.SecurityType = s.SecurityType mss.SecuritySettings = ess } return mss, nil } ================================================ FILE: transport/internet/quic/config.go ================================================ package quic import ( "crypto/aes" "crypto/cipher" "crypto/sha256" "golang.org/x/crypto/chacha20poly1305" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func getAuth(config *Config) (cipher.AEAD, error) { security := config.Security.GetSecurityType() if security == protocol.SecurityType_NONE { return nil, nil } salted := []byte(config.Key + "v2ray-quic-salt") key := sha256.Sum256(salted) if security == protocol.SecurityType_AES128_GCM { block, err := aes.NewCipher(key[:16]) common.Must(err) return cipher.NewGCM(block) } if security == protocol.SecurityType_CHACHA20_POLY1305 { return chacha20poly1305.New(key[:]) } return nil, newError("unsupported security type") } func getHeader(config *Config) (internet.PacketHeader, error) { if config.Header == nil { return nil, nil } msg, err := serial.GetInstanceOf(config.Header) if err != nil { return nil, err } return internet.CreatePacketHeader(msg) } ================================================ FILE: transport/internet/quic/config.pb.go ================================================ package quic import ( protocol "github.com/v2fly/v2ray-core/v5/common/protocol" _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Security *protocol.SecurityConfig `protobuf:"bytes,2,opt,name=security,proto3" json:"security,omitempty"` Header *anypb.Any `protobuf:"bytes,3,opt,name=header,proto3" json:"header,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_quic_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_quic_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_quic_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetKey() string { if x != nil { return x.Key } return "" } func (x *Config) GetSecurity() *protocol.SecurityConfig { if x != nil { return x.Security } return nil } func (x *Config) GetHeader() *anypb.Any { if x != nil { return x.Header } return nil } var File_transport_internet_quic_config_proto protoreflect.FileDescriptor const file_transport_internet_quic_config_proto_rawDesc = "" + "\n" + "$transport/internet/quic/config.proto\x12\"v2ray.core.transport.internet.quic\x1a\x19google/protobuf/any.proto\x1a\x1dcommon/protocol/headers.proto\x1a common/protoext/extensions.proto\"\xa7\x01\n" + "\x06Config\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12F\n" + "\bsecurity\x18\x02 \x01(\v2*.v2ray.core.common.protocol.SecurityConfigR\bsecurity\x12,\n" + "\x06header\x18\x03 \x01(\v2\x14.google.protobuf.AnyR\x06header:\x15\x82\xb5\x18\x11\n" + "\ttransport\x12\x04quicB\x87\x01\n" + "&com.v2ray.core.transport.internet.quicP\x01Z6github.com/v2fly/v2ray-core/v5/transport/internet/quic\xaa\x02\"V2Ray.Core.Transport.Internet.Quicb\x06proto3" var ( file_transport_internet_quic_config_proto_rawDescOnce sync.Once file_transport_internet_quic_config_proto_rawDescData []byte ) func file_transport_internet_quic_config_proto_rawDescGZIP() []byte { file_transport_internet_quic_config_proto_rawDescOnce.Do(func() { file_transport_internet_quic_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_quic_config_proto_rawDesc), len(file_transport_internet_quic_config_proto_rawDesc))) }) return file_transport_internet_quic_config_proto_rawDescData } var file_transport_internet_quic_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_quic_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.quic.Config (*protocol.SecurityConfig)(nil), // 1: v2ray.core.common.protocol.SecurityConfig (*anypb.Any)(nil), // 2: google.protobuf.Any } var file_transport_internet_quic_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.transport.internet.quic.Config.security:type_name -> v2ray.core.common.protocol.SecurityConfig 2, // 1: v2ray.core.transport.internet.quic.Config.header:type_name -> google.protobuf.Any 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_transport_internet_quic_config_proto_init() } func file_transport_internet_quic_config_proto_init() { if File_transport_internet_quic_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_quic_config_proto_rawDesc), len(file_transport_internet_quic_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_quic_config_proto_goTypes, DependencyIndexes: file_transport_internet_quic_config_proto_depIdxs, MessageInfos: file_transport_internet_quic_config_proto_msgTypes, }.Build() File_transport_internet_quic_config_proto = out.File file_transport_internet_quic_config_proto_goTypes = nil file_transport_internet_quic_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/quic/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.quic; option csharp_namespace = "V2Ray.Core.Transport.Internet.Quic"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/quic"; option java_package = "com.v2ray.core.transport.internet.quic"; option java_multiple_files = true; import "google/protobuf/any.proto"; import "common/protocol/headers.proto"; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "quic"; string key = 1; v2ray.core.common.protocol.SecurityConfig security = 2; google.protobuf.Any header = 3; } ================================================ FILE: transport/internet/quic/conn.go ================================================ package quic import ( "crypto/cipher" "crypto/rand" "errors" "syscall" "time" "github.com/quic-go/quic-go" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type sysConn struct { conn *net.UDPConn header internet.PacketHeader auth cipher.AEAD } func wrapSysConn(rawConn *net.UDPConn, config *Config) (*sysConn, error) { header, err := getHeader(config) if err != nil { return nil, err } auth, err := getAuth(config) if err != nil { return nil, err } return &sysConn{ conn: rawConn, header: header, auth: auth, }, nil } var errInvalidPacket = errors.New("invalid packet") func (c *sysConn) readFromInternal(p []byte) (int, net.Addr, error) { buffer := getBuffer() defer putBuffer(buffer) nBytes, addr, err := c.conn.ReadFrom(buffer) if err != nil { return 0, nil, err } payload := buffer[:nBytes] if c.header != nil { if len(payload) <= int(c.header.Size()) { return 0, nil, errInvalidPacket } payload = payload[c.header.Size():] } if c.auth == nil { n := copy(p, payload) return n, addr, nil } if len(payload) <= c.auth.NonceSize() { return 0, nil, errInvalidPacket } nonce := payload[:c.auth.NonceSize()] payload = payload[c.auth.NonceSize():] p, err = c.auth.Open(p[:0], nonce, payload, nil) if err != nil { return 0, nil, errInvalidPacket } return len(p), addr, nil } func (c *sysConn) ReadFrom(p []byte) (int, net.Addr, error) { if c.header == nil && c.auth == nil { return c.conn.ReadFrom(p) } for { n, addr, err := c.readFromInternal(p) if err != nil && err != errInvalidPacket { return 0, nil, err } if err == nil { return n, addr, nil } } } func (c *sysConn) WriteTo(p []byte, addr net.Addr) (int, error) { if c.header == nil && c.auth == nil { return c.conn.WriteTo(p, addr) } buffer := getBuffer() defer putBuffer(buffer) payload := buffer n := 0 if c.header != nil { c.header.Serialize(payload) n = int(c.header.Size()) } if c.auth == nil { nBytes := copy(payload[n:], p) n += nBytes } else { nounce := payload[n : n+c.auth.NonceSize()] common.Must2(rand.Read(nounce)) n += c.auth.NonceSize() pp := c.auth.Seal(payload[:n], nounce, p, nil) n = len(pp) } return c.conn.WriteTo(payload[:n], addr) } func (c *sysConn) Close() error { return c.conn.Close() } func (c *sysConn) LocalAddr() net.Addr { return c.conn.LocalAddr() } func (c *sysConn) SetReadBuffer(bytes int) error { return c.conn.SetReadBuffer(bytes) } func (c *sysConn) SetWriteBuffer(bytes int) error { return c.conn.SetWriteBuffer(bytes) } func (c *sysConn) SetDeadline(t time.Time) error { return c.conn.SetDeadline(t) } func (c *sysConn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) } func (c *sysConn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) } func (c *sysConn) SyscallConn() (syscall.RawConn, error) { return c.conn.SyscallConn() } type interConn struct { stream *quic.Stream local net.Addr remote net.Addr } func (c *interConn) Read(b []byte) (int, error) { return c.stream.Read(b) } func (c *interConn) WriteMultiBuffer(mb buf.MultiBuffer) error { mb = buf.Compact(mb) mb, err := buf.WriteMultiBuffer(c, mb) buf.ReleaseMulti(mb) return err } func (c *interConn) Write(b []byte) (int, error) { return c.stream.Write(b) } func (c *interConn) Close() error { return c.stream.Close() } func (c *interConn) LocalAddr() net.Addr { return c.local } func (c *interConn) RemoteAddr() net.Addr { return c.remote } func (c *interConn) SetDeadline(t time.Time) error { return c.stream.SetDeadline(t) } func (c *interConn) SetReadDeadline(t time.Time) error { return c.stream.SetReadDeadline(t) } func (c *interConn) SetWriteDeadline(t time.Time) error { return c.stream.SetWriteDeadline(t) } ================================================ FILE: transport/internet/quic/dialer.go ================================================ package quic import ( "context" "sync" "time" "github.com/quic-go/quic-go" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) type connectionContext struct { rawConn *sysConn conn *quic.Conn } var errConnectionClosed = newError("connection closed") func (c *connectionContext) openStream(destAddr net.Addr) (*interConn, error) { if !isActive(c.conn) { return nil, errConnectionClosed } stream, err := c.conn.OpenStream() if err != nil { return nil, err } conn := &interConn{ stream: stream, local: c.conn.LocalAddr(), remote: destAddr, } return conn, nil } type clientConnections struct { access sync.Mutex conns map[net.Destination][]*connectionContext cleanup *task.Periodic } func isActive(s *quic.Conn) bool { select { case <-s.Context().Done(): return false default: return true } } func removeInactiveConnections(conns []*connectionContext) []*connectionContext { activeConnections := make([]*connectionContext, 0, len(conns)) for _, s := range conns { if isActive(s.conn) { activeConnections = append(activeConnections, s) continue } if err := s.conn.CloseWithError(0, ""); err != nil { newError("failed to close connection").Base(err).WriteToLog() } if err := s.rawConn.Close(); err != nil { newError("failed to close raw connection").Base(err).WriteToLog() } } if len(activeConnections) < len(conns) { return activeConnections } return conns } func openStream(conns []*connectionContext, destAddr net.Addr) *interConn { for _, s := range conns { if !isActive(s.conn) { continue } conn, err := s.openStream(destAddr) if err != nil { continue } return conn } return nil } func (s *clientConnections) cleanConnections() error { s.access.Lock() defer s.access.Unlock() if len(s.conns) == 0 { return nil } newConnMap := make(map[net.Destination][]*connectionContext) for dest, conns := range s.conns { conns = removeInactiveConnections(conns) if len(conns) > 0 { newConnMap[dest] = conns } } s.conns = newConnMap return nil } func (s *clientConnections) openConnection(destAddr net.Addr, config *Config, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (internet.Connection, error) { s.access.Lock() defer s.access.Unlock() if s.conns == nil { s.conns = make(map[net.Destination][]*connectionContext) } dest := net.DestinationFromAddr(destAddr) var conns []*connectionContext if s, found := s.conns[dest]; found { conns = s } { conn := openStream(conns, destAddr) if conn != nil { return conn, nil } } conns = removeInactiveConnections(conns) newError("dialing QUIC to ", dest).WriteToLog() rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{ IP: []byte{0, 0, 0, 0}, Port: 0, }, sockopt) if err != nil { return nil, err } quicConfig := &quic.Config{ HandshakeIdleTimeout: time.Second * 8, MaxIdleTimeout: time.Second * 30, KeepAlivePeriod: time.Second * 15, } sysConn, err := wrapSysConn(rawConn.(*net.UDPConn), config) if err != nil { rawConn.Close() return nil, err } tr := quic.Transport{ Conn: sysConn, ConnectionIDLength: 12, } conn, err := tr.Dial(context.Background(), destAddr, tlsConfig.GetTLSConfig(tls.WithDestination(dest)), quicConfig) if err != nil { sysConn.Close() return nil, err } context := &connectionContext{ conn: conn, rawConn: sysConn, } s.conns[dest] = append(conns, context) return context.openStream(destAddr) } var client clientConnections func init() { client.conns = make(map[net.Destination][]*connectionContext) client.cleanup = &task.Periodic{ Interval: time.Minute, Execute: client.cleanConnections, } common.Must(client.cleanup.Start()) } func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { tlsConfig := tls.ConfigFromStreamSettings(streamSettings) if tlsConfig == nil { tlsConfig = &tls.Config{ ServerName: internalDomain, AllowInsecure: true, } } var destAddr *net.UDPAddr if dest.Address.Family().IsIP() { destAddr = &net.UDPAddr{ IP: dest.Address.IP(), Port: int(dest.Port), } } else { addr, err := net.ResolveUDPAddr("udp", dest.NetAddr()) if err != nil { return nil, err } destAddr = addr } config := streamSettings.ProtocolSettings.(*Config) return client.openConnection(destAddr, config, tlsConfig, streamSettings.SocketSettings) } func init() { common.Must(internet.RegisterTransportDialer(protocolName, Dial)) } ================================================ FILE: transport/internet/quic/errors.generated.go ================================================ package quic import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/quic/hub.go ================================================ package quic import ( "context" "time" "github.com/quic-go/quic-go" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) // Listener is an internet.Listener that listens for TCP connections. type Listener struct { rawConn *sysConn listener *quic.Listener done *done.Instance addConn internet.ConnHandler } func (l *Listener) acceptStreams(conn *quic.Conn) { for { stream, err := conn.AcceptStream(context.Background()) if err != nil { newError("failed to accept stream").Base(err).WriteToLog() select { case <-conn.Context().Done(): return case <-l.done.Wait(): if err := conn.CloseWithError(0, ""); err != nil { newError("failed to close connection").Base(err).WriteToLog() } return default: time.Sleep(time.Second) continue } } conn := &interConn{ stream: stream, local: conn.LocalAddr(), remote: conn.RemoteAddr(), } l.addConn(conn) } } func (l *Listener) keepAccepting() { for { conn, err := l.listener.Accept(context.Background()) if err != nil { newError("failed to accept QUIC connections").Base(err).WriteToLog() if l.done.Done() { break } time.Sleep(time.Second) continue } go l.acceptStreams(conn) } } // Addr implements internet.Listener.Addr. func (l *Listener) Addr() net.Addr { return l.listener.Addr() } // Close implements internet.Listener.Close. func (l *Listener) Close() error { l.done.Close() l.listener.Close() l.rawConn.Close() return nil } // Listen creates a new Listener based on configurations. func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { if address.Family().IsDomain() { return nil, newError("domain address is not allows for listening quic") } tlsConfig := tls.ConfigFromStreamSettings(streamSettings) if tlsConfig == nil { tlsConfig = &tls.Config{ Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.DNSNames(internalDomain), cert.CommonName(internalDomain)))}, } } config := streamSettings.ProtocolSettings.(*Config) rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{ IP: address.IP(), Port: int(port), }, streamSettings.SocketSettings) if err != nil { return nil, err } quicConfig := &quic.Config{ HandshakeIdleTimeout: time.Second * 8, MaxIdleTimeout: time.Second * 45, MaxIncomingStreams: 32, MaxIncomingUniStreams: -1, KeepAlivePeriod: time.Second * 15, } conn, err := wrapSysConn(rawConn.(*net.UDPConn), config) if err != nil { conn.Close() return nil, err } tr := quic.Transport{ Conn: conn, ConnectionIDLength: 12, } qListener, err := tr.Listen(tlsConfig.GetTLSConfig(), quicConfig) if err != nil { conn.Close() return nil, err } listener := &Listener{ done: done.New(), rawConn: conn, listener: qListener, addConn: handler, } go listener.keepAccepting() return listener, nil } func init() { common.Must(internet.RegisterTransportListener(protocolName, Listen)) } ================================================ FILE: transport/internet/quic/pool.go ================================================ package quic import ( "sync" "github.com/v2fly/v2ray-core/v5/common/bytespool" ) var pool *sync.Pool func init() { pool = bytespool.GetPool(2048) } func getBuffer() []byte { return pool.Get().([]byte) } func putBuffer(p []byte) { pool.Put(p) // nolint: staticcheck } ================================================ FILE: transport/internet/quic/quic.go ================================================ package quic import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen // Here is some modification needs to be done before update quic vendor. // * use bytespool in buffer_pool.go // * set MaxReceivePacketSize to 1452 - 32 (16 bytes auth, 16 bytes head) // // const ( protocolName = "quic" internalDomain = "quic.internal.v2fly.org" ) func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/quic/quic_test.go ================================================ package quic_test import ( "context" "crypto/rand" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/testing/servers/udp" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/headers/wireguard" "github.com/v2fly/v2ray-core/v5/transport/internet/quic" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func TestQuicConnection(t *testing.T) { port := udp.PickPort() listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ ProtocolName: "quic", ProtocolSettings: &quic.Config{}, SecurityType: "tls", SecuritySettings: &tls.Config{ Certificate: []*tls.Certificate{ tls.ParseCertificate( cert.MustGenerate(nil, cert.DNSNames("www.v2fly.org"), ), ), }, }, }, func(conn internet.Connection) { go func() { defer conn.Close() b := buf.New() defer b.Release() for { b.Clear() if _, err := b.ReadFrom(conn); err != nil { return } common.Must2(conn.Write(b.Bytes())) } }() }) common.Must(err) defer listener.Close() time.Sleep(time.Second) dctx := context.Background() conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ ProtocolName: "quic", ProtocolSettings: &quic.Config{}, SecurityType: "tls", SecuritySettings: &tls.Config{ ServerName: "www.v2fly.org", AllowInsecure: true, }, }) common.Must(err) defer conn.Close() const N = 1024 b1 := make([]byte, N) common.Must2(rand.Read(b1)) b2 := buf.New() common.Must2(conn.Write(b1)) b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } common.Must2(conn.Write(b1)) b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } } func TestQuicConnectionWithoutTLS(t *testing.T) { port := udp.PickPort() listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ ProtocolName: "quic", ProtocolSettings: &quic.Config{}, }, func(conn internet.Connection) { go func() { defer conn.Close() b := buf.New() defer b.Release() for { b.Clear() if _, err := b.ReadFrom(conn); err != nil { return } common.Must2(conn.Write(b.Bytes())) } }() }) common.Must(err) defer listener.Close() time.Sleep(time.Second) dctx := context.Background() conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ ProtocolName: "quic", ProtocolSettings: &quic.Config{}, }) common.Must(err) defer conn.Close() const N = 1024 b1 := make([]byte, N) common.Must2(rand.Read(b1)) b2 := buf.New() common.Must2(conn.Write(b1)) b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } common.Must2(conn.Write(b1)) b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } } func TestQuicConnectionAuthHeader(t *testing.T) { port := udp.PickPort() listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{ ProtocolName: "quic", ProtocolSettings: &quic.Config{ Header: serial.ToTypedMessage(&wireguard.WireguardConfig{}), Key: "abcd", Security: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }, }, func(conn internet.Connection) { go func() { defer conn.Close() b := buf.New() defer b.Release() for { b.Clear() if _, err := b.ReadFrom(conn); err != nil { return } common.Must2(conn.Write(b.Bytes())) } }() }) common.Must(err) defer listener.Close() time.Sleep(time.Second) dctx := context.Background() conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{ ProtocolName: "quic", ProtocolSettings: &quic.Config{ Header: serial.ToTypedMessage(&wireguard.WireguardConfig{}), Key: "abcd", Security: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }, }) common.Must(err) defer conn.Close() const N = 1024 b1 := make([]byte, N) common.Must2(rand.Read(b1)) b2 := buf.New() common.Must2(conn.Write(b1)) b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } common.Must2(conn.Write(b1)) b2.Clear() common.Must2(b2.ReadFullFrom(conn, N)) if r := cmp.Diff(b2.Bytes(), b1); r != "" { t.Error(r) } } ================================================ FILE: transport/internet/request/assembler/packetconn/errors.generated.go ================================================ package packetconn import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/request/assembler/packetconn/packetConn.pb.go ================================================ package packetconn import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` UnderlyingTransportSetting *anypb.Any `protobuf:"bytes,1,opt,name=underlying_transport_setting,json=underlyingTransportSetting,proto3" json:"underlying_transport_setting,omitempty"` UnderlyingTransportName string `protobuf:"bytes,2,opt,name=underlying_transport_name,json=underlyingTransportName,proto3" json:"underlying_transport_name,omitempty"` MaxWriteDelay int32 `protobuf:"varint,3,opt,name=max_write_delay,json=maxWriteDelay,proto3" json:"max_write_delay,omitempty"` MaxRequestSize int32 `protobuf:"varint,4,opt,name=max_request_size,json=maxRequestSize,proto3" json:"max_request_size,omitempty"` PollingIntervalInitial int32 `protobuf:"varint,5,opt,name=polling_interval_initial,json=pollingIntervalInitial,proto3" json:"polling_interval_initial,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP(), []int{0} } func (x *ClientConfig) GetUnderlyingTransportSetting() *anypb.Any { if x != nil { return x.UnderlyingTransportSetting } return nil } func (x *ClientConfig) GetUnderlyingTransportName() string { if x != nil { return x.UnderlyingTransportName } return "" } func (x *ClientConfig) GetMaxWriteDelay() int32 { if x != nil { return x.MaxWriteDelay } return 0 } func (x *ClientConfig) GetMaxRequestSize() int32 { if x != nil { return x.MaxRequestSize } return 0 } func (x *ClientConfig) GetPollingIntervalInitial() int32 { if x != nil { return x.PollingIntervalInitial } return 0 } type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` UnderlyingTransportSetting *anypb.Any `protobuf:"bytes,1,opt,name=underlying_transport_setting,json=underlyingTransportSetting,proto3" json:"underlying_transport_setting,omitempty"` UnderlyingTransportName string `protobuf:"bytes,2,opt,name=underlying_transport_name,json=underlyingTransportName,proto3" json:"underlying_transport_name,omitempty"` MaxWriteSize int32 `protobuf:"varint,3,opt,name=max_write_size,json=maxWriteSize,proto3" json:"max_write_size,omitempty"` MaxWriteDurationMs int32 `protobuf:"varint,4,opt,name=max_write_duration_ms,json=maxWriteDurationMs,proto3" json:"max_write_duration_ms,omitempty"` MaxSimultaneousWriteConnection int32 `protobuf:"varint,5,opt,name=max_simultaneous_write_connection,json=maxSimultaneousWriteConnection,proto3" json:"max_simultaneous_write_connection,omitempty"` PacketWritingBuffer int32 `protobuf:"varint,6,opt,name=packet_writing_buffer,json=packetWritingBuffer,proto3" json:"packet_writing_buffer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP(), []int{1} } func (x *ServerConfig) GetUnderlyingTransportSetting() *anypb.Any { if x != nil { return x.UnderlyingTransportSetting } return nil } func (x *ServerConfig) GetUnderlyingTransportName() string { if x != nil { return x.UnderlyingTransportName } return "" } func (x *ServerConfig) GetMaxWriteSize() int32 { if x != nil { return x.MaxWriteSize } return 0 } func (x *ServerConfig) GetMaxWriteDurationMs() int32 { if x != nil { return x.MaxWriteDurationMs } return 0 } func (x *ServerConfig) GetMaxSimultaneousWriteConnection() int32 { if x != nil { return x.MaxSimultaneousWriteConnection } return 0 } func (x *ServerConfig) GetPacketWritingBuffer() int32 { if x != nil { return x.PacketWritingBuffer } return 0 } var File_transport_internet_request_assembler_packetconn_packetConn_proto protoreflect.FileDescriptor const file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc = "" + "\n" + "@transport/internet/request/assembler/packetconn/packetConn.proto\x12:v2ray.core.transport.internet.request.assembler.packetconn\x1a common/protoext/extensions.proto\x1a\x19google/protobuf/any.proto\"\xe4\x02\n" + "\fClientConfig\x12V\n" + "\x1cunderlying_transport_setting\x18\x01 \x01(\v2\x14.google.protobuf.AnyR\x1aunderlyingTransportSetting\x12:\n" + "\x19underlying_transport_name\x18\x02 \x01(\tR\x17underlyingTransportName\x12&\n" + "\x0fmax_write_delay\x18\x03 \x01(\x05R\rmaxWriteDelay\x12(\n" + "\x10max_request_size\x18\x04 \x01(\x05R\x0emaxRequestSize\x128\n" + "\x18polling_interval_initial\x18\x05 \x01(\x05R\x16pollingIntervalInitial:4\x82\xb5\x180\n" + "\"transport.request.assembler.client\x12\n" + "packetconn\"\xb0\x03\n" + "\fServerConfig\x12V\n" + "\x1cunderlying_transport_setting\x18\x01 \x01(\v2\x14.google.protobuf.AnyR\x1aunderlyingTransportSetting\x12:\n" + "\x19underlying_transport_name\x18\x02 \x01(\tR\x17underlyingTransportName\x12$\n" + "\x0emax_write_size\x18\x03 \x01(\x05R\fmaxWriteSize\x121\n" + "\x15max_write_duration_ms\x18\x04 \x01(\x05R\x12maxWriteDurationMs\x12I\n" + "!max_simultaneous_write_connection\x18\x05 \x01(\x05R\x1emaxSimultaneousWriteConnection\x122\n" + "\x15packet_writing_buffer\x18\x06 \x01(\x05R\x13packetWritingBuffer:4\x82\xb5\x180\n" + "\"transport.request.assembler.server\x12\n" + "packetconnB\xcf\x01\n" + ">com.v2ray.core.transport.internet.request.assembler.packetconnP\x01ZNgithub.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/packetconn\xaa\x02:V2Ray.Core.Transport.Internet.Request.Assembler.Packetconnb\x06proto3" var ( file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescOnce sync.Once file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData []byte ) func file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP() []byte { file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescOnce.Do(func() { file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc), len(file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc))) }) return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData } var file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes = []any{ (*ClientConfig)(nil), // 0: v2ray.core.transport.internet.request.assembler.packetconn.ClientConfig (*ServerConfig)(nil), // 1: v2ray.core.transport.internet.request.assembler.packetconn.ServerConfig (*anypb.Any)(nil), // 2: google.protobuf.Any } var file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs = []int32{ 2, // 0: v2ray.core.transport.internet.request.assembler.packetconn.ClientConfig.underlying_transport_setting:type_name -> google.protobuf.Any 2, // 1: v2ray.core.transport.internet.request.assembler.packetconn.ServerConfig.underlying_transport_setting:type_name -> google.protobuf.Any 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_transport_internet_request_assembler_packetconn_packetConn_proto_init() } func file_transport_internet_request_assembler_packetconn_packetConn_proto_init() { if File_transport_internet_request_assembler_packetconn_packetConn_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc), len(file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes, DependencyIndexes: file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs, MessageInfos: file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes, }.Build() File_transport_internet_request_assembler_packetconn_packetConn_proto = out.File file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes = nil file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs = nil } ================================================ FILE: transport/internet/request/assembler/packetconn/packetConn.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.request.assembler.packetconn; option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.Assembler.Packetconn"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/packetconn"; option java_package = "com.v2ray.core.transport.internet.request.assembler.packetconn"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "google/protobuf/any.proto"; message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "transport.request.assembler.client"; option (v2ray.core.common.protoext.message_opt).short_name = "packetconn"; google.protobuf.Any underlying_transport_setting = 1; string underlying_transport_name = 2; int32 max_write_delay = 3; int32 max_request_size = 4; int32 polling_interval_initial = 5; } message ServerConfig { option (v2ray.core.common.protoext.message_opt).type = "transport.request.assembler.server"; option (v2ray.core.common.protoext.message_opt).short_name = "packetconn"; google.protobuf.Any underlying_transport_setting = 1; string underlying_transport_name = 2; int32 max_write_size = 3; int32 max_write_duration_ms = 4; int32 max_simultaneous_write_connection = 5; int32 packet_writing_buffer = 6; } ================================================ FILE: transport/internet/request/assembler/packetconn/packetbundle.go ================================================ package packetconn import ( "encoding/binary" "io" ) func NewPacketBundle() PacketBundle { return &packetBundle{} } type packetBundle struct{} func (p *packetBundle) Overhead() int { return 2 } func (p *packetBundle) WriteToBundle(b []byte, writer io.Writer) (err error) { err = binary.Write(writer, binary.BigEndian, uint16(len(b))) if err != nil { return } _, err = writer.Write(b) return } func (p *packetBundle) ReadFromBundle(writer io.Reader) (b []byte, err error) { var length uint16 err = binary.Read(writer, binary.BigEndian, &length) if err != nil { return } b = make([]byte, length) n, err := io.ReadFull(writer, b) if err != nil { return } if n != int(length) { return nil, io.ErrUnexpectedEOF } return } type PacketBundle interface { Overhead() int WriteToBundle(b []byte, writer io.Writer) (err error) ReadFromBundle(writer io.Reader) (b []byte, err error) } ================================================ FILE: transport/internet/request/assembler/packetconn/packetconn.go ================================================ package packetconn //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/request/assembler/packetconn/req2packet.go ================================================ package packetconn import ( "bytes" "context" "crypto/rand" "io" "sync" "time" "github.com/golang-collections/go-datastructures/queue" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) func newRequestToPacketConnClient(ctx context.Context, config *ClientConfig) (*requestToPacketConnClient, error) { //nolint: unparam return &requestToPacketConnClient{ctx: ctx, config: config}, nil } type requestToPacketConnClient struct { assembly request.TransportClientAssembly ctx context.Context config *ClientConfig } func (r *requestToPacketConnClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) { r.assembly = assembly } func (r *requestToPacketConnClient) Dial() (io.ReadWriteCloser, error) { sessionID := make([]byte, 16) _, err := rand.Read(sessionID) if err != nil { return nil, err } ctxWithCancel, cancel := context.WithCancel(r.ctx) clientSess := &requestToPacketConnClientSession{ sessionID: sessionID, currentPollingInterval: int(r.config.PollingIntervalInitial), maxRequestSize: int(r.config.MaxRequestSize), maxWriteDelay: int(r.config.MaxWriteDelay), assembly: r.assembly, writerChan: make(chan []byte, 256), readerChan: make(chan []byte, 256), ctx: ctxWithCancel, finish: cancel, } go clientSess.keepRunning() return clientSess, nil } type requestToPacketConnClientSession struct { sessionID []byte currentPollingInterval int maxRequestSize int maxWriteDelay int assembly request.TransportClientAssembly writerChan chan []byte readerChan chan []byte ctx context.Context finish func() nextWrite []byte } func (r *requestToPacketConnClientSession) keepRunning() { for r.ctx.Err() == nil { r.runOnce() } } func (r *requestToPacketConnClientSession) runOnce() { requestBody := bytes.NewBuffer(nil) waitTimer := time.NewTimer(time.Duration(r.currentPollingInterval) * time.Millisecond) var seenPacket bool packetBundler := NewPacketBundle() copyFromChan: for { select { case <-r.ctx.Done(): return case <-waitTimer.C: break copyFromChan case packet := <-r.writerChan: if !seenPacket { seenPacket = true waitTimer.Stop() waitTimer.Reset(time.Duration(r.maxWriteDelay) * time.Millisecond) } sizeOffset := packetBundler.Overhead() + len(packet) if requestBody.Len()+sizeOffset > r.maxRequestSize { r.nextWrite = packet break copyFromChan } err := packetBundler.WriteToBundle(packet, requestBody) if err != nil { newError("failed to write to bundle").Base(err).WriteToLog() } } } waitTimer.Stop() go func() { reader, writer := io.Pipe() defer writer.Close() streamingRespOpt := &pipedStreamingRespOption{writer} go func() { for { if packet, err := packetBundler.ReadFromBundle(reader); err == nil { r.readerChan <- packet } else { return } } }() resp, err := r.assembly.Tripper().RoundTrip(r.ctx, request.Request{Data: requestBody.Bytes(), ConnectionTag: r.sessionID}, streamingRespOpt) if err != nil { newError("failed to roundtrip").Base(err).WriteToLog() if r.ctx.Err() != nil { return } } if len(resp.Data) != 0 { respReader := bytes.NewReader(resp.Data) for respReader.Len() != 0 { packet, err := packetBundler.ReadFromBundle(respReader) if err != nil { newError("failed to read from bundle").Base(err).WriteToLog() if r.ctx.Err() != nil { return } } r.readerChan <- packet } } }() } type pipedStreamingRespOption struct { writer *io.PipeWriter } func (p *pipedStreamingRespOption) RoundTripperOption() { } func (p *pipedStreamingRespOption) GetResponseWriter() io.Writer { return p.writer } func (r *requestToPacketConnClientSession) Write(p []byte) (n int, err error) { buf := make([]byte, len(p)) copy(buf, p) select { case <-r.ctx.Done(): return 0, r.ctx.Err() case r.writerChan <- buf: return len(p), nil } } func (r *requestToPacketConnClientSession) Read(p []byte) (n int, err error) { select { case <-r.ctx.Done(): return 0, r.ctx.Err() case buf := <-r.readerChan: copy(p, buf) return len(buf), nil } } func (r *requestToPacketConnClientSession) Close() error { r.finish() return nil } func newRequestToPacketConnServer(ctx context.Context, config *ServerConfig) *requestToPacketConnServer { return &requestToPacketConnServer{ sessionMap: sync.Map{}, ctx: ctx, config: config, } } type requestToPacketConnServer struct { packetSessionReceiver request.SessionReceiver sessionMap sync.Map ctx context.Context config *ServerConfig } func (r *requestToPacketConnServer) onSessionReceiverReady(sessrecv request.SessionReceiver) { r.packetSessionReceiver = sessrecv } func (r *requestToPacketConnServer) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption, ) (resp request.Response, err error) { SessionID := req.ConnectionTag if SessionID == nil { return request.Response{}, newError("nil session id") } sessionID := string(SessionID) var session *requestToPacketConnServerSession sessionAny, found := r.sessionMap.Load(sessionID) if found { var ok bool session, ok = sessionAny.(*requestToPacketConnServerSession) if !ok { return request.Response{}, newError("failed to cast session") } } if !found { ctxWithFinish, finish := context.WithCancel(ctx) session = &requestToPacketConnServerSession{ SessionID: SessionID, writingConnectionQueue: queue.New(64), writerChan: make(chan []byte, int(r.config.PacketWritingBuffer)), readerChan: make(chan []byte, 256), ctx: ctxWithFinish, finish: finish, server: r, maxWriteSize: int(r.config.MaxWriteSize), maxWriteDuration: int(r.config.MaxWriteDurationMs), maxSimultaneousWriteConnection: int(r.config.MaxSimultaneousWriteConnection), } _, loaded := r.sessionMap.LoadOrStore(sessionID, session) if !loaded { err = r.packetSessionReceiver.OnNewSession(ctx, session) } } if err != nil { return request.Response{}, err } return session.OnRoundTrip(ctx, req, opts...) } func (r *requestToPacketConnServer) removeSessionID(sessionID []byte) { r.sessionMap.Delete(string(sessionID)) } type requestToPacketConnServerSession struct { SessionID []byte writingConnectionQueue *queue.Queue writerChan chan []byte readerChan chan []byte ctx context.Context finish func() server *requestToPacketConnServer maxWriteSize int maxWriteDuration int maxSimultaneousWriteConnection int } func (r *requestToPacketConnServerSession) Read(p []byte) (n int, err error) { select { case <-r.ctx.Done(): return 0, r.ctx.Err() case buf := <-r.readerChan: copy(p, buf) return len(buf), nil } } var debugStats struct { packetWritten int packetDropped int } /* var _ = func() bool { go func() { for { time.Sleep(time.Second) newError("packet written: ", debugStats.packetWritten, " packet dropped: ", debugStats.packetDropped).WriteToLog() } }() return true }()*/ func (r *requestToPacketConnServerSession) Write(p []byte) (n int, err error) { buf := make([]byte, len(p)) copy(buf, p) select { case <-r.ctx.Done(): return 0, r.ctx.Err() case r.writerChan <- buf: debugStats.packetWritten++ return len(p), nil default: // This write will be called from global listener's routine, it must not block debugStats.packetDropped++ return len(p), nil } } func (r *requestToPacketConnServerSession) Close() error { r.server.removeSessionID(r.SessionID) r.finish() return nil } type writingConnection struct { focus func() finish func() finishCtx context.Context } func (r *requestToPacketConnServerSession) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption, ) (resp request.Response, err error) { // TODO: fix connection graceful close var streamingRespWriter io.Writer var streamingRespWriterFlusher request.OptionSupportsStreamingResponseExtensionFlusher for _, opt := range opts { if streamingRespOpt, ok := opt.(request.OptionSupportsStreamingResponse); ok { streamingRespWriter = streamingRespOpt.GetResponseWriter() if streamingRespWriterFlusherOpt, ok := opt.(request.OptionSupportsStreamingResponseExtensionFlusher); ok { streamingRespWriterFlusher = streamingRespWriterFlusherOpt } } } packetBundler := NewPacketBundle() reqReader := bytes.NewReader(req.Data) for reqReader.Len() != 0 { packet, err := packetBundler.ReadFromBundle(reqReader) if err != nil { err = newError("failed to read from bundle").Base(err) return request.Response{}, err } r.readerChan <- packet } onFocusCtx, focus := context.WithCancel(ctx) onFinishCtx, finish := context.WithCancel(ctx) r.writingConnectionQueue.Put(&writingConnection{ focus: focus, finish: finish, finishCtx: onFinishCtx, }) amountToEnd := r.writingConnectionQueue.Len() - int64(r.maxSimultaneousWriteConnection) for amountToEnd > 0 { { _, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool { i.(*writingConnection).finish() amountToEnd-- return amountToEnd > 0 }) } } { _, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool { i.(*writingConnection).focus() return false }) } bufferedRespWriter := bytes.NewBuffer(nil) finishWrite := func() { resp.Data = bufferedRespWriter.Bytes() { _, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool { i.(*writingConnection).focus() if i.(*writingConnection).finishCtx.Err() != nil { //nolint: gosimple return true } return false }) } } progressiveSend := streamingRespWriter != nil var respWriter io.Writer if progressiveSend { respWriter = streamingRespWriter } else { respWriter = bufferedRespWriter } var bytesSent int onReceivePacket := func(packet []byte) bool { bytesSent += len(packet) + packetBundler.Overhead() err := packetBundler.WriteToBundle(packet, respWriter) if err != nil { newError("failed to write to bundle").Base(err).WriteToLog() } if streamingRespWriterFlusher != nil { streamingRespWriterFlusher.Flush() } if bytesSent >= r.maxWriteSize { return false } return true } finishWriteTimer := time.NewTimer(time.Millisecond * time.Duration(r.maxWriteDuration)) if !progressiveSend { select { case <-onFocusCtx.Done(): case <-onFinishCtx.Done(): finishWrite() return resp, nil } } else { select { case <-onFinishCtx.Done(): finishWrite() return resp, nil default: } } firstRead := true for { select { case <-onFinishCtx.Done(): finishWrite() finishWriteTimer.Stop() return resp, nil case packet := <-r.writerChan: keepSending := onReceivePacket(packet) if firstRead { firstRead = false } if !keepSending { finishWrite() finishWriteTimer.Stop() return resp, nil } case <-finishWriteTimer.C: finishWrite() return resp, nil } } } ================================================ FILE: transport/internet/request/assembler/packetconn/udpassembler.go ================================================ package packetconn import ( "golang.org/x/net/context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type wrappedTransportEnvironment struct { environment.TransportEnvironment client *requestToPacketConnClient server *requestToPacketConnServer } func (w *wrappedTransportEnvironment) Listen(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.Listener, error) { return nil, newError("not implemented") } func (w *wrappedTransportEnvironment) ListenPacket(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.PacketConn, error) { packetConn := newWrappedPacketConn(ctx) w.server.onSessionReceiverReady(packetConn) return packetConn, nil } func (w *wrappedTransportEnvironment) Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *internet.SocketConfig) (net.Conn, error) { session, err := w.client.Dial() if err != nil { return nil, err } return newWrappedConn(session), nil } func (w *wrappedTransportEnvironment) Dialer() internet.SystemDialer { return w } func (w *wrappedTransportEnvironment) Listener() internet.SystemListener { return w } func newUDPAssemblerServerFromConfig(ctx context.Context, config *ServerConfig) (*udpAssemblerServer, error) { instance, err := serial.GetInstanceOf(config.UnderlyingTransportSetting) if err != nil { return nil, newError("failed to get instance of underlying transport").Base(err).AtError() } memcfg := &internet.MemoryStreamConfig{ProtocolName: config.UnderlyingTransportName, ProtocolSettings: instance} return newUDPAssemblerServer(ctx, config, memcfg), nil } func newUDPAssemblerClientFromConfig(ctx context.Context, config *ClientConfig) (*udpAssemblerClient, error) { instance, err := serial.GetInstanceOf(config.UnderlyingTransportSetting) if err != nil { return nil, newError("failed to get instance of underlying transport").Base(err).AtError() } memcfg := &internet.MemoryStreamConfig{ProtocolName: config.UnderlyingTransportName, ProtocolSettings: instance} return newUDPAssemblerClient(ctx, config, memcfg), nil } func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { serverConfig, ok := config.(*ServerConfig) if !ok { return nil, newError("not a ServerConfig") } return newUDPAssemblerServerFromConfig(ctx, serverConfig) })) common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { clientConfig, ok := config.(*ClientConfig) if !ok { return nil, newError("not a ClientConfig") } return newUDPAssemblerClientFromConfig(ctx, clientConfig) })) } ================================================ FILE: transport/internet/request/assembler/packetconn/udpassemblerClient.go ================================================ package packetconn import ( "io" gonet "net" "sync" "time" "golang.org/x/net/context" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) type udpAssemblerClient struct { ctx context.Context streamSettings *internet.MemoryStreamConfig assembly request.TransportClientAssembly req2connc *requestToPacketConnClient } func (u *udpAssemblerClient) NewSession(ctx context.Context, opts ...request.SessionOption) (request.Session, error) { return u.dial(net.Destination{}) } func (u *udpAssemblerClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) { u.assembly = assembly u.req2connc.OnTransportClientAssemblyReady(assembly) } func newWrappedConn(in io.ReadWriteCloser) net.Conn { return wrappedConn{in} } type wrappedConn struct { io.ReadWriteCloser } func (w wrappedConn) LocalAddr() gonet.Addr { return nil } func (w wrappedConn) RemoteAddr() gonet.Addr { return nil } func (w wrappedConn) SetDeadline(t time.Time) error { return nil } func (w wrappedConn) SetReadDeadline(t time.Time) error { return nil } func (w wrappedConn) SetWriteDeadline(t time.Time) error { return nil } func newWrappedPacketConn(ctx context.Context) *wrappedPacketConn { ctxWithCancel, cancel := context.WithCancel(ctx) return &wrappedPacketConn{ conn: make(map[string]*serverSession), readChan: make(chan packet, 16), ctx: ctxWithCancel, finish: cancel, connLock: &sync.Mutex{}, } } func newUDPAssemblerClient(ctx context.Context, config *ClientConfig, streamSettings *internet.MemoryStreamConfig) *udpAssemblerClient { transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) transportEnvironmentWrapped := &wrappedTransportEnvironment{TransportEnvironment: transportEnvironment} transportEnvironmentWrapped.client, _ = newRequestToPacketConnClient(ctx, config) wrappedContext := envctx.ContextWithEnvironment(ctx, transportEnvironmentWrapped) return &udpAssemblerClient{ctx: wrappedContext, streamSettings: streamSettings, req2connc: transportEnvironmentWrapped.client} } func (u *udpAssemblerClient) dial(dest net.Destination) (internet.Connection, error) { _ = dest return internet.Dial(u.ctx, net.TCPDestination(net.LocalHostIP, 0), u.streamSettings) } ================================================ FILE: transport/internet/request/assembler/packetconn/udpassemblerServer.go ================================================ package packetconn import ( "crypto/rand" "io" "net" "sync" "time" "golang.org/x/net/context" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" net2 "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) type packet struct { addr string data []byte } type wrappedPacketConn struct { connLock *sync.Mutex conn map[string]*serverSession readChan chan packet ctx context.Context finish func() } func (w *wrappedPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { select { case pack := <-w.readChan: n := copy(p, pack.data) if n < len(pack.data) { return n, nil, io.ErrShortBuffer } return n, &net.UDPAddr{IP: net2.IP(pack.addr)}, nil case <-w.ctx.Done(): return 0, nil, w.ctx.Err() } } func (w *wrappedPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { w.connLock.Lock() conn := w.conn[string(addr.(*net.UDPAddr).IP)] w.connLock.Unlock() return conn.Write(p) } func (w *wrappedPacketConn) Close() error { w.finish() return nil } func (w *wrappedPacketConn) LocalAddr() net.Addr { return nil } func (w *wrappedPacketConn) SetDeadline(t time.Time) error { return nil } func (w *wrappedPacketConn) SetReadDeadline(t time.Time) error { return nil } func (w *wrappedPacketConn) SetWriteDeadline(t time.Time) error { return nil } func (w wrappedPacketConn) OnNewSession(ctx context.Context, sess request.Session, opts ...request.SessionOption) error { imaginaryAddr := net2.UDPAddr{ IP: net2.AnyIPv6.IP(), Port: 0, } rand.Read([]byte(imaginaryAddr.IP)) session := newServerSession(ctx, sess, string(imaginaryAddr.IP), &w) w.connLock.Lock() w.conn[string(imaginaryAddr.IP)] = session w.connLock.Unlock() session.start() return nil } func newServerSession(ctx context.Context, sess request.Session, name string, listener *wrappedPacketConn) *serverSession { _ = ctx return &serverSession{session: sess, name: name, listener: listener} } type serverSession struct { name string session request.Session listener *wrappedPacketConn } func (s *serverSession) start() { go func() { for { select { case <-s.listener.ctx.Done(): return default: buf := make([]byte, 2000) n, err := s.session.Read(buf) if err != nil || n > 2000 { return } s.listener.readChan <- packet{s.name, buf[:n]} } } }() } func (s *serverSession) Write(p []byte) (int, error) { return s.session.Write(p) } type udpAssemblerServer struct { ctx context.Context streamSettings *internet.MemoryStreamConfig assembly request.TransportServerAssembly req2packs *requestToPacketConnServer listener internet.Listener } func (u *udpAssemblerServer) Start() error { listener, err := u.listen(net2.LocalHostIP, 0) if err != nil { return newError("failed to listen").Base(err).AtError() } u.listener = listener return nil } func (u *udpAssemblerServer) Close() error { return u.listener.Close() } func (u *udpAssemblerServer) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) { return u.req2packs.OnRoundTrip(ctx, req, opts...) } func (u *udpAssemblerServer) OnTransportServerAssemblyReady(assembly request.TransportServerAssembly) { u.assembly = assembly } func newUDPAssemblerServer(ctx context.Context, config *ServerConfig, streamSettings *internet.MemoryStreamConfig) *udpAssemblerServer { transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) transportEnvironmentWrapped := &wrappedTransportEnvironment{TransportEnvironment: transportEnvironment} transportEnvironmentWrapped.server = newRequestToPacketConnServer(ctx, config) wrappedContext := envctx.ContextWithEnvironment(ctx, transportEnvironmentWrapped) return &udpAssemblerServer{ctx: wrappedContext, streamSettings: streamSettings, req2packs: transportEnvironmentWrapped.server} } func (u *udpAssemblerServer) listen(address net2.Address, port net2.Port) (internet.Listener, error) { return internet.ListenTCP(u.ctx, address, port, u.streamSettings, func(connection internet.Connection) { err := u.assembly.SessionReceiver().OnNewSession(u.ctx, connection) if err != nil { newError("failed to handle new session").Base(err).WriteToLog() } }) } ================================================ FILE: transport/internet/request/assembler/simple/client.go ================================================ package simple import ( "bytes" "context" "crypto/rand" "io" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) func newClient(config *ClientConfig) request.SessionAssemblerClient { return &simpleAssemblerClient{config: config} } type simpleAssemblerClient struct { assembly request.TransportClientAssembly config *ClientConfig } func (s *simpleAssemblerClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) { s.assembly = assembly } func (s *simpleAssemblerClient) NewSession(ctx context.Context, opts ...request.SessionOption) (request.Session, error) { sessionID := make([]byte, 16) _, err := io.ReadFull(rand.Reader, sessionID) if err != nil { return nil, err } sessionContext, finish := context.WithCancel(ctx) session := &simpleAssemblerClientSession{ sessionID: sessionID, tripper: s.assembly.Tripper(), readBuffer: bytes.NewBuffer(nil), ctx: sessionContext, finish: finish, writerChan: make(chan []byte), readerChan: make(chan []byte, 16), assembler: s, } go session.keepRunning() return session, nil } type simpleAssemblerClientSession struct { sessionID []byte currentWriteWait int assembler *simpleAssemblerClient tripper request.Tripper readBuffer *bytes.Buffer writerChan chan []byte readerChan chan []byte ctx context.Context finish func() } func (s *simpleAssemblerClientSession) keepRunning() { s.currentWriteWait = int(s.assembler.config.InitialPollingIntervalMs) for s.ctx.Err() == nil { s.runOnce() } } func (s *simpleAssemblerClientSession) runOnce() { sendBuffer := bytes.NewBuffer(nil) if s.currentWriteWait != 0 { waitTimer := time.NewTimer(time.Millisecond * time.Duration(s.currentWriteWait)) waitForFirstWrite := true copyFromWriterLoop: for { select { case <-s.ctx.Done(): return case data := <-s.writerChan: sendBuffer.Write(data) if sendBuffer.Len() >= int(s.assembler.config.MaxWriteSize) { break copyFromWriterLoop } if waitForFirstWrite { waitForFirstWrite = false waitTimer.Reset(time.Millisecond * time.Duration(s.assembler.config.WaitSubsequentWriteMs)) } case <-waitTimer.C: break copyFromWriterLoop } } waitTimer.Stop() } firstRound := true pollConnection := true for sendBuffer.Len() != 0 || firstRound { firstRound = false sendAmount := sendBuffer.Len() if sendAmount > int(s.assembler.config.MaxWriteSize) { sendAmount = int(s.assembler.config.MaxWriteSize) } data := sendBuffer.Next(sendAmount) if len(data) != 0 { pollConnection = false } for { resp, err := s.tripper.RoundTrip(s.ctx, request.Request{Data: data, ConnectionTag: s.sessionID}) if err != nil { newError("failed to send data").Base(err).WriteToLog() if s.ctx.Err() != nil { return } time.Sleep(time.Millisecond * time.Duration(s.assembler.config.FailedRetryIntervalMs)) continue } if len(resp.Data) != 0 { s.readerChan <- resp.Data } if len(resp.Data) != 0 { pollConnection = false } break } } if pollConnection { s.currentWriteWait = int(s.assembler.config.BackoffFactor * float32(s.currentWriteWait)) if s.currentWriteWait > int(s.assembler.config.MaxPollingIntervalMs) { s.currentWriteWait = int(s.assembler.config.MaxPollingIntervalMs) } if s.currentWriteWait < int(s.assembler.config.MinPollingIntervalMs) { s.currentWriteWait = int(s.assembler.config.MinPollingIntervalMs) } } else { s.currentWriteWait = int(0) } } func (s *simpleAssemblerClientSession) Read(p []byte) (n int, err error) { if s.readBuffer.Len() == 0 { select { case <-s.ctx.Done(): return 0, s.ctx.Err() case data := <-s.readerChan: s.readBuffer.Write(data) } } n, err = s.readBuffer.Read(p) if err == io.EOF { s.readBuffer.Reset() return 0, nil } return } func (s *simpleAssemblerClientSession) Write(p []byte) (n int, err error) { buf := make([]byte, len(p)) copy(buf, p) select { case <-s.ctx.Done(): return 0, s.ctx.Err() case s.writerChan <- buf: return len(p), nil } } func (s *simpleAssemblerClientSession) Close() error { s.finish() return nil } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { clientConfig, ok := config.(*ClientConfig) if !ok { return nil, newError("not a ClientConfig") } return newClient(clientConfig), nil })) } ================================================ FILE: transport/internet/request/assembler/simple/config.pb.go ================================================ package simple import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` MaxWriteSize int32 `protobuf:"varint,1,opt,name=max_write_size,json=maxWriteSize,proto3" json:"max_write_size,omitempty"` WaitSubsequentWriteMs int32 `protobuf:"varint,2,opt,name=wait_subsequent_write_ms,json=waitSubsequentWriteMs,proto3" json:"wait_subsequent_write_ms,omitempty"` InitialPollingIntervalMs int32 `protobuf:"varint,3,opt,name=initial_polling_interval_ms,json=initialPollingIntervalMs,proto3" json:"initial_polling_interval_ms,omitempty"` MaxPollingIntervalMs int32 `protobuf:"varint,4,opt,name=max_polling_interval_ms,json=maxPollingIntervalMs,proto3" json:"max_polling_interval_ms,omitempty"` MinPollingIntervalMs int32 `protobuf:"varint,5,opt,name=min_polling_interval_ms,json=minPollingIntervalMs,proto3" json:"min_polling_interval_ms,omitempty"` BackoffFactor float32 `protobuf:"fixed32,6,opt,name=backoff_factor,json=backoffFactor,proto3" json:"backoff_factor,omitempty"` FailedRetryIntervalMs int32 `protobuf:"varint,7,opt,name=failed_retry_interval_ms,json=failedRetryIntervalMs,proto3" json:"failed_retry_interval_ms,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_transport_internet_request_assembler_simple_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_assembler_simple_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_transport_internet_request_assembler_simple_config_proto_rawDescGZIP(), []int{0} } func (x *ClientConfig) GetMaxWriteSize() int32 { if x != nil { return x.MaxWriteSize } return 0 } func (x *ClientConfig) GetWaitSubsequentWriteMs() int32 { if x != nil { return x.WaitSubsequentWriteMs } return 0 } func (x *ClientConfig) GetInitialPollingIntervalMs() int32 { if x != nil { return x.InitialPollingIntervalMs } return 0 } func (x *ClientConfig) GetMaxPollingIntervalMs() int32 { if x != nil { return x.MaxPollingIntervalMs } return 0 } func (x *ClientConfig) GetMinPollingIntervalMs() int32 { if x != nil { return x.MinPollingIntervalMs } return 0 } func (x *ClientConfig) GetBackoffFactor() float32 { if x != nil { return x.BackoffFactor } return 0 } func (x *ClientConfig) GetFailedRetryIntervalMs() int32 { if x != nil { return x.FailedRetryIntervalMs } return 0 } type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` MaxWriteSize int32 `protobuf:"varint,1,opt,name=max_write_size,json=maxWriteSize,proto3" json:"max_write_size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_transport_internet_request_assembler_simple_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_assembler_simple_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_transport_internet_request_assembler_simple_config_proto_rawDescGZIP(), []int{1} } func (x *ServerConfig) GetMaxWriteSize() int32 { if x != nil { return x.MaxWriteSize } return 0 } var File_transport_internet_request_assembler_simple_config_proto protoreflect.FileDescriptor const file_transport_internet_request_assembler_simple_config_proto_rawDesc = "" + "\n" + "8transport/internet/request/assembler/simple/config.proto\x126v2ray.core.transport.internet.request.assembler.simple\x1a common/protoext/extensions.proto\"\xac\x03\n" + "\fClientConfig\x12$\n" + "\x0emax_write_size\x18\x01 \x01(\x05R\fmaxWriteSize\x127\n" + "\x18wait_subsequent_write_ms\x18\x02 \x01(\x05R\x15waitSubsequentWriteMs\x12=\n" + "\x1binitial_polling_interval_ms\x18\x03 \x01(\x05R\x18initialPollingIntervalMs\x125\n" + "\x17max_polling_interval_ms\x18\x04 \x01(\x05R\x14maxPollingIntervalMs\x125\n" + "\x17min_polling_interval_ms\x18\x05 \x01(\x05R\x14minPollingIntervalMs\x12%\n" + "\x0ebackoff_factor\x18\x06 \x01(\x02R\rbackoffFactor\x127\n" + "\x18failed_retry_interval_ms\x18\a \x01(\x05R\x15failedRetryIntervalMs:0\x82\xb5\x18,\n" + "\"transport.request.assembler.client\x12\x06simple\"f\n" + "\fServerConfig\x12$\n" + "\x0emax_write_size\x18\x01 \x01(\x05R\fmaxWriteSize:0\x82\xb5\x18,\n" + "\"transport.request.assembler.server\x12\x06simpleB\xc3\x01\n" + ":com.v2ray.core.transport.internet.request.assembler.simpleP\x01ZJgithub.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/simple\xaa\x026V2Ray.Core.Transport.Internet.Request.Assembler.Simpleb\x06proto3" var ( file_transport_internet_request_assembler_simple_config_proto_rawDescOnce sync.Once file_transport_internet_request_assembler_simple_config_proto_rawDescData []byte ) func file_transport_internet_request_assembler_simple_config_proto_rawDescGZIP() []byte { file_transport_internet_request_assembler_simple_config_proto_rawDescOnce.Do(func() { file_transport_internet_request_assembler_simple_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_request_assembler_simple_config_proto_rawDesc), len(file_transport_internet_request_assembler_simple_config_proto_rawDesc))) }) return file_transport_internet_request_assembler_simple_config_proto_rawDescData } var file_transport_internet_request_assembler_simple_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_transport_internet_request_assembler_simple_config_proto_goTypes = []any{ (*ClientConfig)(nil), // 0: v2ray.core.transport.internet.request.assembler.simple.ClientConfig (*ServerConfig)(nil), // 1: v2ray.core.transport.internet.request.assembler.simple.ServerConfig } var file_transport_internet_request_assembler_simple_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_request_assembler_simple_config_proto_init() } func file_transport_internet_request_assembler_simple_config_proto_init() { if File_transport_internet_request_assembler_simple_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_request_assembler_simple_config_proto_rawDesc), len(file_transport_internet_request_assembler_simple_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_request_assembler_simple_config_proto_goTypes, DependencyIndexes: file_transport_internet_request_assembler_simple_config_proto_depIdxs, MessageInfos: file_transport_internet_request_assembler_simple_config_proto_msgTypes, }.Build() File_transport_internet_request_assembler_simple_config_proto = out.File file_transport_internet_request_assembler_simple_config_proto_goTypes = nil file_transport_internet_request_assembler_simple_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/request/assembler/simple/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.request.assembler.simple; option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.Assembler.Simple"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/simple"; option java_package = "com.v2ray.core.transport.internet.request.assembler.simple"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "transport.request.assembler.client"; option (v2ray.core.common.protoext.message_opt).short_name = "simple"; int32 max_write_size = 1; int32 wait_subsequent_write_ms = 2; int32 initial_polling_interval_ms = 3; int32 max_polling_interval_ms = 4; int32 min_polling_interval_ms = 5; float backoff_factor = 6; int32 failed_retry_interval_ms = 7; } message ServerConfig { option (v2ray.core.common.protoext.message_opt).type = "transport.request.assembler.server"; option (v2ray.core.common.protoext.message_opt).short_name = "simple"; int32 max_write_size = 1; } ================================================ FILE: transport/internet/request/assembler/simple/errors.generated.go ================================================ package simple import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/request/assembler/simple/server.go ================================================ package simple import ( "bytes" "context" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) func newServer(config *ServerConfig) request.SessionAssemblerServer { return &simpleAssemblerServer{} } type simpleAssemblerServer struct { sessions sync.Map assembly request.TransportServerAssembly } func (s *simpleAssemblerServer) OnTransportServerAssemblyReady(assembly request.TransportServerAssembly) { s.assembly = assembly } func (s *simpleAssemblerServer) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption, ) (resp request.Response, err error) { connectionID := req.ConnectionTag session := newSimpleAssemblerServerSession(ctx) loadedSession, loaded := s.sessions.LoadOrStore(string(connectionID), session) if loaded { session = loadedSession.(*simpleAssemblerServerSession) } else { if err := s.assembly.SessionReceiver().OnNewSession(ctx, session); err != nil { return request.Response{}, newError("failed to create new session").Base(err) } } return session.OnRoundTrip(ctx, req, opts...) } func newSimpleAssemblerServerSession(ctx context.Context) *simpleAssemblerServerSession { sessionCtx, finish := context.WithCancel(ctx) return &simpleAssemblerServerSession{ readBuffer: bytes.NewBuffer(nil), readChan: make(chan []byte, 16), requestProcessed: make(chan struct{}), writeLock: new(sync.Mutex), writeBuffer: bytes.NewBuffer(nil), maxWriteSize: 4096, ctx: sessionCtx, finish: finish, } } type simpleAssemblerServerSession struct { maxWriteSize int readBuffer *bytes.Buffer readChan chan []byte requestProcessed chan struct{} writeLock *sync.Mutex writeBuffer *bytes.Buffer ctx context.Context finish func() } func (s *simpleAssemblerServerSession) Read(p []byte) (n int, err error) { if s.readBuffer.Len() == 0 { select { case <-s.ctx.Done(): return 0, s.ctx.Err() case data := <-s.readChan: s.readBuffer.Write(data) } } return s.readBuffer.Read(p) } func (s *simpleAssemblerServerSession) Write(p []byte) (n int, err error) { s.writeLock.Lock() n, err = s.writeBuffer.Write(p) length := s.writeBuffer.Len() s.writeLock.Unlock() if err != nil { return 0, err } if length > s.maxWriteSize { select { case <-s.requestProcessed: case <-s.ctx.Done(): return 0, s.ctx.Err() } } return } func (s *simpleAssemblerServerSession) Close() error { s.finish() return nil } func (s *simpleAssemblerServerSession) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption, ) (resp request.Response, err error) { if len(req.Data) > 0 { select { case <-s.ctx.Done(): return request.Response{}, s.ctx.Err() case s.readChan <- req.Data: } } s.writeLock.Lock() nextWrite := s.writeBuffer.Next(s.maxWriteSize) data := make([]byte, len(nextWrite)) copy(data, nextWrite) s.writeLock.Unlock() select { case s.requestProcessed <- struct{}{}: case <-s.ctx.Done(): return request.Response{}, s.ctx.Err() default: } return request.Response{Data: data}, nil } func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { serverConfig, ok := config.(*ServerConfig) if !ok { return nil, newError("not a SimpleServerConfig") } return newServer(serverConfig), nil })) } ================================================ FILE: transport/internet/request/assembler/simple/simple.go ================================================ package simple //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/request/assembler.go ================================================ package request import ( "io" ) type SessionAssemblerClient interface { SessionCreator TransportClientAssemblyReceiver } type SessionAssemblerServer interface { TripperReceiver TransportServerAssemblyReceiver } type SessionOption interface { RoundTripperOption() } type Session interface { io.ReadWriteCloser } ================================================ FILE: transport/internet/request/assembly/assembly.go ================================================ package assembly import ( "context" "github.com/v2fly/v2ray-core/v5/common" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const protocolName = "request" func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return nil, newError("request is a transport protocol.") })) } ================================================ FILE: transport/internet/request/assembly/config.pb.go ================================================ package assembly import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Assembler *anypb.Any `protobuf:"bytes,1,opt,name=assembler,proto3" json:"assembler,omitempty"` Roundtripper *anypb.Any `protobuf:"bytes,2,opt,name=roundtripper,proto3" json:"roundtripper,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_request_assembly_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_assembly_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_request_assembly_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetAssembler() *anypb.Any { if x != nil { return x.Assembler } return nil } func (x *Config) GetRoundtripper() *anypb.Any { if x != nil { return x.Roundtripper } return nil } var File_transport_internet_request_assembly_config_proto protoreflect.FileDescriptor const file_transport_internet_request_assembly_config_proto_rawDesc = "" + "\n" + "0transport/internet/request/assembly/config.proto\x12.v2ray.core.transport.internet.request.assembly\x1a common/protoext/extensions.proto\x1a\x19google/protobuf/any.proto\"\x90\x01\n" + "\x06Config\x122\n" + "\tassembler\x18\x01 \x01(\v2\x14.google.protobuf.AnyR\tassembler\x128\n" + "\froundtripper\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\froundtripper:\x18\x82\xb5\x18\x14\n" + "\ttransport\x12\arequestB\xab\x01\n" + "2com.v2ray.core.transport.internet.request.assemblyP\x01ZBgithub.com/v2fly/v2ray-core/v5/transport/internet/request/assembly\xaa\x02.V2Ray.Core.Transport.Internet.Request.Assemblyb\x06proto3" var ( file_transport_internet_request_assembly_config_proto_rawDescOnce sync.Once file_transport_internet_request_assembly_config_proto_rawDescData []byte ) func file_transport_internet_request_assembly_config_proto_rawDescGZIP() []byte { file_transport_internet_request_assembly_config_proto_rawDescOnce.Do(func() { file_transport_internet_request_assembly_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_request_assembly_config_proto_rawDesc), len(file_transport_internet_request_assembly_config_proto_rawDesc))) }) return file_transport_internet_request_assembly_config_proto_rawDescData } var file_transport_internet_request_assembly_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_request_assembly_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.request.assembly.Config (*anypb.Any)(nil), // 1: google.protobuf.Any } var file_transport_internet_request_assembly_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.transport.internet.request.assembly.Config.assembler:type_name -> google.protobuf.Any 1, // 1: v2ray.core.transport.internet.request.assembly.Config.roundtripper:type_name -> google.protobuf.Any 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_transport_internet_request_assembly_config_proto_init() } func file_transport_internet_request_assembly_config_proto_init() { if File_transport_internet_request_assembly_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_request_assembly_config_proto_rawDesc), len(file_transport_internet_request_assembly_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_request_assembly_config_proto_goTypes, DependencyIndexes: file_transport_internet_request_assembly_config_proto_depIdxs, MessageInfos: file_transport_internet_request_assembly_config_proto_msgTypes, }.Build() File_transport_internet_request_assembly_config_proto = out.File file_transport_internet_request_assembly_config_proto_goTypes = nil file_transport_internet_request_assembly_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/request/assembly/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.request.assembly; option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.Assembly"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembly"; option java_package = "com.v2ray.core.transport.internet.request.assembly"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "google/protobuf/any.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "request"; google.protobuf.Any assembler = 1; google.protobuf.Any roundtripper = 2; } ================================================ FILE: transport/internet/request/assembly/dialer.go ================================================ package assembly import ( "context" gonet "net" "time" "github.com/v2fly/v2ray-core/v5/transport/internet/transportcommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) type client struct { tripper request.RoundTripperClient assembler request.SessionAssemblerClient streamSettings *internet.MemoryStreamConfig dest net.Destination } func (c client) Dial(ctx context.Context) (net.Conn, error) { return transportcommon.DialWithSecuritySettings(ctx, c.dest, c.streamSettings) } func (c client) AutoImplDialer() request.Dialer { return c } func (c client) Tripper() request.Tripper { return c.tripper } func (c client) dialRequestSession(ctx context.Context) (net.Conn, error) { session, err := c.assembler.NewSession(ctx) if err != nil { return nil, newError("failed to create new session").Base(err) } return clientConnection{session}, nil } type clientConnection struct { request.Session } func (c clientConnection) LocalAddr() gonet.Addr { return &net.UnixAddr{Name: "unimplemented"} } func (c clientConnection) RemoteAddr() gonet.Addr { return &net.UnixAddr{Name: "unimplemented"} } func (c clientConnection) SetDeadline(t time.Time) error { // Unimplemented return nil } func (c clientConnection) SetReadDeadline(t time.Time) error { // Unimplemented return nil } func (c clientConnection) SetWriteDeadline(t time.Time) error { // Unimplemented return nil } func dialRequest(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) { clientAssembly := &client{} transportConfiguration := streamSettings.ProtocolSettings.(*Config) assemblerConfigInstance, err := serial.GetInstanceOf(transportConfiguration.Assembler) if err != nil { return nil, newError("failed to get config instance of assembler").Base(err) } assembler, err := common.CreateObject(ctx, assemblerConfigInstance) if err != nil { return nil, newError("failed to create assembler").Base(err) } if typedAssembler, ok := assembler.(request.SessionAssemblerClient); !ok { return nil, newError("failed to type assert assembler to SessionAssemblerClient") } else { clientAssembly.assembler = typedAssembler } roundtripperConfigInstance, err := serial.GetInstanceOf(transportConfiguration.Roundtripper) if err != nil { return nil, newError("failed to get config instance of roundtripper").Base(err) } roundtripper, err := common.CreateObject(ctx, roundtripperConfigInstance) if err != nil { return nil, newError("failed to create roundtripper").Base(err) } if typedRoundtripper, ok := roundtripper.(request.RoundTripperClient); !ok { return nil, newError("failed to type assert roundtripper to RoundTripperClient") } else { clientAssembly.tripper = typedRoundtripper } clientAssembly.streamSettings = streamSettings clientAssembly.dest = dest clientAssembly.assembler.OnTransportClientAssemblyReady(clientAssembly) clientAssembly.tripper.OnTransportClientAssemblyReady(clientAssembly) return clientAssembly.dialRequestSession(ctx) } func dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn, err := dialRequest(ctx, dest, streamSettings) if err != nil { return nil, newError("failed to dial request to ", dest).Base(err) } return internet.Connection(conn), nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, dial)) } ================================================ FILE: transport/internet/request/assembly/errors.generated.go ================================================ package assembly import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/request/assembly/hub.go ================================================ package assembly import ( "context" gonet "net" "time" "github.com/v2fly/v2ray-core/v5/transport/internet/transportcommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) type server struct { tripper request.RoundTripperServer assembler request.SessionAssemblerServer addConn internet.ConnHandler streamSettings *internet.MemoryStreamConfig addr net.Address port net.Port } func (s server) Listen(ctx context.Context) (net.Listener, error) { return transportcommon.ListenWithSecuritySettings(ctx, s.addr, s.port, s.streamSettings) } func (s server) AutoImplListener() request.Listener { return s } func (s server) Close() error { if err := s.tripper.Close(); err != nil { return newError("failed to close tripper").Base(err) } if runnableAssembler, ok := s.assembler.(common.Runnable); ok { if err := runnableAssembler.Close(); err != nil { return newError("failed to close assembler").Base(err) } } return nil } func (s server) Addr() net.Addr { // Unimplemented return nil } type serverConnection struct { request.Session } func (s serverConnection) LocalAddr() gonet.Addr { return &net.UnixAddr{Name: "unimplemented"} } func (s serverConnection) RemoteAddr() gonet.Addr { return &net.UnixAddr{Name: "unimplemented"} } func (s serverConnection) SetDeadline(t time.Time) error { // Unimplemented return nil } func (s serverConnection) SetReadDeadline(t time.Time) error { // Unimplemented return nil } func (s serverConnection) SetWriteDeadline(t time.Time) error { // Unimplemented return nil } func (s server) OnNewSession(ctx context.Context, sess request.Session, opts ...request.SessionOption) error { s.addConn(&serverConnection{sess}) return nil } func (s server) SessionReceiver() request.SessionReceiver { return s } func (s server) TripperReceiver() request.TripperReceiver { return s.assembler } func listenRequest(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { transportConfiguration := streamSettings.ProtocolSettings.(*Config) serverAssembly := &server{addConn: addConn} assemblerConfigInstance, err := serial.GetInstanceOf(transportConfiguration.Assembler) if err != nil { return nil, newError("failed to get config instance of assembler").Base(err) } assembler, err := common.CreateObject(ctx, assemblerConfigInstance) if err != nil { return nil, newError("failed to create assembler").Base(err) } if typedAssembler, ok := assembler.(request.SessionAssemblerServer); !ok { return nil, newError("failed to type assert assembler to SessionAssemblerServer") } else { serverAssembly.assembler = typedAssembler } roundtripperConfigInstance, err := serial.GetInstanceOf(transportConfiguration.Roundtripper) if err != nil { return nil, newError("failed to get config instance of roundtripper").Base(err) } roundtripper, err := common.CreateObject(ctx, roundtripperConfigInstance) if err != nil { return nil, newError("failed to create roundtripper").Base(err) } if typedRoundtripper, ok := roundtripper.(request.RoundTripperServer); !ok { return nil, newError("failed to type assert roundtripper to RoundTripperServer") } else { serverAssembly.tripper = typedRoundtripper } serverAssembly.addr = address serverAssembly.port = port serverAssembly.streamSettings = streamSettings serverAssembly.assembler.OnTransportServerAssemblyReady(serverAssembly) serverAssembly.tripper.OnTransportServerAssemblyReady(serverAssembly) if err := serverAssembly.tripper.Start(); err != nil { return nil, newError("failed to start tripper").Base(err) } if runnableAssembler, ok := serverAssembly.assembler.(common.Runnable); ok { if err := runnableAssembler.Start(); err != nil { return nil, newError("failed to start assembler").Base(err) } } return serverAssembly, nil } func init() { common.Must(internet.RegisterTransportListener(protocolName, listenRequest)) } ================================================ FILE: transport/internet/request/options.go ================================================ package request ================================================ FILE: transport/internet/request/request.go ================================================ package request import ( "context" "github.com/v2fly/v2ray-core/v5/common/net" ) type TransportClientAssembly interface { Tripper() Tripper AutoImplDialer() Dialer } type TransportClientAssemblyReceiver interface { OnTransportClientAssemblyReady(TransportClientAssembly) } type TransportServerAssembly interface { TripperReceiver() TripperReceiver SessionReceiver() SessionReceiver AutoImplListener() Listener } type TransportServerAssemblyReceiver interface { OnTransportServerAssemblyReady(TransportServerAssembly) } type SessionCreator interface { NewSession(ctx context.Context, opts ...SessionOption) (Session, error) } type SessionReceiver interface { OnNewSession(ctx context.Context, sess Session, opts ...SessionOption) error } type Dialer interface { Dial(ctx context.Context) (net.Conn, error) } type Listener interface { Listen(ctx context.Context) (net.Listener, error) } ================================================ FILE: transport/internet/request/reverser.go ================================================ package request import "context" type ReverserImpl interface { OnOtherRoundTrip(ctx context.Context, req Request, opts ...RoundTripperOption) (resp Response, err error) OnAuthenticatedServerIntentRoundTrip(ctx context.Context, serverPublic []byte, req Request, opts ...RoundTripperOption) (resp Response, err error) } type ReverserAccessChecker interface { CheckReverserAccess(ctx context.Context, serverKey []byte) (clientKey []byte, err error) } ================================================ FILE: transport/internet/request/roundtripper/httprt/config.pb.go ================================================ package httprt import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Http *HTTPConfig `protobuf:"bytes,1,opt,name=http,proto3" json:"http,omitempty"` AllowHttp bool `protobuf:"varint,2,opt,name=allow_http,json=allowHttp,proto3" json:"allow_http,omitempty"` H2PoolSize int32 `protobuf:"varint,3,opt,name=h2_pool_size,json=h2PoolSize,proto3" json:"h2_pool_size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_transport_internet_request_roundtripper_httprt_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_roundtripper_httprt_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_transport_internet_request_roundtripper_httprt_config_proto_rawDescGZIP(), []int{0} } func (x *ClientConfig) GetHttp() *HTTPConfig { if x != nil { return x.Http } return nil } func (x *ClientConfig) GetAllowHttp() bool { if x != nil { return x.AllowHttp } return false } func (x *ClientConfig) GetH2PoolSize() int32 { if x != nil { return x.H2PoolSize } return 0 } type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Http *HTTPConfig `protobuf:"bytes,1,opt,name=http,proto3" json:"http,omitempty"` NoDecodingSessionTag bool `protobuf:"varint,2,opt,name=no_decoding_session_tag,json=noDecodingSessionTag,proto3" json:"no_decoding_session_tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_transport_internet_request_roundtripper_httprt_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_roundtripper_httprt_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_transport_internet_request_roundtripper_httprt_config_proto_rawDescGZIP(), []int{1} } func (x *ServerConfig) GetHttp() *HTTPConfig { if x != nil { return x.Http } return nil } func (x *ServerConfig) GetNoDecodingSessionTag() bool { if x != nil { return x.NoDecodingSessionTag } return false } type HTTPConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` UrlPrefix string `protobuf:"bytes,2,opt,name=urlPrefix,proto3" json:"urlPrefix,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HTTPConfig) Reset() { *x = HTTPConfig{} mi := &file_transport_internet_request_roundtripper_httprt_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HTTPConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*HTTPConfig) ProtoMessage() {} func (x *HTTPConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_roundtripper_httprt_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HTTPConfig.ProtoReflect.Descriptor instead. func (*HTTPConfig) Descriptor() ([]byte, []int) { return file_transport_internet_request_roundtripper_httprt_config_proto_rawDescGZIP(), []int{2} } func (x *HTTPConfig) GetPath() string { if x != nil { return x.Path } return "" } func (x *HTTPConfig) GetUrlPrefix() string { if x != nil { return x.UrlPrefix } return "" } var File_transport_internet_request_roundtripper_httprt_config_proto protoreflect.FileDescriptor const file_transport_internet_request_roundtripper_httprt_config_proto_rawDesc = "" + "\n" + ";transport/internet/request/roundtripper/httprt/config.proto\x129v2ray.core.transport.internet.request.roundtripper.httprt\x1a common/protoext/extensions.proto\"\xdf\x01\n" + "\fClientConfig\x12Y\n" + "\x04http\x18\x01 \x01(\v2E.v2ray.core.transport.internet.request.roundtripper.httprt.HTTPConfigR\x04http\x12\x1d\n" + "\n" + "allow_http\x18\x02 \x01(\bR\tallowHttp\x12 \n" + "\fh2_pool_size\x18\x03 \x01(\x05R\n" + "h2PoolSize:3\x82\xb5\x18/\n" + "%transport.request.roundtripper.client\x12\x06httprt\"\xd5\x01\n" + "\fServerConfig\x12Y\n" + "\x04http\x18\x01 \x01(\v2E.v2ray.core.transport.internet.request.roundtripper.httprt.HTTPConfigR\x04http\x125\n" + "\x17no_decoding_session_tag\x18\x02 \x01(\bR\x14noDecodingSessionTag:3\x82\xb5\x18/\n" + "%transport.request.roundtripper.server\x12\x06httprt\">\n" + "\n" + "HTTPConfig\x12\x12\n" + "\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n" + "\turlPrefix\x18\x02 \x01(\tR\turlPrefixB\xcc\x01\n" + "=com.v2ray.core.transport.internet.request.roundtripper.httprtP\x01ZMgithub.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripper/httprt\xaa\x029V2Ray.Core.Transport.Internet.Request.Roundtripper.httprtb\x06proto3" var ( file_transport_internet_request_roundtripper_httprt_config_proto_rawDescOnce sync.Once file_transport_internet_request_roundtripper_httprt_config_proto_rawDescData []byte ) func file_transport_internet_request_roundtripper_httprt_config_proto_rawDescGZIP() []byte { file_transport_internet_request_roundtripper_httprt_config_proto_rawDescOnce.Do(func() { file_transport_internet_request_roundtripper_httprt_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_request_roundtripper_httprt_config_proto_rawDesc), len(file_transport_internet_request_roundtripper_httprt_config_proto_rawDesc))) }) return file_transport_internet_request_roundtripper_httprt_config_proto_rawDescData } var file_transport_internet_request_roundtripper_httprt_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_transport_internet_request_roundtripper_httprt_config_proto_goTypes = []any{ (*ClientConfig)(nil), // 0: v2ray.core.transport.internet.request.roundtripper.httprt.ClientConfig (*ServerConfig)(nil), // 1: v2ray.core.transport.internet.request.roundtripper.httprt.ServerConfig (*HTTPConfig)(nil), // 2: v2ray.core.transport.internet.request.roundtripper.httprt.HTTPConfig } var file_transport_internet_request_roundtripper_httprt_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.transport.internet.request.roundtripper.httprt.ClientConfig.http:type_name -> v2ray.core.transport.internet.request.roundtripper.httprt.HTTPConfig 2, // 1: v2ray.core.transport.internet.request.roundtripper.httprt.ServerConfig.http:type_name -> v2ray.core.transport.internet.request.roundtripper.httprt.HTTPConfig 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_transport_internet_request_roundtripper_httprt_config_proto_init() } func file_transport_internet_request_roundtripper_httprt_config_proto_init() { if File_transport_internet_request_roundtripper_httprt_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_request_roundtripper_httprt_config_proto_rawDesc), len(file_transport_internet_request_roundtripper_httprt_config_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_request_roundtripper_httprt_config_proto_goTypes, DependencyIndexes: file_transport_internet_request_roundtripper_httprt_config_proto_depIdxs, MessageInfos: file_transport_internet_request_roundtripper_httprt_config_proto_msgTypes, }.Build() File_transport_internet_request_roundtripper_httprt_config_proto = out.File file_transport_internet_request_roundtripper_httprt_config_proto_goTypes = nil file_transport_internet_request_roundtripper_httprt_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/request/roundtripper/httprt/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.request.roundtripper.httprt; option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.Roundtripper.httprt"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripper/httprt"; option java_package = "com.v2ray.core.transport.internet.request.roundtripper.httprt"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message ClientConfig { option (v2ray.core.common.protoext.message_opt).type = "transport.request.roundtripper.client"; option (v2ray.core.common.protoext.message_opt).short_name = "httprt"; HTTPConfig http = 1; bool allow_http = 2; int32 h2_pool_size = 3; } message ServerConfig { option (v2ray.core.common.protoext.message_opt).type = "transport.request.roundtripper.server"; option (v2ray.core.common.protoext.message_opt).short_name = "httprt"; HTTPConfig http = 1; bool no_decoding_session_tag = 2; } message HTTPConfig { string path = 1; string urlPrefix = 2; } ================================================ FILE: transport/internet/request/roundtripper/httprt/errors.generated.go ================================================ package httprt import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/request/roundtripper/httprt/httprt.go ================================================ package httprt //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "bytes" "context" "encoding/base64" "io" gonet "net" "net/http" "time" "github.com/v2fly/v2ray-core/v5/transport/internet/transportcommon" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) func newHTTPRoundTripperClient(ctx context.Context, config *ClientConfig) request.RoundTripperClient { _ = ctx return &httpTripperClient{config: config} } type httpTripperClient struct { httpRTT http.RoundTripper config *ClientConfig assembly request.TransportClientAssembly } type unimplementedBackDrop struct{} func (u unimplementedBackDrop) RoundTrip(r *http.Request) (*http.Response, error) { return nil, newError("unimplemented") } func (h *httpTripperClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) { h.assembly = assembly } func (h *httpTripperClient) RoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) { var streamingWriter io.Writer for _, v := range opts { if streamingResp, ok := v.(request.OptionSupportsStreamingResponse); ok { streamingWriter = streamingResp.GetResponseWriter() } } if h.httpRTT == nil { var backDrop http.RoundTripper = unimplementedBackDrop{} if h.config.AllowHttp { backDrop = &http.Transport{ DialContext: func(_ context.Context, network, addr string) (gonet.Conn, error) { return h.assembly.AutoImplDialer().Dial(ctx) }, DialTLSContext: func(_ context.Context, network, addr string) (gonet.Conn, error) { return nil, newError("unexpected dial of TLS") }, } } h.httpRTT = transportcommon.NewALPNAwareHTTPRoundTripperWithH2Pool(ctx, func(ctx context.Context, addr string) (gonet.Conn, error) { return h.assembly.AutoImplDialer().Dial(ctx) }, backDrop, int(h.config.H2PoolSize)) } connectionTagStr := base64.RawURLEncoding.EncodeToString(req.ConnectionTag) httpRequest, err := http.NewRequest("POST", h.config.Http.UrlPrefix+h.config.Http.Path, bytes.NewReader(req.Data)) if err != nil { return resp, err } httpRequest.Header.Set("X-Session-ID", connectionTagStr) httpResp, err := h.httpRTT.RoundTrip(httpRequest) if err != nil { return resp, err } defer httpResp.Body.Close() if httpResp.StatusCode != http.StatusOK { newError("non-200 response: ", httpResp.Status).AtInfo().WriteToLog() } if streamingWriter == nil { result, err := io.ReadAll(httpResp.Body) if err != nil { return request.Response{}, err } return request.Response{Data: result}, nil } _, err = io.Copy(streamingWriter, httpResp.Body) if err != nil { return request.Response{}, newError("unable to copy response").Base(err) } return request.Response{}, nil } func newHTTPRoundTripperServer(ctx context.Context, config *ServerConfig) request.RoundTripperServer { return &httpTripperServer{ctx: ctx, config: config} } type httpTripperServer struct { ctx context.Context listener net.Listener assembly request.TransportServerAssembly config *ServerConfig } func (h *httpTripperServer) OnTransportServerAssemblyReady(assembly request.TransportServerAssembly) { h.assembly = assembly } func (h *httpTripperServer) ServeHTTP(writer http.ResponseWriter, r *http.Request) { h.onRequest(writer, r) } type httpRespStreamWriting struct { resp http.ResponseWriter used bool } func (h *httpRespStreamWriting) RoundTripperOption() { } func (h *httpRespStreamWriting) GetResponseWriter() io.Writer { h.used = true return h.resp } func (h *httpRespStreamWriting) Flush() { if f, ok := h.resp.(http.Flusher); ok { f.Flush() } } func (h *httpTripperServer) onRequest(resp http.ResponseWriter, req *http.Request) { tail := req.Header.Get("X-Session-ID") data := []byte(tail) if !h.config.NoDecodingSessionTag { decodedData, err := base64.RawURLEncoding.DecodeString(tail) if err != nil { newError("unable to decode tag").Base(err).AtInfo().WriteToLog() return } data = decodedData } body, err := io.ReadAll(req.Body) req.Body.Close() if err != nil { newError("unable to read body").Base(err).AtInfo().WriteToLog() } streamingRespOption := &httpRespStreamWriting{resp: resp} recvResp, err := h.assembly.TripperReceiver().OnRoundTrip(h.ctx, request.Request{Data: body, ConnectionTag: data}, streamingRespOption) if err != nil { newError("unable to process roundtrip").Base(err).AtInfo().WriteToLog() } if streamingRespOption.used { return } _, err = io.Copy(resp, bytes.NewReader(recvResp.Data)) if err != nil { newError("unable to send response").Base(err).AtInfo().WriteToLog() } } func (h *httpTripperServer) Start() error { listener, err := h.assembly.AutoImplListener().Listen(h.ctx) if err != nil { return newError("unable to create a listener for http tripper server").Base(err) } h.listener = listener go func() { httpServer := http.Server{ ReadHeaderTimeout: 240 * time.Second, ReadTimeout: 240 * time.Second, WriteTimeout: 240 * time.Second, IdleTimeout: 240 * time.Second, } httpServer.Handler = h err := httpServer.Serve(h.listener) if err != nil { newError("unable to serve listener for http tripper server").Base(err).WriteToLog() } }() return nil } func (h *httpTripperServer) Close() error { return h.listener.Close() } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { clientConfig, ok := config.(*ClientConfig) if !ok { return nil, newError("not a ClientConfig") } return newHTTPRoundTripperClient(ctx, clientConfig), nil })) common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { serverConfig, ok := config.(*ServerConfig) if !ok { return nil, newError("not a ServerConfig") } return newHTTPRoundTripperServer(ctx, serverConfig), nil })) } ================================================ FILE: transport/internet/request/roundtripper.go ================================================ package request import ( "context" "io" "github.com/v2fly/v2ray-core/v5/common" ) type RoundTripperClient interface { Tripper TransportClientAssemblyReceiver } type RoundTripperServer interface { common.Runnable TransportServerAssemblyReceiver } type Tripper interface { RoundTrip(ctx context.Context, req Request, opts ...RoundTripperOption) (resp Response, err error) } type TripperReceiver interface { OnRoundTrip(ctx context.Context, req Request, opts ...RoundTripperOption) (resp Response, err error) } type RoundTripperOption interface { RoundTripperOption() } type Request struct { Data []byte ConnectionTag []byte } type Response struct { Data []byte } type OptionSupportsStreamingResponse interface { RoundTripperOption GetResponseWriter() io.Writer } type OptionSupportsStreamingResponseExtensionFlusher interface { Flush() } ================================================ FILE: transport/internet/request/roundtripperreverserserver/accesschecker_password.go ================================================ package roundtripperreverserserver import ( "bytes" "context" "crypto/aes" "crypto/cipher" "crypto/hkdf" "crypto/sha256" "math" "github.com/v2fly/struc" ) type PasswordAccessChecker struct { Password string block cipher.Block } // NewPasswordAccessChecker creates a PasswordAccessChecker from the given password // and initializes its internal cipher block. It returns an error if initialization // fails. func NewPasswordAccessChecker(password string) (*PasswordAccessChecker, error) { p := &PasswordAccessChecker{Password: password} if err := p.init(); err != nil { return nil, err } return p, nil } type tokenUnpacked struct { UserID int64 `struc:"int64,little"` Check uint64 `struc:"uint64,little"` } func (p *PasswordAccessChecker) CheckReverserAccess(ctx context.Context, serverKey []byte) (clientKey []byte, err error) { if len(serverKey) != 16 { return nil, newError("invalid server key length") } buffer := make([]byte, 16) // Decrypt into buffer from serverKey p.block.Decrypt(buffer, serverKey) token := &tokenUnpacked{} err = struc.Unpack(bytes.NewReader(buffer), token) if err != nil { return nil, newError("failed to unpack token").Base(err) } expectedCheck := uint64(0) if token.Check != expectedCheck { return nil, newError("invalid token check value") } if token.UserID == int64(math.MinInt64) { return nil, newError("invalid token userID: MinInt64") } if token.UserID < 0 { return nil, newError("invalid token userID for server: client token has negative userID") } // pack client token into a buffer with capacity 16 buf := bytes.NewBuffer(make([]byte, 0, 16)) err = struc.Pack(buf, &tokenUnpacked{ UserID: -token.UserID, Check: expectedCheck, }) if err != nil { return nil, newError("failed to pack client token").Base(err) } clientKeyBuffer := buf.Bytes() if len(clientKeyBuffer) != 16 { return nil, newError("invalid packed client token length") } // encrypt into a fresh slice to avoid overlapping issues encrypted := make([]byte, 16) p.block.Encrypt(encrypted, clientKeyBuffer) return encrypted, nil } func (p *PasswordAccessChecker) GenerateToken(userID int64) ([]byte, error) { if userID == int64(math.MinInt64) { return nil, newError("userID cannot be MinInt64") } expectedCheck := uint64(0) // pack token into a buffer with capacity 16 buf := bytes.NewBuffer(make([]byte, 0, 16)) err := struc.Pack(buf, &tokenUnpacked{ UserID: userID, Check: expectedCheck, }) if err != nil { return nil, newError("failed to pack token").Base(err) } buffer := buf.Bytes() if len(buffer) != 16 { return nil, newError("invalid packed token length") } encrypted := make([]byte, 16) p.block.Encrypt(encrypted, buffer) return encrypted, nil } func (p *PasswordAccessChecker) init() error { block, err := createBlockFromPassword(p.Password) if err != nil { return newError("failed to create block from password").Base(err) } p.block = block return nil } func createBlockFromPassword(password string) (cipher.Block, error) { // derive a 16-byte key from the password with hkdf key, err := hkdf.Expand(sha256.New, []byte(password), "v2ray-hd87BYQL-aBzumdEh-Yv4E6Rdu:request-roundtripperreverserserver"+"createBlockFromPassword", 16) if err != nil { return nil, newError("unable to derive key from password").Base(err) } block, err := aes.NewCipher(key) if err != nil { return nil, newError("unable to create AES cipher").Base(err) } return block, nil } ================================================ FILE: transport/internet/request/roundtripperreverserserver/accesschecker_password_test.go ================================================ package roundtripperreverserserver import ( "bytes" "context" "math" "strings" "testing" "github.com/v2fly/struc" ) func TestGenerateTokenLength(t *testing.T) { p, err := NewPasswordAccessChecker("test-password") if err != nil { t.Fatalf("failed to create checker: %v", err) } tok, err := p.GenerateToken(12345) if err != nil { t.Fatalf("GenerateToken failed: %v", err) } if len(tok) != 16 { t.Fatalf("expected token length 16, got %d", len(tok)) } } func TestGenerateAndCheckRoundTrip(t *testing.T) { p, err := NewPasswordAccessChecker("another-secret") if err != nil { t.Fatalf("failed to create checker: %v", err) } userID := int64(42) serverKey, err := p.GenerateToken(userID) if err != nil { t.Fatalf("GenerateToken failed: %v", err) } clientKey, err := p.CheckReverserAccess(context.Background(), serverKey) if err != nil { t.Fatalf("CheckReverserAccess failed: %v", err) } if len(clientKey) != 16 { t.Fatalf("expected client key length 16, got %d", len(clientKey)) } // decrypt clientKey using internal block to inspect contents decrypted := make([]byte, 16) p.block.Decrypt(decrypted, clientKey) token := &tokenUnpacked{} if err := struc.Unpack(bytes.NewReader(decrypted), token); err != nil { t.Fatalf("failed to unpack client token: %v", err) } if token.Check != 0 { t.Fatalf("expected check 0, got %d", token.Check) } if token.UserID != -userID { t.Fatalf("expected userID %d, got %d", -userID, token.UserID) } } func TestPasswordAccessChecker_ValidRoundTrip(t *testing.T) { p, err := NewPasswordAccessChecker("test-password") if err != nil { t.Fatalf("NewPasswordAccessChecker failed: %v", err) } userID := int64(42) token, err := p.GenerateToken(userID) if err != nil { t.Fatalf("GenerateToken failed: %v", err) } clientKey, err := p.CheckReverserAccess(context.TODO(), token) if err != nil { t.Fatalf("CheckReverserAccess failed: %v", err) } // decrypt clientKey and verify contents buf := make([]byte, 16) p.block.Decrypt(buf, clientKey) tok := &tokenUnpacked{} if err := struc.Unpack(bytes.NewReader(buf), tok); err != nil { t.Fatalf("failed to unpack client token: %v", err) } if tok.Check != 0 { t.Fatalf("unexpected Check value: got %d want 0", tok.Check) } if tok.UserID != -userID { t.Fatalf("unexpected UserID: got %d want %d", tok.UserID, -userID) } } func TestPasswordAccessChecker_InvalidKeyLength(t *testing.T) { p, err := NewPasswordAccessChecker("test-password") if err != nil { t.Fatalf("NewPasswordAccessChecker failed: %v", err) } _, err = p.CheckReverserAccess(context.TODO(), []byte{1, 2, 3}) if err == nil { t.Fatalf("expected error for invalid key length, got nil") } if !strings.Contains(err.Error(), "invalid server key length") { t.Fatalf("unexpected error: %v", err) } } func TestPasswordAccessChecker_InvalidTokenCheckValue(t *testing.T) { p, err := NewPasswordAccessChecker("test-password") if err != nil { t.Fatalf("NewPasswordAccessChecker failed: %v", err) } // craft a token with non-zero Check buf := bytes.NewBuffer(make([]byte, 0, 16)) if err := struc.Pack(buf, &tokenUnpacked{UserID: 7, Check: 1}); err != nil { t.Fatalf("failed to pack token: %v", err) } plain := buf.Bytes() if len(plain) != 16 { t.Fatalf("unexpected packed length: %d", len(plain)) } encrypted := make([]byte, 16) p.block.Encrypt(encrypted, plain) _, err = p.CheckReverserAccess(context.TODO(), encrypted) if err == nil { t.Fatalf("expected error for invalid token check value, got nil") } if !strings.Contains(err.Error(), "invalid token check value") { t.Fatalf("unexpected error: %v", err) } } func TestTokensNotInterchangeable(t *testing.T) { p1, err := NewPasswordAccessChecker("password-one") if err != nil { t.Fatalf("failed to create checker p1: %v", err) } p2, err := NewPasswordAccessChecker("password-two") if err != nil { t.Fatalf("failed to create checker p2: %v", err) } userID := int64(7) // token created by p1 should not be accepted by p2 tok1, err := p1.GenerateToken(userID) if err != nil { t.Fatalf("p1.GenerateToken failed: %v", err) } if _, err := p2.CheckReverserAccess(context.Background(), tok1); err == nil { t.Fatalf("expected p2 to reject token generated by p1, but it accepted it") } // token created by p2 should not be accepted by p1 tok2, err := p2.GenerateToken(userID) if err != nil { t.Fatalf("p2.GenerateToken failed: %v", err) } if _, err := p1.CheckReverserAccess(context.Background(), tok2); err == nil { t.Fatalf("expected p1 to reject token generated by p2, but it accepted it") } } func TestCheckReverserAccess_NegativeUserID(t *testing.T) { p, err := NewPasswordAccessChecker("neg-test") if err != nil { t.Fatalf("failed to create checker: %v", err) } // craft a token with negative UserID and Check == 0 buf := bytes.NewBuffer(make([]byte, 0, 16)) if err := struc.Pack(buf, &tokenUnpacked{UserID: -5, Check: 0}); err != nil { t.Fatalf("failed to pack token: %v", err) } plain := buf.Bytes() if len(plain) != 16 { t.Fatalf("unexpected packed length: %d", len(plain)) } serverKey := make([]byte, 16) p.block.Encrypt(serverKey, plain) if _, err := p.CheckReverserAccess(context.Background(), serverKey); err == nil { t.Fatalf("expected error for negative userID token, got nil") } else if !strings.Contains(err.Error(), "invalid token userID for server") { t.Fatalf("unexpected error for negative userID token: %v", err) } } func TestCheckReverserAccess_MinInt64Token(t *testing.T) { p, err := NewPasswordAccessChecker("minint-test") if err != nil { t.Fatalf("failed to create checker: %v", err) } // craft a token with MinInt64 and Check == 0 buf := bytes.NewBuffer(make([]byte, 0, 16)) if err := struc.Pack(buf, &tokenUnpacked{UserID: math.MinInt64, Check: 0}); err != nil { t.Fatalf("failed to pack token: %v", err) } plain := buf.Bytes() if len(plain) != 16 { t.Fatalf("unexpected packed length: %d", len(plain)) } serverKey := make([]byte, 16) p.block.Encrypt(serverKey, plain) if _, err := p.CheckReverserAccess(context.Background(), serverKey); err == nil { t.Fatalf("expected error for MinInt64 token, got nil") } else if !strings.Contains(err.Error(), "invalid token userID: MinInt64") { t.Fatalf("unexpected error for MinInt64 token: %v", err) } } func TestGenerateTokensDifferentPasswords(t *testing.T) { p1, err := NewPasswordAccessChecker("pw-a") if err != nil { t.Fatalf("failed to create checker p1: %v", err) } p2, err := NewPasswordAccessChecker("pw-b") if err != nil { t.Fatalf("failed to create checker p2: %v", err) } userID := int64(100) tok1, err := p1.GenerateToken(userID) if err != nil { t.Fatalf("p1.GenerateToken failed: %v", err) } tok2, err := p2.GenerateToken(userID) if err != nil { t.Fatalf("p2.GenerateToken failed: %v", err) } if bytes.Equal(tok1, tok2) { t.Fatalf("expected tokens from different passwords to differ, but they are equal") } } func TestNewPasswordAccessChecker_EmptyPassword(t *testing.T) { // ensure creating a checker with empty password succeeds and block is set p, err := NewPasswordAccessChecker("") if err != nil { t.Fatalf("failed to create checker with empty password: %v", err) } if p.block == nil { t.Fatalf("expected block to be initialized for empty password") } } ================================================ FILE: transport/internet/request/roundtripperreverserserver/clicommand/errors.generated.go ================================================ package clicommand import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/request/roundtripperreverserserver/clicommand/generate_token_cli.go ================================================ package clicommand import ( "encoding/base64" "flag" "fmt" "os" "strconv" "github.com/v2fly/v2ray-core/v5/main/commands/all/engineering" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripperreverserserver" ) var ( genPassword *string genPasswordShort *string genUser *string genOut *string ) var cmdGenerateToken = &base.Command{ UsageLine: "{{.Exec}} engineering request-rtt-reverser-gen-token", Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) // user-facing flag name: access passphrase genPassword = fs.String("access-passphrase", "", "access passphrase used to derive token key (required)") // short alias for convenience genPasswordShort = fs.String("p", "", "access passphrase (shorthand)") genUser = fs.String("u", "", "userID (required, int64, positive)") genOut = fs.String("o", "", "write base64 tokens to file (optional)") return *fs }(), Run: func(cmd *base.Command, args []string) { if err := cmd.Flag.Parse(args); err != nil { base.Fatalf("failed to parse flags: %v", err) } // require password (either long form or short alias) var passphrase string if genPasswordShort != nil && *genPasswordShort != "" { passphrase = *genPasswordShort } else if genPassword != nil && *genPassword != "" { passphrase = *genPassword } else { base.Fatalf("-access-passphrase (or -p) is required") } if *genUser == "" { base.Fatalf("-u userID is required") } id, err := strconv.ParseInt(*genUser, 10, 64) if err != nil { base.Fatalf("invalid userID %q: %v", *genUser, err) } // require positive userID if id <= 0 { base.Fatalf("userID must be a positive integer") } checker, err := roundtripperreverserserver.NewPasswordAccessChecker(passphrase) if err != nil { base.Fatalf("failed to create PasswordAccessChecker: %v", err) } // generate private (positive) and public (negative) tokens privToken, err := checker.GenerateToken(id) if err != nil { base.Fatalf("GenerateToken (private) failed: %v", err) } pubToken, err := checker.GenerateToken(-id) if err != nil { base.Fatalf("GenerateToken (public) failed: %v", err) } b64Priv := base64.StdEncoding.EncodeToString(privToken) b64Pub := base64.StdEncoding.EncodeToString(pubToken) if *genOut != "" { content := fmt.Sprintf("private: %s\npublic: %s\n", b64Priv, b64Pub) if err := os.WriteFile(*genOut, []byte(content), 0o600); err != nil { base.Fatalf("failed to write tokens to file %q: %v", *genOut, err) } if _, err := fmt.Fprintf(os.Stdout, "wrote base64 tokens to %s\n", *genOut); err != nil { base.Fatalf("failed to write token message: %v", err) } return } // print both tokens to stdout fmt.Printf("private: %s\npublic: %s\n", b64Priv, b64Pub) }, } func init() { engineering.AddCommand(cmdGenerateToken) } ================================================ FILE: transport/internet/request/roundtripperreverserserver/clicommand/roundTripperReserseServerCli.go ================================================ package clicommand import ( "bytes" "context" "flag" "os" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl" "github.com/v2fly/v2ray-core/v5/main/commands/all/engineering" "github.com/v2fly/v2ray-core/v5/main/commands/base" "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripperreverserserver" "google.golang.org/protobuf/encoding/protojson" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen var config *string var cmdRTTReverseServer = &base.Command{ UsageLine: "{{.Exec}} engineering request-rtt-reverse-server", Flag: func() flag.FlagSet { fs := flag.NewFlagSet("", flag.ExitOnError) config = fs.String("c", "", "") return *fs }(), Run: func(cmd *base.Command, args []string) { err := cmd.Flag.Parse(args) if err != nil { base.Fatalf("failed to parse flags: %v", err) } fd, err := os.Open(*config) if err != nil { base.Fatalf("failed to open config file %q: %v", *config, err) } defer func() { _ = fd.Close() }() content := bytes.NewBuffer(nil) _, err = content.ReadFrom(fd) if err != nil { base.Fatalf("failed to read config file %q: %v", *config, err) } var reverserConfig roundtripperreverserserver.Config err = protojson.Unmarshal(content.Bytes(), &reverserConfig) if err != nil { base.Fatalf("failed to unmarshal JSON config from %q: %v", *config, err) } ctx := context.Background() systemNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() ctx = envctx.ContextWithEnvironment(ctx, systemNetworkImpl) server, err := roundtripperreverserserver.NewReverser(ctx, &reverserConfig) if err != nil { base.Fatalf("failed to create RTT reverse server: %v", err) } _ = server select {} }, } func init() { engineering.AddCommand(cmdRTTReverseServer) } ================================================ FILE: transport/internet/request/roundtripperreverserserver/config.pb.go ================================================ package roundtripperreverserserver import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` RoundTripperServer *anypb.Any `protobuf:"bytes,2,opt,name=round_tripper_server,json=roundTripperServer,proto3" json:"round_tripper_server,omitempty"` Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"` AccessPassphrase string `protobuf:"bytes,4,opt,name=access_passphrase,json=accessPassphrase,proto3" json:"access_passphrase,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_request_roundtripperreverserserver_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_roundtripperreverserserver_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_request_roundtripperreverserserver_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetRoundTripperServer() *anypb.Any { if x != nil { return x.RoundTripperServer } return nil } func (x *Config) GetListen() string { if x != nil { return x.Listen } return "" } func (x *Config) GetAccessPassphrase() string { if x != nil { return x.AccessPassphrase } return "" } var File_transport_internet_request_roundtripperreverserserver_config_proto protoreflect.FileDescriptor const file_transport_internet_request_roundtripperreverserserver_config_proto_rawDesc = "" + "\n" + "Btransport/internet/request/roundtripperreverserserver/config.proto\x12@v2ray.core.transport.internet.request.roundtripperreverserserver\x1a common/protoext/extensions.proto\x1a\x19google/protobuf/any.proto\"\xc9\x01\n" + "\x06Config\x12F\n" + "\x14round_tripper_server\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x12roundTripperServer\x12\x16\n" + "\x06listen\x18\x03 \x01(\tR\x06listen\x12+\n" + "\x11access_passphrase\x18\x04 \x01(\tR\x10accessPassphrase:2\x82\xb5\x18.\n" + ",transport.request.roundtripperreverserserverB\xe0\x01\n" + "Dcom.v2ray.core.transport.internet.request.roundtripperreverserserverP\x01ZTgithub.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripperreverserserver\xaa\x02?V2Ray.Core.Transport.Internet.Request.RoundtripperReverseServerb\x06proto3" var ( file_transport_internet_request_roundtripperreverserserver_config_proto_rawDescOnce sync.Once file_transport_internet_request_roundtripperreverserserver_config_proto_rawDescData []byte ) func file_transport_internet_request_roundtripperreverserserver_config_proto_rawDescGZIP() []byte { file_transport_internet_request_roundtripperreverserserver_config_proto_rawDescOnce.Do(func() { file_transport_internet_request_roundtripperreverserserver_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_request_roundtripperreverserserver_config_proto_rawDesc), len(file_transport_internet_request_roundtripperreverserserver_config_proto_rawDesc))) }) return file_transport_internet_request_roundtripperreverserserver_config_proto_rawDescData } var file_transport_internet_request_roundtripperreverserserver_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_request_roundtripperreverserserver_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.request.roundtripperreverserserver.Config (*anypb.Any)(nil), // 1: google.protobuf.Any } var file_transport_internet_request_roundtripperreverserserver_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.transport.internet.request.roundtripperreverserserver.Config.round_tripper_server:type_name -> google.protobuf.Any 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_request_roundtripperreverserserver_config_proto_init() } func file_transport_internet_request_roundtripperreverserserver_config_proto_init() { if File_transport_internet_request_roundtripperreverserserver_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_request_roundtripperreverserserver_config_proto_rawDesc), len(file_transport_internet_request_roundtripperreverserserver_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_request_roundtripperreverserserver_config_proto_goTypes, DependencyIndexes: file_transport_internet_request_roundtripperreverserserver_config_proto_depIdxs, MessageInfos: file_transport_internet_request_roundtripperreverserserver_config_proto_msgTypes, }.Build() File_transport_internet_request_roundtripperreverserserver_config_proto = out.File file_transport_internet_request_roundtripperreverserserver_config_proto_goTypes = nil file_transport_internet_request_roundtripperreverserserver_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/request/roundtripperreverserserver/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.request.roundtripperreverserserver; option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.RoundtripperReverseServer"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripperreverserserver"; option java_package = "com.v2ray.core.transport.internet.request.roundtripperreverserserver"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "google/protobuf/any.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "transport.request.roundtripperreverserserver"; google.protobuf.Any round_tripper_server = 2; string listen = 3; string access_passphrase = 4; } ================================================ FILE: transport/internet/request/roundtripperreverserserver/errors.generated.go ================================================ package roundtripperreverserserver import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/request/roundtripperreverserserver/reverser.go ================================================ package roundtripperreverserserver import ( "context" "net" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" v2net "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func NewReverser(ctx context.Context, config *Config) (*Reverser, error) { reverser := &Reverser{ ctx: ctx, config: config, } if err := reverser.init(); err != nil { return nil, newError("failed to initialize Reverser").Base(err).AtError() } return reverser, nil } type Reverser struct { ctx context.Context config *Config rttServer request.RoundTripperServer reverser request.ReverserImpl accessChecker request.ReverserAccessChecker } func (s *Reverser) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) { serverIntent := len(req.ConnectionTag) == 16 if serverIntent { serverPublic, err := s.accessChecker.CheckReverserAccess(ctx, req.ConnectionTag) if err != nil { return request.Response{}, newError("reverser access check failed").Base(err).AtError() } reverserImpl, err := s.reverser.OnAuthenticatedServerIntentRoundTrip(ctx, serverPublic, req, opts...) if err != nil { return request.Response{}, newError("failed to handle authenticated server round trip").Base(err).AtError() } return reverserImpl, nil } if len(req.ConnectionTag) != 32 { return request.Response{}, newError("invalid ConnectionTag length") } reverserImpl, err := s.reverser.OnOtherRoundTrip(ctx, req, opts...) if err != nil { return request.Response{}, newError("failed to handle client round trip").Base(err).AtError() } return reverserImpl, nil } func (s *Reverser) Listen(ctx context.Context) (v2net.Listener, error) { systemNetworkCapabilitySet := envctx.EnvironmentFromContext(s.ctx).(environment.SystemNetworkCapabilitySet) listener := systemNetworkCapabilitySet.Listener() addr, err := v2net.ParseDestination(s.config.Listen) if err != nil { return nil, newError("invalid listen address " + s.config.Listen).Base(err).AtError() } netaddr := &net.TCPAddr{IP: addr.Address.IP(), Port: int(addr.Port)} l, err := listener.Listen(ctx, netaddr, nil) if err != nil { return nil, newError("failed to listen on " + s.config.Listen).Base(err).AtError() } return l, nil } func (s *Reverser) init() error { if s.config == nil { return newError("nil ServerConfig") } if s.config.AccessPassphrase == "" { return newError("empty AccessPassphrase in ServerConfig") } accessChecker, err := NewPasswordAccessChecker(s.config.AccessPassphrase) if err != nil { return newError("failed to create AccessChecker").Base(err).AtError() } s.accessChecker = accessChecker ReverserImplInst, err := NewReverserImpl() if err != nil { return newError("failed to create ReverserImpl").Base(err).AtError() } s.reverser = ReverserImplInst if s.config.RoundTripperServer == nil { return newError("nil RoundTripperServer in ServerConfig") } RoundTripperServerConfig, err := serial.GetInstanceOf(s.config.RoundTripperServer) if err != nil { return newError("failed to get instance of RoundTripperServer").Base(err).AtError() } RoundTripperServerObj, err := common.CreateObject(s.ctx, RoundTripperServerConfig) if err != nil { return newError("failed to create RoundTripperServer").Base(err).AtError() } RoundTripperServerTyped, ok := RoundTripperServerObj.(request.RoundTripperServer) if !ok { return newError("RoundTripperServer is not a valid request.RoundTripperServer") } s.rttServer = RoundTripperServerTyped s.rttServer.OnTransportServerAssemblyReady(s) if err := s.rttServer.Start(); err != nil { return newError("failed to start RoundTripperServer").Base(err).AtError() } return nil } func (s *Reverser) TripperReceiver() request.TripperReceiver { return s } func (s *Reverser) SessionReceiver() request.SessionReceiver { return nil } func (s *Reverser) AutoImplListener() request.Listener { return s } ================================================ FILE: transport/internet/request/roundtripperreverserserver/reverserimpl.go ================================================ package roundtripperreverserserver import ( "context" "sync" "time" "github.com/v2fly/v2ray-core/v5/common/task" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) func NewReverserImpl() (request.ReverserImpl, error) { r := &ReverserImpl{ timeoutDuration: 5 * time.Minute, } // configure periodic cleaner r.periodicCleaner = &task.Periodic{ Interval: time.Second * 30, Execute: func() error { now := time.Now() r.serverPublicKeyToStateMap.Range(func(k, v interface{}) bool { ss, ok := v.(*serverState) if !ok { return true } if now.Sub(ss.lastSeen) > r.timeoutDuration { r.serverPublicKeyToStateMap.Delete(k) r.serverPrivateKeyToPublicKey.Delete(ss.privateKey) } return true }) return nil }, } // start the cleaner; return error if Start fails if err := r.periodicCleaner.Start(); err != nil { return nil, err } return r, nil } type ReverserImpl struct { serverPublicKeyToStateMap sync.Map serverPrivateKeyToPublicKey sync.Map clientTemporaryKeyToStateMap sync.Map // cleanup fields periodicCleaner *task.Periodic timeoutDuration time.Duration } // StopCleanup stops the periodic cleaner (if running). func (r *ReverserImpl) StopCleanup() error { if r.periodicCleaner == nil { return nil } return r.periodicCleaner.Close() } func (r *ReverserImpl) OnOtherRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) { _ = ctx _ = opts routingKey := req.ConnectionTag if len(routingKey) != 32 { return request.Response{}, newError("invalid routing key") } sourceKey := routingKey[:16] destKey := routingKey[16:] if _, ok := r.serverPrivateKeyToPublicKey.Load(string(sourceKey)); ok { stateInterface, clientOk := r.clientTemporaryKeyToStateMap.Load(string(destKey)) if clientOk { state := stateInterface.(*clientState) message := &reverserMessage{ Data: req.Data, } select { case state.messageQueue <- message: default: return request.Response{}, newError("client message queue full") } return request.Response{}, nil } return request.Response{}, newError("no client found for the given routing key") } // try if this is a client to server message stateInterface, _ := r.clientTemporaryKeyToStateMap.LoadOrStore(string(sourceKey), &clientState{ messageQueue: make(chan *reverserMessage, 1), }) state := stateInterface.(*clientState) defer func() { r.clientTemporaryKeyToStateMap.Delete(string(sourceKey)) }() serverStateInterface, ok := r.serverPublicKeyToStateMap.Load(string(destKey)) if ok { serverStateInst := serverStateInterface.(*serverState) message := &reverserMessage{ Data: req.Data, } timeOutTimer := time.NewTimer(time.Second * 25) select { case serverStateInst.messageQueue <- message: case <-timeOutTimer.C: return request.Response{}, newError("server message queue full timeout") } select { case respMessage := <-state.messageQueue: timeOutTimer.Stop() return request.Response{ Data: respMessage.Data, }, nil case <-timeOutTimer.C: return request.Response{}, newError("client message queue empty timeout") } } return request.Response{}, newError("no server found for the given routing key") } func (r *ReverserImpl) OnAuthenticatedServerIntentRoundTrip(ctx context.Context, serverPublic []byte, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) { _ = ctx _ = opts if len(req.ConnectionTag) != 16 { return request.Response{}, newError("invalid server private key") } if len(serverPublic) != 16 { return request.Response{}, newError("invalid server public key") } serverPrivate := req.ConnectionTag // store mapping from private -> public r.serverPrivateKeyToPublicKey.Store(string(serverPrivate), string(serverPublic)) stateInterface, _ := r.serverPublicKeyToStateMap.LoadOrStore(string(serverPublic), &serverState{ messageQueue: make(chan *reverserMessage, 16), lastSeen: time.Now(), }) state := stateInterface.(*serverState) state.lastSeen = time.Now() timeOutTimer := time.NewTimer(time.Minute) select { case message := <-state.messageQueue: timeOutTimer.Stop() return request.Response{ Data: message.Data, }, nil case <-timeOutTimer.C: return request.Response{}, nil } } func (r *ReverserImpl) __CleanupNow____TestOnly() { // trigger cleanup immediately for testing purposes r.periodicCleaner.Execute() } type clientState struct { messageQueue chan *reverserMessage } type serverState struct { messageQueue chan *reverserMessage lastSeen time.Time privateKey string } type reverserMessage struct { Data []byte } ================================================ FILE: transport/internet/request/roundtripperreverserserver/reverserimpl_test.go ================================================ package roundtripperreverserserver import ( "context" "strings" "testing" "time" "github.com/v2fly/v2ray-core/v5/transport/internet/request" ) // helper to make a byte slice of given length filled with a pattern func fill(b byte, n int) []byte { res := make([]byte, n) for i := range res { res[i] = b } return res } // stopCleanup tries to call StopCleanup on concrete *ReverserImpl returned by NewReverserImpl. func stopCleanup(t *testing.T, r request.ReverserImpl) { if r == nil { return } if impl, ok := r.(*ReverserImpl); ok { if err := impl.StopCleanup(); err != nil { t.Fatalf("StopCleanup failed: %v", err) } } else { t.Fatalf("expected *ReverserImpl, got %T", r) } } func TestOnOtherRoundTrip_InvalidRoutingKeyLength(t *testing.T) { r, err := NewReverserImpl() if err != nil { // constructor currently never errors t.Fatalf("unexpected constructor error: %v", err) } defer stopCleanup(t, r) _, gotErr := r.OnOtherRoundTrip(context.Background(), request.Request{ConnectionTag: []byte("short")}) if gotErr == nil { t.Fatalf("expected error for invalid routing key length") } if !strings.Contains(gotErr.Error(), "invalid routing key") { t.Fatalf("unexpected error: %v", gotErr) } } func TestOnOtherRoundTrip_ServerToClient_NoClientFound(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) source := fill('A', 16) dest := fill('B', 16) impl.serverPrivateKeyToPublicKey.Store(string(source), string(source)) _, err := impl.OnOtherRoundTrip(context.Background(), request.Request{ConnectionTag: append(source, dest...)}) if err == nil || !strings.Contains(err.Error(), "no client found") { t.Fatalf("expected no client found error, got: %v", err) } } func TestOnOtherRoundTrip_ServerToClient_Success(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) source := fill('S', 16) dest := fill('C', 16) impl.serverPrivateKeyToPublicKey.Store(string(source), string(source)) clientState := &clientState{messageQueue: make(chan *reverserMessage, 1)} impl.clientTemporaryKeyToStateMap.Store(string(dest), clientState) data := []byte("hello-client") _, err := impl.OnOtherRoundTrip(context.Background(), request.Request{ConnectionTag: append(source, dest...), Data: data}) if err != nil { t.Fatalf("unexpected error: %v", err) } select { case msg := <-clientState.messageQueue: if string(msg.Data) != string(data) { t.Fatalf("unexpected message data: got %q want %q", msg.Data, data) } default: t.Fatalf("expected message queued for client but queue empty") } } func TestOnOtherRoundTrip_ServerToClient_QueueFull(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) source := fill('F', 16) dest := fill('Q', 16) impl.serverPrivateKeyToPublicKey.Store(string(source), string(source)) clientState := &clientState{messageQueue: make(chan *reverserMessage, 1)} // pre-fill queue so next send fails non-blocking clientState.messageQueue <- &reverserMessage{Data: []byte("prefill")} impl.clientTemporaryKeyToStateMap.Store(string(dest), clientState) _, err := impl.OnOtherRoundTrip(context.Background(), request.Request{ConnectionTag: append(source, dest...), Data: []byte("new")}) if err == nil || !strings.Contains(err.Error(), "client message queue full") { t.Fatalf("expected queue full error, got: %v", err) } } func TestOnOtherRoundTrip_ClientToServer_NoServerFound(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) source := fill('X', 16) dest := fill('Y', 16) _, err := impl.OnOtherRoundTrip(context.Background(), request.Request{ConnectionTag: append(source, dest...), Data: []byte("ping")}) if err == nil || !strings.Contains(err.Error(), "no server found") { t.Fatalf("expected no server found error, got: %v", err) } } func TestOnOtherRoundTrip_ClientToServer_Success(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) source := fill('1', 16) dest := fill('2', 16) serverState := &serverState{messageQueue: make(chan *reverserMessage, 1)} impl.serverPublicKeyToStateMap.Store(string(dest), serverState) clientState := &clientState{messageQueue: make(chan *reverserMessage, 1)} clientState.messageQueue <- &reverserMessage{Data: []byte("pong")} impl.clientTemporaryKeyToStateMap.Store(string(source), clientState) data := []byte("ping") resp, err := impl.OnOtherRoundTrip(context.Background(), request.Request{ConnectionTag: append(source, dest...), Data: data}) if err != nil { t.Fatalf("unexpected error: %v", err) } // verify server received the original message select { case msg := <-serverState.messageQueue: if string(msg.Data) != string(data) { t.Fatalf("server message mismatch: got %q want %q", msg.Data, data) } default: t.Fatalf("expected server to receive message but queue empty") } if string(resp.Data) != "pong" { t.Fatalf("unexpected response data: got %q want %q", resp.Data, "pong") } } func TestOnAuthenticatedServerIntentRoundTrip_InvalidPrivateKeyLength(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) serverPublic := fill('P', 16) _, err := impl.OnAuthenticatedServerIntentRoundTrip(context.Background(), serverPublic, request.Request{ConnectionTag: fill('Z', 15)}) if err == nil || !strings.Contains(err.Error(), "invalid server private key") { t.Fatalf("expected invalid server private key error, got: %v", err) } } func TestOnAuthenticatedServerIntentRoundTrip_InvalidPublicKeyLength(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) _, err := impl.OnAuthenticatedServerIntentRoundTrip(context.Background(), fill('P', 15), request.Request{ConnectionTag: fill('K', 16)}) if err == nil || !strings.Contains(err.Error(), "invalid server public key") { t.Fatalf("expected invalid server public key error, got: %v", err) } } func TestOnAuthenticatedServerIntentRoundTrip_SuccessMessage(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) serverPublic := fill('P', 16) serverPrivate := fill('K', 16) state := &serverState{messageQueue: make(chan *reverserMessage, 1)} state.messageQueue <- &reverserMessage{Data: []byte("welcome")} impl.serverPublicKeyToStateMap.Store(string(serverPublic), state) resp, err := impl.OnAuthenticatedServerIntentRoundTrip(context.Background(), serverPublic, request.Request{ConnectionTag: serverPrivate}) if err != nil { t.Fatalf("unexpected error: %v", err) } if string(resp.Data) != "welcome" { t.Fatalf("unexpected response data: got %q want %q", resp.Data, "welcome") } if _, ok := impl.serverPrivateKeyToPublicKey.Load(string(serverPrivate)); !ok { t.Fatalf("expected server private key mapping to be stored") } } func TestSmoke(t *testing.T) { if _, err := NewReverserImpl(); err != nil { t.Fatalf("unexpected error constructing reverser: %v", err) } } func TestPeriodicCleanupRemovesOldServer(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) // configure short durations for test speed impl.timeoutDuration = 10 * time.Millisecond impl.periodicCleaner.Interval = 5 * time.Millisecond // insert a server state with lastSeen far in the past old := &serverState{messageQueue: make(chan *reverserMessage, 1), lastSeen: time.Now().Add(-time.Hour)} impl.serverPublicKeyToStateMap.Store("old-server", old) // trigger cleanup now impl.__CleanupNow____TestOnly() if _, ok := impl.serverPublicKeyToStateMap.Load("old-server"); ok { t.Fatalf("expected old server entry to be removed by cleaner") } } func TestCleanupNowRemovesServerAndPrivateMapping(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) // create an old server entry with private key present in private->public map publicKey := "old-server-public" privateKey := "old-server-private" old := &serverState{messageQueue: make(chan *reverserMessage, 1), lastSeen: time.Now().Add(-time.Hour), privateKey: privateKey} impl.serverPublicKeyToStateMap.Store(publicKey, old) impl.serverPrivateKeyToPublicKey.Store(privateKey, publicKey) // trigger immediate cleanup impl.__CleanupNow____TestOnly() if _, ok := impl.serverPublicKeyToStateMap.Load(publicKey); ok { t.Fatalf("expected old server entry to be removed by cleaner via CleanupNow") } if _, ok := impl.serverPrivateKeyToPublicKey.Load(privateKey); ok { t.Fatalf("expected old private->public mapping to be removed by cleaner via CleanupNow") } } func TestStopCleanupIdempotent(t *testing.T) { r, _ := NewReverserImpl() impl := r.(*ReverserImpl) // first stop if err := impl.StopCleanup(); err != nil { t.Fatalf("StopCleanup failed: %v", err) } // second stop should not panic or return error if err := impl.StopCleanup(); err != nil { t.Fatalf("StopCleanup second call failed: %v", err) } } func TestOnAuthenticatedServerIntentRoundTrip_UpdatesLastSeenAndMapping(t *testing.T) { r, _ := NewReverserImpl() defer stopCleanup(t, r) impl := r.(*ReverserImpl) serverPublic := fill('P', 16) serverPrivate := fill('K', 16) // pre-insert a server state that has an old lastSeen but contains a queued message state := &serverState{messageQueue: make(chan *reverserMessage, 1), lastSeen: time.Now().Add(-time.Hour)} state.messageQueue <- &reverserMessage{Data: []byte("welcome-back")} impl.serverPublicKeyToStateMap.Store(string(serverPublic), state) resp, err := impl.OnAuthenticatedServerIntentRoundTrip(context.Background(), serverPublic, request.Request{ConnectionTag: serverPrivate}) if err != nil { t.Fatalf("unexpected error: %v", err) } if string(resp.Data) != "welcome-back" { t.Fatalf("unexpected response data: got %q want %q", resp.Data, "welcome-back") } // mapping from private -> public should be stored if v, ok := impl.serverPrivateKeyToPublicKey.Load(string(serverPrivate)); !ok || v != string(serverPublic) { t.Fatalf("expected private->public mapping stored, got %v (ok=%v)", v, ok) } // lastSeen should be updated to a recent time si, ok := impl.serverPublicKeyToStateMap.Load(string(serverPublic)) if !ok { t.Fatalf("expected server state to still exist after intent call") } ss := si.(*serverState) if time.Since(ss.lastSeen) > time.Second*2 { t.Fatalf("expected lastSeen to be updated recently, got %v", ss.lastSeen) } } ================================================ FILE: transport/internet/request/stereotype/meek/config.pb.go ================================================ package meek import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_request_stereotype_meek_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_stereotype_meek_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_request_stereotype_meek_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetUrl() string { if x != nil { return x.Url } return "" } var File_transport_internet_request_stereotype_meek_config_proto protoreflect.FileDescriptor const file_transport_internet_request_stereotype_meek_config_proto_rawDesc = "" + "\n" + "7transport/internet/request/stereotype/meek/config.proto\x125v2ray.core.transport.internet.request.stereotype.meek\x1a common/protoext/extensions.proto\"5\n" + "\x06Config\x12\x10\n" + "\x03url\x18\x01 \x01(\tR\x03url:\x19\x82\xb5\x18\x15\n" + "\ttransport\x12\x04meek\x90\xff)\x01B\xc0\x01\n" + "9com.v2ray.core.transport.internet.request.stereotype.meekP\x01ZIgithub.com/v2fly/v2ray-core/v5/transport/internet/request/stereotype/meek\xaa\x025V2Ray.Core.Transport.Internet.Request.Stereotype.Meekb\x06proto3" var ( file_transport_internet_request_stereotype_meek_config_proto_rawDescOnce sync.Once file_transport_internet_request_stereotype_meek_config_proto_rawDescData []byte ) func file_transport_internet_request_stereotype_meek_config_proto_rawDescGZIP() []byte { file_transport_internet_request_stereotype_meek_config_proto_rawDescOnce.Do(func() { file_transport_internet_request_stereotype_meek_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_request_stereotype_meek_config_proto_rawDesc), len(file_transport_internet_request_stereotype_meek_config_proto_rawDesc))) }) return file_transport_internet_request_stereotype_meek_config_proto_rawDescData } var file_transport_internet_request_stereotype_meek_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_request_stereotype_meek_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.request.stereotype.meek.Config } var file_transport_internet_request_stereotype_meek_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_request_stereotype_meek_config_proto_init() } func file_transport_internet_request_stereotype_meek_config_proto_init() { if File_transport_internet_request_stereotype_meek_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_request_stereotype_meek_config_proto_rawDesc), len(file_transport_internet_request_stereotype_meek_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_request_stereotype_meek_config_proto_goTypes, DependencyIndexes: file_transport_internet_request_stereotype_meek_config_proto_depIdxs, MessageInfos: file_transport_internet_request_stereotype_meek_config_proto_msgTypes, }.Build() File_transport_internet_request_stereotype_meek_config_proto = out.File file_transport_internet_request_stereotype_meek_config_proto_goTypes = nil file_transport_internet_request_stereotype_meek_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/request/stereotype/meek/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.request.stereotype.meek; option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.Stereotype.Meek"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/stereotype/meek"; option java_package = "com.v2ray.core.transport.internet.request.stereotype.meek"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Config{ option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "meek"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; string url = 1; } ================================================ FILE: transport/internet/request/stereotype/meek/errors.generated.go ================================================ package meek import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/request/stereotype/meek/meek.go ================================================ package meek import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/simple" "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembly" "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripper/httprt" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const protocolName = "meek" func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return nil, newError("meek is a transport") })) common.Must(internet.RegisterTransportDialer(protocolName, meekDial)) common.Must(internet.RegisterTransportListener(protocolName, meekListen)) } func meekDial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { meekSetting := streamSettings.ProtocolSettings.(*Config) simpleAssembler := &simple.ClientConfig{ MaxWriteSize: 65536, WaitSubsequentWriteMs: 10, InitialPollingIntervalMs: 100, MaxPollingIntervalMs: 1000, MinPollingIntervalMs: 10, BackoffFactor: 1.5, FailedRetryIntervalMs: 1000, } httprtSetting := &httprt.ClientConfig{ Http: &httprt.HTTPConfig{ UrlPrefix: meekSetting.Url, }, } request := &assembly.Config{ Assembler: serial.ToTypedMessage(simpleAssembler), Roundtripper: serial.ToTypedMessage(httprtSetting), } constructedSetting := &internet.MemoryStreamConfig{ ProtocolName: "request", ProtocolSettings: request, SecurityType: streamSettings.SecurityType, SecuritySettings: streamSettings.SecuritySettings, SocketSettings: streamSettings.SocketSettings, } return internet.Dial(ctx, dest, constructedSetting) } func meekListen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, callback internet.ConnHandler) (internet.Listener, error) { meekSetting := streamSettings.ProtocolSettings.(*Config) simpleAssembler := &simple.ServerConfig{MaxWriteSize: 65536} httprtSetting := &httprt.ServerConfig{NoDecodingSessionTag: true, Http: &httprt.HTTPConfig{UrlPrefix: meekSetting.Url}} request := &assembly.Config{ Assembler: serial.ToTypedMessage(simpleAssembler), Roundtripper: serial.ToTypedMessage(httprtSetting), } constructedSetting := &internet.MemoryStreamConfig{ ProtocolName: "request", ProtocolSettings: request, SecurityType: streamSettings.SecurityType, SecuritySettings: streamSettings.SecuritySettings, SocketSettings: streamSettings.SocketSettings, } return internet.ListenTCP(ctx, address, port, constructedSetting, callback) } ================================================ FILE: transport/internet/request/stereotype/mekya/config.pb.go ================================================ package mekya import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" kcp "github.com/v2fly/v2ray-core/v5/transport/internet/kcp" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Kcp *kcp.Config `protobuf:"bytes,1,opt,name=kcp,proto3" json:"kcp,omitempty"` // Client MaxWriteDelay int32 `protobuf:"varint,1003,opt,name=max_write_delay,json=maxWriteDelay,proto3" json:"max_write_delay,omitempty"` MaxRequestSize int32 `protobuf:"varint,1004,opt,name=max_request_size,json=maxRequestSize,proto3" json:"max_request_size,omitempty"` PollingIntervalInitial int32 `protobuf:"varint,1005,opt,name=polling_interval_initial,json=pollingIntervalInitial,proto3" json:"polling_interval_initial,omitempty"` // Server MaxWriteSize int32 `protobuf:"varint,2003,opt,name=max_write_size,json=maxWriteSize,proto3" json:"max_write_size,omitempty"` MaxWriteDurationMs int32 `protobuf:"varint,2004,opt,name=max_write_duration_ms,json=maxWriteDurationMs,proto3" json:"max_write_duration_ms,omitempty"` MaxSimultaneousWriteConnection int32 `protobuf:"varint,2005,opt,name=max_simultaneous_write_connection,json=maxSimultaneousWriteConnection,proto3" json:"max_simultaneous_write_connection,omitempty"` PacketWritingBuffer int32 `protobuf:"varint,2006,opt,name=packet_writing_buffer,json=packetWritingBuffer,proto3" json:"packet_writing_buffer,omitempty"` // Roundtripper Url string `protobuf:"bytes,3001,opt,name=url,proto3" json:"url,omitempty"` H2PoolSize int32 `protobuf:"varint,3003,opt,name=h2_pool_size,json=h2PoolSize,proto3" json:"h2_pool_size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_request_stereotype_mekya_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_request_stereotype_mekya_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_request_stereotype_mekya_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetKcp() *kcp.Config { if x != nil { return x.Kcp } return nil } func (x *Config) GetMaxWriteDelay() int32 { if x != nil { return x.MaxWriteDelay } return 0 } func (x *Config) GetMaxRequestSize() int32 { if x != nil { return x.MaxRequestSize } return 0 } func (x *Config) GetPollingIntervalInitial() int32 { if x != nil { return x.PollingIntervalInitial } return 0 } func (x *Config) GetMaxWriteSize() int32 { if x != nil { return x.MaxWriteSize } return 0 } func (x *Config) GetMaxWriteDurationMs() int32 { if x != nil { return x.MaxWriteDurationMs } return 0 } func (x *Config) GetMaxSimultaneousWriteConnection() int32 { if x != nil { return x.MaxSimultaneousWriteConnection } return 0 } func (x *Config) GetPacketWritingBuffer() int32 { if x != nil { return x.PacketWritingBuffer } return 0 } func (x *Config) GetUrl() string { if x != nil { return x.Url } return "" } func (x *Config) GetH2PoolSize() int32 { if x != nil { return x.H2PoolSize } return 0 } var File_transport_internet_request_stereotype_mekya_config_proto protoreflect.FileDescriptor const file_transport_internet_request_stereotype_mekya_config_proto_rawDesc = "" + "\n" + "8transport/internet/request/stereotype/mekya/config.proto\x126v2ray.core.transport.internet.request.stereotype.mekya\x1a common/protoext/extensions.proto\x1a#transport/internet/kcp/config.proto\"\x82\x04\n" + "\x06Config\x12;\n" + "\x03kcp\x18\x01 \x01(\v2).v2ray.core.transport.internet.kcp.ConfigR\x03kcp\x12'\n" + "\x0fmax_write_delay\x18\xeb\a \x01(\x05R\rmaxWriteDelay\x12)\n" + "\x10max_request_size\x18\xec\a \x01(\x05R\x0emaxRequestSize\x129\n" + "\x18polling_interval_initial\x18\xed\a \x01(\x05R\x16pollingIntervalInitial\x12%\n" + "\x0emax_write_size\x18\xd3\x0f \x01(\x05R\fmaxWriteSize\x122\n" + "\x15max_write_duration_ms\x18\xd4\x0f \x01(\x05R\x12maxWriteDurationMs\x12J\n" + "!max_simultaneous_write_connection\x18\xd5\x0f \x01(\x05R\x1emaxSimultaneousWriteConnection\x123\n" + "\x15packet_writing_buffer\x18\xd6\x0f \x01(\x05R\x13packetWritingBuffer\x12\x11\n" + "\x03url\x18\xb9\x17 \x01(\tR\x03url\x12!\n" + "\fh2_pool_size\x18\xbb\x17 \x01(\x05R\n" + "h2PoolSize:\x1a\x82\xb5\x18\x16\n" + "\ttransport\x12\x05mekya\x90\xff)\x01B\xc3\x01\n" + ":com.v2ray.core.transport.internet.request.stereotype.mekyaP\x01ZJgithub.com/v2fly/v2ray-core/v5/transport/internet/request/stereotype/mekya\xaa\x026V2Ray.Core.Transport.Internet.Request.Stereotype.Mekyab\x06proto3" var ( file_transport_internet_request_stereotype_mekya_config_proto_rawDescOnce sync.Once file_transport_internet_request_stereotype_mekya_config_proto_rawDescData []byte ) func file_transport_internet_request_stereotype_mekya_config_proto_rawDescGZIP() []byte { file_transport_internet_request_stereotype_mekya_config_proto_rawDescOnce.Do(func() { file_transport_internet_request_stereotype_mekya_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_request_stereotype_mekya_config_proto_rawDesc), len(file_transport_internet_request_stereotype_mekya_config_proto_rawDesc))) }) return file_transport_internet_request_stereotype_mekya_config_proto_rawDescData } var file_transport_internet_request_stereotype_mekya_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_request_stereotype_mekya_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.request.stereotype.mekya.Config (*kcp.Config)(nil), // 1: v2ray.core.transport.internet.kcp.Config } var file_transport_internet_request_stereotype_mekya_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.transport.internet.request.stereotype.mekya.Config.kcp:type_name -> v2ray.core.transport.internet.kcp.Config 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_request_stereotype_mekya_config_proto_init() } func file_transport_internet_request_stereotype_mekya_config_proto_init() { if File_transport_internet_request_stereotype_mekya_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_request_stereotype_mekya_config_proto_rawDesc), len(file_transport_internet_request_stereotype_mekya_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_request_stereotype_mekya_config_proto_goTypes, DependencyIndexes: file_transport_internet_request_stereotype_mekya_config_proto_depIdxs, MessageInfos: file_transport_internet_request_stereotype_mekya_config_proto_msgTypes, }.Build() File_transport_internet_request_stereotype_mekya_config_proto = out.File file_transport_internet_request_stereotype_mekya_config_proto_goTypes = nil file_transport_internet_request_stereotype_mekya_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/request/stereotype/mekya/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.request.stereotype.mekya; option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.Stereotype.Mekya"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/stereotype/mekya"; option java_package = "com.v2ray.core.transport.internet.request.stereotype.mekya"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "transport/internet/kcp/config.proto"; message Config{ option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "mekya"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; v2ray.core.transport.internet.kcp.Config kcp = 1; // Client int32 max_write_delay = 1003; int32 max_request_size = 1004; int32 polling_interval_initial = 1005; // Server int32 max_write_size = 2003; int32 max_write_duration_ms = 2004; int32 max_simultaneous_write_connection = 2005; int32 packet_writing_buffer = 2006; // Roundtripper string url = 3001; int32 h2_pool_size = 3003; } ================================================ FILE: transport/internet/request/stereotype/mekya/errors.generated.go ================================================ package mekya import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/request/stereotype/mekya/mekya.go ================================================ package mekya import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/packetconn" "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembly" "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripper/httprt" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const protocolName = "mekya" func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return nil, newError("meek is a transport") })) common.Must(internet.RegisterTransportDialer(protocolName, mekyaDial)) common.Must(internet.RegisterTransportListener(protocolName, mekyaListen)) } func mekyaDial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { mekyaSetting := streamSettings.ProtocolSettings.(*Config) packetConnAssembler := &packetconn.ClientConfig{} packetConnAssembler.PollingIntervalInitial = mekyaSetting.PollingIntervalInitial packetConnAssembler.MaxRequestSize = mekyaSetting.MaxRequestSize packetConnAssembler.MaxWriteDelay = mekyaSetting.MaxWriteDelay packetConnAssembler.UnderlyingTransportName = "kcp" packetConnAssembler.UnderlyingTransportSetting = serial.ToTypedMessage(mekyaSetting.Kcp) httprtSetting := &httprt.ClientConfig{ Http: &httprt.HTTPConfig{ UrlPrefix: mekyaSetting.Url, }, H2PoolSize: mekyaSetting.H2PoolSize, } request := &assembly.Config{ Assembler: serial.ToTypedMessage(packetConnAssembler), Roundtripper: serial.ToTypedMessage(httprtSetting), } constructedSetting := &internet.MemoryStreamConfig{ ProtocolName: "request", ProtocolSettings: request, SecurityType: streamSettings.SecurityType, SecuritySettings: streamSettings.SecuritySettings, SocketSettings: streamSettings.SocketSettings, } return internet.Dial(ctx, dest, constructedSetting) } func mekyaListen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, callback internet.ConnHandler) (internet.Listener, error) { mekyaSetting := streamSettings.ProtocolSettings.(*Config) packetConnAssembler := &packetconn.ServerConfig{} packetConnAssembler.MaxWriteSize = mekyaSetting.MaxWriteSize packetConnAssembler.MaxSimultaneousWriteConnection = mekyaSetting.MaxSimultaneousWriteConnection packetConnAssembler.MaxWriteDurationMs = mekyaSetting.MaxWriteDurationMs packetConnAssembler.PacketWritingBuffer = mekyaSetting.PacketWritingBuffer packetConnAssembler.UnderlyingTransportName = "kcp" packetConnAssembler.UnderlyingTransportSetting = serial.ToTypedMessage(mekyaSetting.Kcp) request := &assembly.Config{ Assembler: serial.ToTypedMessage(packetConnAssembler), Roundtripper: serial.ToTypedMessage(&httprt.ServerConfig{}), } constructedSetting := &internet.MemoryStreamConfig{ ProtocolName: "request", ProtocolSettings: request, SecurityType: streamSettings.SecurityType, SecuritySettings: streamSettings.SecuritySettings, SocketSettings: streamSettings.SocketSettings, } return internet.ListenTCP(ctx, address, port, constructedSetting, callback) } ================================================ FILE: transport/internet/security/connprop.go ================================================ package security type ConnectionApplicationProtocol interface { GetConnectionApplicationProtocol() (string, error) } ================================================ FILE: transport/internet/security/errors.generated.go ================================================ package security import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/security/security.go ================================================ package security //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen import ( "github.com/v2fly/v2ray-core/v5/common/net" ) type Engine interface { Client(conn net.Conn, opts ...Option) (Conn, error) } type Conn interface { net.Conn } type Option interface { isSecurityOption() } type OptionWithALPN struct { ALPNs []string } func (a OptionWithALPN) isSecurityOption() { } type OptionWithDestination struct { Dest net.Destination } func (a OptionWithDestination) isSecurityOption() { } ================================================ FILE: transport/internet/security/util.go ================================================ package security import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func CreateSecurityEngineFromSettings(context context.Context, settings *internet.MemoryStreamConfig) (Engine, error) { if settings == nil || settings.SecurityType == "" { return nil, nil } securityEngine, err := common.CreateObject(context, settings.SecuritySettings) if err != nil { return nil, newError("unable to create security engine from security settings").Base(err) } securityEngineTyped, ok := securityEngine.(Engine) if !ok { return nil, newError("type assertion error when create security engine from security settings") } return securityEngineTyped, nil } ================================================ FILE: transport/internet/socket_activation_other.go ================================================ //go:build !unix // +build !unix package internet import ( "fmt" "github.com/v2fly/v2ray-core/v5/common/net" ) func activateSocket(address string, f func(network, address string, fd uintptr)) (net.Listener, error) { return nil, fmt.Errorf("socket activation is not supported on this platform") } ================================================ FILE: transport/internet/socket_activation_unix.go ================================================ //go:build unix // +build unix package internet import ( "fmt" "os" "path" "strconv" "syscall" "github.com/v2fly/v2ray-core/v5/common/net" ) func activateSocket(address string, f func(network, address string, fd uintptr)) (net.Listener, error) { fd, err := strconv.Atoi(path.Base(address)) if err != nil { return nil, err } err = syscall.SetNonblock(fd, true) if err != nil { return nil, err } acceptConn, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ACCEPTCONN) if err != nil { return nil, err } if acceptConn == 0 { return nil, fmt.Errorf("socket '%s' has not been marked to accept connections", address) } sockType, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE) if err != nil { return nil, err } if sockType != syscall.SOCK_STREAM { // XXX: currently only stream socks are supported return nil, fmt.Errorf("socket '%s' is not a stream socket", address) } ufd := uintptr(fd) sa, err := syscall.Getsockname(fd) if err != nil { return nil, err } switch sa := sa.(type) { case *syscall.SockaddrInet4: addr := net.TCPAddr{IP: sa.Addr[:], Port: sa.Port, Zone: ""} f("tcp4", addr.String(), ufd) case *syscall.SockaddrInet6: addr := net.TCPAddr{IP: sa.Addr[:], Port: sa.Port, Zone: strconv.Itoa(int(sa.ZoneId))} f("tcp6", addr.String(), ufd) } file := os.NewFile(ufd, address) defer file.Close() return net.FileListener(file) } ================================================ FILE: transport/internet/sockopt.go ================================================ package internet func isTCPSocket(network string) bool { switch network { case "tcp", "tcp4", "tcp6": return true default: return false } } func isUDPSocket(network string) bool { switch network { case "udp", "udp4", "udp6": return true default: return false } } ================================================ FILE: transport/internet/sockopt_darwin.go ================================================ package internet import ( "net" "golang.org/x/sys/unix" ) const ( // TCP_FASTOPEN_SERVER is the value to enable TCP fast open on darwin for server connections. TCP_FASTOPEN_SERVER = 0x01 // nolint: revive,stylecheck // TCP_FASTOPEN_CLIENT is the value to enable TCP fast open on darwin for client connections. TCP_FASTOPEN_CLIENT = 0x02 // nolint: revive,stylecheck ) func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { if isTCPSocket(network) { switch config.Tfo { case SocketConfig_Enable: if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, TCP_FASTOPEN_CLIENT); err != nil { return err } case SocketConfig_Disable: if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 0); err != nil { return err } } if config.TcpKeepAliveInterval > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { return newError("failed to set TCP_KEEPINTVL").Base(err) } } if config.TcpKeepAliveIdle > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveIdle)); err != nil { return newError("failed to set TCP_KEEPALIVE (TCP keepalive idle time on Darwin)").Base(err) } } if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil { return newError("failed to set SO_KEEPALIVE").Base(err) } } } if config.BindToDevice != "" { iface, err := net.InterfaceByName(config.BindToDevice) if err != nil { return newError("failed to get interface ", config.BindToDevice).Base(err) } if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index); err != nil { return newError("failed to set IP_BOUND_IF", err) } if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index); err != nil { return newError("failed to set IPV6_BOUND_IF", err) } } if config.TxBufSize != 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, int(config.TxBufSize)); err != nil { return newError("failed to set SO_SNDBUF").Base(err) } } if config.RxBufSize != 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, int(config.RxBufSize)); err != nil { return newError("failed to set SO_RCVBUF").Base(err) } } return nil } func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error { if isTCPSocket(network) { switch config.Tfo { case SocketConfig_Enable: if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, TCP_FASTOPEN_SERVER); err != nil { return err } case SocketConfig_Disable: if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 0); err != nil { return err } } if config.TcpKeepAliveInterval > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { return newError("failed to set TCP_KEEPINTVL").Base(err) } } if config.TcpKeepAliveIdle > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveIdle)); err != nil { return newError("failed to set TCP_KEEPALIVE (TCP keepalive idle time on Darwin)").Base(err) } } if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_KEEPALIVE, 1); err != nil { return newError("failed to set SO_KEEPALIVE").Base(err) } } } if config.BindToDevice != "" { iface, err := net.InterfaceByName(config.BindToDevice) if err != nil { return newError("failed to get interface ", config.BindToDevice).Base(err) } if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index); err != nil { return newError("failed to set IP_BOUND_IF", err) } if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index); err != nil { return newError("failed to set IPV6_BOUND_IF", err) } } if config.TxBufSize != 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, int(config.TxBufSize)); err != nil { return newError("failed to set SO_SNDBUF/SO_SNDBUFFORCE").Base(err) } } if config.RxBufSize != 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, int(config.RxBufSize)); err != nil { return newError("failed to set SO_RCVBUF/SO_RCVBUFFORCE").Base(err) } } return nil } func bindAddr(fd uintptr, address []byte, port uint32) error { return nil } func setReuseAddr(fd uintptr) error { return nil } func setReusePort(fd uintptr) error { return nil } ================================================ FILE: transport/internet/sockopt_freebsd.go ================================================ package internet import ( "encoding/binary" "net" "os" "syscall" "unsafe" "golang.org/x/sys/unix" ) const ( sysPFINOUT = 0x0 sysPFIN = 0x1 sysPFOUT = 0x2 sysPFFWD = 0x3 sysDIOCNATLOOK = 0xc04c4417 ) type pfiocNatlook struct { Saddr [16]byte /* pf_addr */ Daddr [16]byte /* pf_addr */ Rsaddr [16]byte /* pf_addr */ Rdaddr [16]byte /* pf_addr */ Sport uint16 Dport uint16 Rsport uint16 Rdport uint16 Af uint8 Proto uint8 Direction uint8 Pad [1]byte } const ( sizeofPfiocNatlook = 0x4c soReUsePort = 0x00000200 soReUsePortLB = 0x00010000 ) func ioctl(s uintptr, ioc int, b []byte) error { if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, s, uintptr(ioc), uintptr(unsafe.Pointer(&b[0]))); errno != 0 { return error(errno) } return nil } func (nl *pfiocNatlook) rdPort() int { return int(binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&nl.Rdport))[:])) } func (nl *pfiocNatlook) setPort(remote, local int) { binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Sport))[:], uint16(remote)) binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Dport))[:], uint16(local)) } // OriginalDst uses ioctl to read original destination from /dev/pf func OriginalDst(la, ra net.Addr) (net.IP, int, error) { f, err := os.Open("/dev/pf") if err != nil { return net.IP{}, -1, newError("failed to open device /dev/pf").Base(err) } defer f.Close() fd := f.Fd() b := make([]byte, sizeofPfiocNatlook) nl := (*pfiocNatlook)(unsafe.Pointer(&b[0])) var raIP, laIP net.IP var raPort, laPort int switch la.(type) { case *net.TCPAddr: raIP = ra.(*net.TCPAddr).IP laIP = la.(*net.TCPAddr).IP raPort = ra.(*net.TCPAddr).Port laPort = la.(*net.TCPAddr).Port nl.Proto = syscall.IPPROTO_TCP case *net.UDPAddr: raIP = ra.(*net.UDPAddr).IP laIP = la.(*net.UDPAddr).IP raPort = ra.(*net.UDPAddr).Port laPort = la.(*net.UDPAddr).Port nl.Proto = syscall.IPPROTO_UDP } if raIP.To4() != nil { if laIP.IsUnspecified() { laIP = net.ParseIP("127.0.0.1") } copy(nl.Saddr[:net.IPv4len], raIP.To4()) copy(nl.Daddr[:net.IPv4len], laIP.To4()) nl.Af = syscall.AF_INET } if raIP.To16() != nil && raIP.To4() == nil { if laIP.IsUnspecified() { laIP = net.ParseIP("::1") } copy(nl.Saddr[:], raIP) copy(nl.Daddr[:], laIP) nl.Af = syscall.AF_INET6 } nl.setPort(raPort, laPort) ioc := uintptr(sysDIOCNATLOOK) for _, dir := range []byte{sysPFOUT, sysPFIN} { nl.Direction = dir err = ioctl(fd, int(ioc), b) if err == nil || err != syscall.ENOENT { break } } if err != nil { return net.IP{}, -1, os.NewSyscallError("ioctl", err) } odPort := nl.rdPort() var odIP net.IP switch nl.Af { case syscall.AF_INET: odIP = make(net.IP, net.IPv4len) copy(odIP, nl.Rdaddr[:net.IPv4len]) case syscall.AF_INET6: odIP = make(net.IP, net.IPv6len) copy(odIP, nl.Rdaddr[:]) } return odIP, odPort, nil } func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { if config.Mark != 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil { return newError("failed to set SO_USER_COOKIE").Base(err) } } if isTCPSocket(network) { switch config.Tfo { case SocketConfig_Enable: if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, 1); err != nil { return newError("failed to set TCP_FASTOPEN_CONNECT=1").Base(err) } case SocketConfig_Disable: if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, 0); err != nil { return newError("failed to set TCP_FASTOPEN_CONNECT=0").Base(err) } } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { return newError("failed to set TCP_KEEPIDLE").Base(err) } } if config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { return newError("failed to set TCP_KEEPINTVL").Base(err) } } if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { return newError("failed to set SO_KEEPALIVE").Base(err) } } } if config.Tproxy.IsEnabled() { ip, _, _ := net.SplitHostPort(address) if net.ParseIP(ip).To4() != nil { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil { return newError("failed to set outbound IP_BINDANY").Base(err) } } else { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil { return newError("failed to set outbound IPV6_BINDANY").Base(err) } } } if config.TxBufSize != 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, int(config.TxBufSize)); err != nil { return newError("failed to set SO_SNDBUF").Base(err) } } if config.RxBufSize != 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, int(config.RxBufSize)); err != nil { return newError("failed to set SO_RCVBUF").Base(err) } } return nil } func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error { if config.Mark != 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil { return newError("failed to set SO_USER_COOKIE").Base(err) } } if isTCPSocket(network) { switch config.Tfo { case SocketConfig_Enable: if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, 1); err != nil { return newError("failed to set TCP_FASTOPEN=1").Base(err) } case SocketConfig_Disable: if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, 0); err != nil { return newError("failed to set TCP_FASTOPEN=0").Base(err) } } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { return newError("failed to set TCP_KEEPIDLE").Base(err) } } if config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { return newError("failed to set TCP_KEEPINTVL").Base(err) } } if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { return newError("failed to set SO_KEEPALIVE").Base(err) } } } if config.Tproxy.IsEnabled() { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil { return newError("failed to set inbound IP_BINDANY").Base(err) } } } if config.TxBufSize != 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, int(config.TxBufSize)); err != nil { return newError("failed to set SO_SNDBUF").Base(err) } } if config.RxBufSize != 0 { if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, int(config.RxBufSize)); err != nil { return newError("failed to set SO_RCVBUF").Base(err) } } return nil } func bindAddr(fd uintptr, ip []byte, port uint32) error { setReuseAddr(fd) setReusePort(fd) var sockaddr syscall.Sockaddr switch len(ip) { case net.IPv4len: a4 := &syscall.SockaddrInet4{ Port: int(port), } copy(a4.Addr[:], ip) sockaddr = a4 case net.IPv6len: a6 := &syscall.SockaddrInet6{ Port: int(port), } copy(a6.Addr[:], ip) sockaddr = a6 default: return newError("unexpected length of ip") } return syscall.Bind(int(fd), sockaddr) } func setReuseAddr(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { return newError("failed to set SO_REUSEADDR").Base(err).AtWarning() } return nil } func setReusePort(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePortLB, 1); err != nil { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePort, 1); err != nil { return newError("failed to set SO_REUSEPORT").Base(err).AtWarning() } } return nil } ================================================ FILE: transport/internet/sockopt_linux.go ================================================ package internet import ( "net" "syscall" "golang.org/x/sys/unix" ) const ( // For incoming connections. TCP_FASTOPEN = 23 // nolint: revive,stylecheck // For out-going connections. TCP_FASTOPEN_CONNECT = 30 // nolint: revive,stylecheck ) func bindAddr(fd uintptr, ip []byte, port uint32) error { setReuseAddr(fd) setReusePort(fd) var sockaddr syscall.Sockaddr switch len(ip) { case net.IPv4len: a4 := &syscall.SockaddrInet4{ Port: int(port), } copy(a4.Addr[:], ip) sockaddr = a4 case net.IPv6len: a6 := &syscall.SockaddrInet6{ Port: int(port), } copy(a6.Addr[:], ip) sockaddr = a6 default: return newError("unexpected length of ip") } return syscall.Bind(int(fd), sockaddr) } func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { if config.Mark != 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(config.Mark)); err != nil { return newError("failed to set SO_MARK").Base(err) } } if isTCPSocket(network) { switch config.Tfo { case SocketConfig_Enable: if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN_CONNECT, 1); err != nil { return newError("failed to set TCP_FASTOPEN_CONNECT=1").Base(err) } case SocketConfig_Disable: if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN_CONNECT, 0); err != nil { return newError("failed to set TCP_FASTOPEN_CONNECT=0").Base(err) } } if config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { return newError("failed to set TCP_KEEPINTVL", err) } } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { return newError("failed to set TCP_KEEPIDLE", err) } } if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { return newError("failed to set SO_KEEPALIVE").Base(err) } } } if config.Tproxy.IsEnabled() { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { return newError("failed to set IP_TRANSPARENT").Base(err) } } if config.BindToDevice != "" { if err := unix.BindToDevice(int(fd), config.BindToDevice); err != nil { return newError("failed to set SO_BINDTODEVICE").Base(err) } } if config.TxBufSize != 0 { syscallTarget := unix.SO_SNDBUF if config.ForceBufSize { syscallTarget = unix.SO_SNDBUFFORCE } if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, syscallTarget, int(config.TxBufSize)); err != nil { return newError("failed to set SO_SNDBUF/SO_SNDBUFFORCE").Base(err) } } if config.RxBufSize != 0 { syscallTarget := unix.SO_RCVBUF if config.ForceBufSize { syscallTarget = unix.SO_RCVBUFFORCE } if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, syscallTarget, int(config.RxBufSize)); err != nil { return newError("failed to set SO_RCVBUF/SO_RCVBUFFORCE").Base(err) } } return nil } func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error { if config.Mark != 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(config.Mark)); err != nil { return newError("failed to set SO_MARK").Base(err) } } if isTCPSocket(network) { switch config.Tfo { case SocketConfig_Enable: if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN, int(config.TfoQueueLength)); err != nil { return newError("failed to set TCP_FASTOPEN=", config.TfoQueueLength).Base(err) } case SocketConfig_Disable: if err := syscall.SetsockoptInt(int(fd), syscall.SOL_TCP, TCP_FASTOPEN, 0); err != nil { return newError("failed to set TCP_FASTOPEN=0").Base(err) } } if config.TcpKeepAliveInterval > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil { return newError("failed to set TCP_KEEPINTVL", err) } } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil { return newError("failed to set TCP_KEEPIDLE", err) } } if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { return newError("failed to set SO_KEEPALIVE", err) } } } if config.Tproxy.IsEnabled() { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { return newError("failed to set IP_TRANSPARENT").Base(err) } } if config.ReceiveOriginalDestAddress && isUDPSocket(network) { err1 := syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1) err2 := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1) if err1 != nil && err2 != nil { return err1 } } if config.BindToDevice != "" { if err := unix.BindToDevice(int(fd), config.BindToDevice); err != nil { return newError("failed to set SO_BINDTODEVICE").Base(err) } } if config.TxBufSize != 0 { syscallTarget := unix.SO_SNDBUF if config.ForceBufSize { syscallTarget = unix.SO_SNDBUFFORCE } if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, syscallTarget, int(config.TxBufSize)); err != nil { return newError("failed to set SO_SNDBUF/SO_SNDBUFFORCE").Base(err) } } if config.RxBufSize != 0 { syscallTarget := unix.SO_RCVBUF if config.ForceBufSize { syscallTarget = unix.SO_RCVBUFFORCE } if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, syscallTarget, int(config.RxBufSize)); err != nil { return newError("failed to set SO_RCVBUF/SO_RCVBUFFORCE").Base(err) } } return nil } func setReuseAddr(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { return newError("failed to set SO_REUSEADDR").Base(err).AtWarning() } return nil } func setReusePort(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { return newError("failed to set SO_REUSEPORT").Base(err).AtWarning() } return nil } ================================================ FILE: transport/internet/sockopt_linux_test.go ================================================ package internet_test import ( "context" "syscall" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" . "github.com/v2fly/v2ray-core/v5/transport/internet" ) func TestSockOptMark(t *testing.T) { t.Skip("requires CAP_NET_ADMIN") tcpServer := tcp.Server{ MsgProcessor: func(b []byte) []byte { return b }, } dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() const mark = 1 dialer := DefaultSystemDialer{} conn, err := dialer.Dial(context.Background(), nil, dest, &SocketConfig{Mark: mark}) common.Must(err) defer conn.Close() rawConn, err := conn.(*net.TCPConn).SyscallConn() common.Must(err) err = rawConn.Control(func(fd uintptr) { m, err := syscall.GetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK) common.Must(err) if mark != m { t.Fatal("unexpected connection mark", m, " want ", mark) } }) common.Must(err) } ================================================ FILE: transport/internet/sockopt_other.go ================================================ //go:build js || dragonfly || netbsd || openbsd || solaris // +build js dragonfly netbsd openbsd solaris package internet func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { return nil } func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error { return nil } func bindAddr(fd uintptr, ip []byte, port uint32) error { return nil } func setReuseAddr(fd uintptr) error { return nil } func setReusePort(fd uintptr) error { return nil } ================================================ FILE: transport/internet/sockopt_test.go ================================================ package internet_test import ( "context" "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" . "github.com/v2fly/v2ray-core/v5/transport/internet" ) func TestTCPFastOpen(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: func(b []byte) []byte { return b }, } dest, err := tcpServer.StartContext(context.Background(), &SocketConfig{Tfo: SocketConfig_Enable}) common.Must(err) defer tcpServer.Close() ctx := context.Background() dialer := DefaultSystemDialer{} conn, err := dialer.Dial(ctx, nil, dest, &SocketConfig{ Tfo: SocketConfig_Enable, }) common.Must(err) defer conn.Close() _, err = conn.Write([]byte("abcd")) common.Must(err) b := buf.New() common.Must2(b.ReadFrom(conn)) if r := cmp.Diff(b.Bytes(), []byte("abcd")); r != "" { t.Fatal(r) } } // Currently, Multipath TCP is only supported on Linux. // We test the Multipath TCP Settings on other platforms for ensure code will not have any negative impact on other platforms. func TestMultipathTCP(t *testing.T) { tcpServer := tcp.Server{ MsgProcessor: func(b []byte) []byte { return b }, } dest, err := tcpServer.StartContext(context.Background(), &SocketConfig{Mptcp: MPTCPState_Enable}) common.Must(err) defer tcpServer.Close() ctx := context.Background() dialer := DefaultSystemDialer{} conn, err := dialer.Dial(ctx, nil, dest, &SocketConfig{ Mptcp: MPTCPState_Enable, }) common.Must(err) defer conn.Close() _, err = conn.Write([]byte("abcd")) common.Must(err) b := buf.New() common.Must2(b.ReadFrom(conn)) if r := cmp.Diff(b.Bytes(), []byte("abcd")); r != "" { t.Fatal(r) } } ================================================ FILE: transport/internet/sockopt_windows.go ================================================ package internet import ( "net" "syscall" "golang.org/x/sys/windows" ) const ( TCP_FASTOPEN = 15 // nolint: revive,stylecheck IP_UNICAST_IF = 31 // nolint: revive,stylecheck IPV6_UNICAST_IF = 31 // nolint: revive,stylecheck ) func setTFO(fd syscall.Handle, settings SocketConfig_TCPFastOpenState) error { switch settings { case SocketConfig_Enable: if err := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, TCP_FASTOPEN, 1); err != nil { return err } case SocketConfig_Disable: if err := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, TCP_FASTOPEN, 0); err != nil { return err } } return nil } func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error { if isTCPSocket(network) { if err := setTFO(syscall.Handle(fd), config.Tfo); err != nil { return err } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { return newError("failed to set SO_KEEPALIVE", err) } } } if config.BindToDevice != "" { iface, err := net.InterfaceByName(config.BindToDevice) if err != nil { return newError("failed to get interface ", config.BindToDevice).Base(err) } if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_UNICAST_IF, iface.Index); err != nil { return newError("failed to set IP_UNICAST_IF", err) } if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IPV6_UNICAST_IF, iface.Index); err != nil { return newError("failed to set IPV6_UNICAST_IF", err) } } if config.TxBufSize != 0 { if err := windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_SNDBUF, int(config.TxBufSize)); err != nil { return newError("failed to set SO_SNDBUF").Base(err) } } if config.RxBufSize != 0 { if err := windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_RCVBUF, int(config.TxBufSize)); err != nil { return newError("failed to set SO_RCVBUF").Base(err) } } return nil } func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error { if isTCPSocket(network) { if err := setTFO(syscall.Handle(fd), config.Tfo); err != nil { return err } if config.TcpKeepAliveIdle > 0 { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { return newError("failed to set SO_KEEPALIVE", err) } } } if config.TxBufSize != 0 { if err := windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_SNDBUF, int(config.TxBufSize)); err != nil { return newError("failed to set SO_SNDBUF").Base(err) } } if config.RxBufSize != 0 { if err := windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_RCVBUF, int(config.TxBufSize)); err != nil { return newError("failed to set SO_RCVBUF").Base(err) } } return nil } func bindAddr(fd uintptr, ip []byte, port uint32) error { return nil } func setReuseAddr(fd uintptr) error { return nil } func setReusePort(fd uintptr) error { return nil } ================================================ FILE: transport/internet/system_dialer.go ================================================ package internet import ( "context" "syscall" "time" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" ) var effectiveSystemDialer SystemDialer = &DefaultSystemDialer{} type SystemDialer interface { Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *SocketConfig) (net.Conn, error) } type DefaultSystemDialer struct { controllers []controller } func resolveSrcAddr(network net.Network, src net.Address) net.Addr { if src == nil || src == net.AnyIP { return nil } if network == net.Network_TCP { return &net.TCPAddr{ IP: src.IP(), Port: 0, } } return &net.UDPAddr{ IP: src.IP(), Port: 0, } } func hasBindAddr(sockopt *SocketConfig) bool { return sockopt != nil && len(sockopt.BindAddress) > 0 && sockopt.BindPort > 0 } func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { if dest.Network == net.Network_UDP && !hasBindAddr(sockopt) { srcAddr := resolveSrcAddr(net.Network_UDP, src) if srcAddr == nil { srcAddr = &net.UDPAddr{ IP: []byte{0, 0, 0, 0}, Port: 0, } } packetConn, err := ListenSystemPacket(ctx, srcAddr, sockopt) if err != nil { return nil, err } destAddr, err := net.ResolveUDPAddr("udp", dest.NetAddr()) if err != nil { return nil, err } return &packetConnWrapper{ conn: packetConn, dest: destAddr, }, nil } goStdKeepAlive := time.Duration(0) if sockopt != nil && (sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0) { goStdKeepAlive = time.Duration(-1) } dialer := &net.Dialer{ Timeout: time.Second * 16, LocalAddr: resolveSrcAddr(dest.Network, src), KeepAlive: goStdKeepAlive, } if dest.Network == net.Network_TCP && sockopt != nil { switch sockopt.Mptcp { case MPTCPState_Enable: dialer.SetMultipathTCP(true) case MPTCPState_Disable: dialer.SetMultipathTCP(false) } } if sockopt != nil || len(d.controllers) > 0 { dialer.Control = func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { if sockopt != nil { if err := applyOutboundSocketOptions(network, address, fd, sockopt); err != nil { newError("failed to apply socket options").Base(err).WriteToLog(session.ExportIDToError(ctx)) } if dest.Network == net.Network_UDP && hasBindAddr(sockopt) { if err := bindAddr(fd, sockopt.BindAddress, sockopt.BindPort); err != nil { newError("failed to bind source address to ", sockopt.BindAddress).Base(err).WriteToLog(session.ExportIDToError(ctx)) } } } for _, ctl := range d.controllers { if err := ctl(network, address, fd); err != nil { newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } }) } } return dialer.DialContext(ctx, dest.Network.SystemString(), dest.NetAddr()) } type packetConnWrapper struct { conn net.PacketConn dest net.Addr } func (c *packetConnWrapper) Close() error { return c.conn.Close() } func (c *packetConnWrapper) LocalAddr() net.Addr { return c.conn.LocalAddr() } func (c *packetConnWrapper) RemoteAddr() net.Addr { return c.dest } func (c *packetConnWrapper) Write(p []byte) (int, error) { return c.conn.WriteTo(p, c.dest) } func (c *packetConnWrapper) Read(p []byte) (int, error) { n, _, err := c.conn.ReadFrom(p) return n, err } func (c *packetConnWrapper) SetDeadline(t time.Time) error { return c.conn.SetDeadline(t) } func (c *packetConnWrapper) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) } func (c *packetConnWrapper) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) } type SystemDialerAdapter interface { Dial(network string, address string) (net.Conn, error) } type SimpleSystemDialer struct { adapter SystemDialerAdapter } func WithAdapter(dialer SystemDialerAdapter) SystemDialer { return &SimpleSystemDialer{ adapter: dialer, } } func (v *SimpleSystemDialer) Dial(ctx context.Context, src net.Address, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { return v.adapter.Dial(dest.Network.SystemString(), dest.NetAddr()) } // UseAlternativeSystemDialer replaces the current system dialer with a given one. // Caller must ensure there is no race condition. // // v2ray:api:stable func UseAlternativeSystemDialer(dialer SystemDialer) { if dialer == nil { dialer = &DefaultSystemDialer{} } effectiveSystemDialer = dialer } // RegisterDialerController adds a controller to the effective system dialer. // The controller can be used to operate on file descriptors before they are put into use. // It only works when effective dialer is the default dialer. // // v2ray:api:beta func RegisterDialerController(ctl func(network, address string, fd uintptr) error) error { if ctl == nil { return newError("nil listener controller") } dialer, ok := effectiveSystemDialer.(*DefaultSystemDialer) if !ok { return newError("RegisterListenerController not supported in custom dialer") } dialer.controllers = append(dialer.controllers, ctl) return nil } ================================================ FILE: transport/internet/system_dns_android.go ================================================ //go:build android // +build android package internet import ( "context" "net" ) const SystemDNS = "8.8.8.8:53" /* DNSResolverFunc This is a temporary API and is subject to removal at any time. */ type DNSResolverFunc func() *net.Resolver /* NewDNSResolver This is a temporary API and is subject to removal at any time. */ var NewDNSResolver DNSResolverFunc = func() *net.Resolver { return &net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, network, _ string) (net.Conn, error) { var dialer net.Dialer return dialer.DialContext(ctx, network, SystemDNS) }, } } func init() { net.DefaultResolver = NewDNSResolver() } ================================================ FILE: transport/internet/system_dns_android_test.go ================================================ //go:build android // +build android package internet import ( "context" "testing" ) func TestDNSResolver(t *testing.T) { resolver := NewDNSResolver() if ips, err := resolver.LookupIP(context.Background(), "ip", "www.google.com"); err != nil { t.Errorf("failed to lookupIP, %v, %v", ips, err) } } ================================================ FILE: transport/internet/system_listener.go ================================================ package internet import ( "context" "os" "runtime" "strconv" "strings" "syscall" "time" "github.com/pires/go-proxyproto" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" ) var effectiveListener = DefaultListener{} type controller func(network, address string, fd uintptr) error type DefaultListener struct { controllers []controller } type combinedListener struct { net.Listener locker *FileLocker // for unix domain socket } func (l *combinedListener) Close() error { if l.locker != nil { l.locker.Release() l.locker = nil } return l.Listener.Close() } func getRawControlFunc(network, address string, ctx context.Context, sockopt *SocketConfig, controllers []controller) func(fd uintptr) { return func(fd uintptr) { if sockopt != nil { if err := applyInboundSocketOptions(network, fd, sockopt); err != nil { newError("failed to apply socket options to incoming connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } setReusePort(fd) // nolint: staticcheck for _, controller := range controllers { if err := controller(network, address, fd); err != nil { newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx)) } } } } func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []controller) func(network, address string, c syscall.RawConn) error { return func(network, address string, c syscall.RawConn) error { return c.Control(getRawControlFunc(network, address, ctx, sockopt, controllers)) } } func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) { var lc net.ListenConfig var network, address string var l net.Listener var err error // callback is called after the Listen function returns // this is used to wrap the listener and do some post processing callback := func(l net.Listener, err error) (net.Listener, error) { return l, err } switch addr := addr.(type) { case *net.TCPAddr: network = addr.Network() address = addr.String() lc.Control = getControlFunc(ctx, sockopt, dl.controllers) if sockopt != nil { switch sockopt.Mptcp { case MPTCPState_Enable: lc.SetMultipathTCP(true) case MPTCPState_Disable: lc.SetMultipathTCP(false) } if sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0 { lc.KeepAlive = time.Duration(-1) } } case *net.UnixAddr: lc.Control = nil network = addr.Network() address = addr.Name if (runtime.GOOS == "linux" || runtime.GOOS == "android") && address[0] == '@' { //nolint: gocritic // linux abstract unix domain socket is lockfree if len(address) > 1 && address[1] == '@' { // but may need padding to work with haproxy fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) copy(fullAddr, address[1:]) address = string(fullAddr) } } else if strings.HasPrefix(address, "/dev/fd/") { // socket activation l, err = activateSocket(address, func(network, address string, fd uintptr) { getRawControlFunc(network, address, ctx, sockopt, dl.controllers)(fd) }) if err != nil { return nil, err } } else { // normal unix domain socket var fileMode *os.FileMode // parse file mode from address if s := strings.Split(address, ","); len(s) == 2 { fMode, err := strconv.ParseUint(s[1], 8, 32) if err != nil { return nil, newError("failed to parse file mode").Base(err) } address = s[0] fm := os.FileMode(fMode) fileMode = &fm } // normal unix domain socket needs lock locker := &FileLocker{ path: address + ".lock", } if err := locker.Acquire(); err != nil { return nil, err } // set file mode for unix domain socket when it is created callback = func(l net.Listener, err error) (net.Listener, error) { if err != nil { locker.Release() return nil, err } l = &combinedListener{Listener: l, locker: locker} if fileMode == nil { return l, err } if cerr := os.Chmod(address, *fileMode); cerr != nil { // failed to set file mode, close the listener l.Close() return nil, newError("failed to set file mode for file: ", address).Base(cerr) } return l, err } } } if l == nil { l, err = lc.Listen(ctx, network, address) l, err = callback(l, err) if err != nil { return nil, err } } if sockopt != nil && sockopt.AcceptProxyProtocol { policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil } l = &proxyproto.Listener{Listener: l, Policy: policyFunc} } return l, nil } func (dl *DefaultListener) ListenPacket(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.PacketConn, error) { var lc net.ListenConfig lc.Control = getControlFunc(ctx, sockopt, dl.controllers) return lc.ListenPacket(ctx, addr.Network(), addr.String()) } // RegisterListenerController adds a controller to the effective system listener. // The controller can be used to operate on file descriptors before they are put into use. // // v2ray:api:beta func RegisterListenerController(controller func(network, address string, fd uintptr) error) error { if controller == nil { return newError("nil listener controller") } effectiveListener.controllers = append(effectiveListener.controllers, controller) return nil } type SystemListener interface { Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) ListenPacket(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.PacketConn, error) } ================================================ FILE: transport/internet/system_listener_test.go ================================================ package internet_test import ( "context" "net" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func TestRegisterListenerController(t *testing.T) { var gotFd uintptr common.Must(internet.RegisterListenerController(func(network string, addr string, fd uintptr) error { gotFd = fd return nil })) conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{ IP: net.IPv4zero, }, nil) common.Must(err) common.Must(conn.Close()) if gotFd == 0 { t.Error("expected none-zero fd, but actually 0") } } ================================================ FILE: transport/internet/tagged/tagged.go ================================================ package tagged import ( "context" "github.com/v2fly/v2ray-core/v5/common/net" ) type DialFunc func(ctx context.Context, dest net.Destination, tag string) (net.Conn, error) var Dialer DialFunc ================================================ FILE: transport/internet/tagged/taggedimpl/errors.generated.go ================================================ package taggedimpl import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tagged/taggedimpl/impl.go ================================================ //go:build !confonly // +build !confonly package taggedimpl import ( "context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport/internet/tagged" ) func DialTaggedOutbound(ctx context.Context, dest net.Destination, tag string) (net.Conn, error) { var dispatcher routing.Dispatcher if core.FromContext(ctx) == nil { return nil, newError("Instance context variable is not in context, dial denied. ") } if err := core.RequireFeatures(ctx, func(dispatcherInstance routing.Dispatcher) { dispatcher = dispatcherInstance }); err != nil { return nil, newError("Required Feature dispatcher not resolved").Base(err) } content := new(session.Content) content.SkipDNSResolve = true ctx = session.ContextWithContent(ctx, content) ctx = session.SetForcedOutboundTagToContext(ctx, tag) r, err := dispatcher.Dispatch(ctx, dest) if err != nil { return nil, err } var readerOpt net.ConnectionOption if dest.Network == net.Network_TCP { readerOpt = net.ConnectionOutputMulti(r.Reader) } else { readerOpt = net.ConnectionOutputMultiUDP(r.Reader) } return net.NewConnection(net.ConnectionInputMulti(r.Writer), readerOpt), nil } func init() { tagged.Dialer = DialTaggedOutbound } ================================================ FILE: transport/internet/tagged/taggedimpl/taggedimpl.go ================================================ package taggedimpl //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/tcp/config.go ================================================ package tcp import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) const protocolName = "tcp" func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/tcp/config.pb.go ================================================ package tcp import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` HeaderSettings *anypb.Any `protobuf:"bytes,2,opt,name=header_settings,json=headerSettings,proto3" json:"header_settings,omitempty"` AcceptProxyProtocol bool `protobuf:"varint,3,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_tcp_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tcp_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_tcp_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetHeaderSettings() *anypb.Any { if x != nil { return x.HeaderSettings } return nil } func (x *Config) GetAcceptProxyProtocol() bool { if x != nil { return x.AcceptProxyProtocol } return false } var File_transport_internet_tcp_config_proto protoreflect.FileDescriptor const file_transport_internet_tcp_config_proto_rawDesc = "" + "\n" + "#transport/internet/tcp/config.proto\x12!v2ray.core.transport.internet.tcp\x1a\x19google/protobuf/any.proto\x1a common/protoext/extensions.proto\"\x9b\x01\n" + "\x06Config\x12=\n" + "\x0fheader_settings\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x0eheaderSettings\x122\n" + "\x15accept_proxy_protocol\x18\x03 \x01(\bR\x13acceptProxyProtocol:\x18\x82\xb5\x18\x14\n" + "\ttransport\x12\x03tcp\x90\xff)\x01J\x04\b\x01\x10\x02B\x84\x01\n" + "%com.v2ray.core.transport.internet.tcpP\x01Z5github.com/v2fly/v2ray-core/v5/transport/internet/tcp\xaa\x02!V2Ray.Core.Transport.Internet.Tcpb\x06proto3" var ( file_transport_internet_tcp_config_proto_rawDescOnce sync.Once file_transport_internet_tcp_config_proto_rawDescData []byte ) func file_transport_internet_tcp_config_proto_rawDescGZIP() []byte { file_transport_internet_tcp_config_proto_rawDescOnce.Do(func() { file_transport_internet_tcp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_tcp_config_proto_rawDesc), len(file_transport_internet_tcp_config_proto_rawDesc))) }) return file_transport_internet_tcp_config_proto_rawDescData } var file_transport_internet_tcp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_tcp_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.tcp.Config (*anypb.Any)(nil), // 1: google.protobuf.Any } var file_transport_internet_tcp_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.transport.internet.tcp.Config.header_settings:type_name -> google.protobuf.Any 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_tcp_config_proto_init() } func file_transport_internet_tcp_config_proto_init() { if File_transport_internet_tcp_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tcp_config_proto_rawDesc), len(file_transport_internet_tcp_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_tcp_config_proto_goTypes, DependencyIndexes: file_transport_internet_tcp_config_proto_depIdxs, MessageInfos: file_transport_internet_tcp_config_proto_msgTypes, }.Build() File_transport_internet_tcp_config_proto = out.File file_transport_internet_tcp_config_proto_goTypes = nil file_transport_internet_tcp_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/tcp/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.tcp; option csharp_namespace = "V2Ray.Core.Transport.Internet.Tcp"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tcp"; option java_package = "com.v2ray.core.transport.internet.tcp"; option java_multiple_files = true; import "google/protobuf/any.proto"; import "common/protoext/extensions.proto"; message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "tcp"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; reserved 1; google.protobuf.Any header_settings = 2; bool accept_proxy_protocol = 3; } ================================================ FILE: transport/internet/tcp/dialer.go ================================================ package tcp import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/security" ) // Dial dials a new TCP connection to the given destination. func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { newError("dialing TCP to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) if err != nil { return nil, err } securityEngine, err := security.CreateSecurityEngineFromSettings(ctx, streamSettings) if err != nil { return nil, newError("unable to create security engine").Base(err) } if securityEngine != nil { conn, err = securityEngine.Client(conn, security.OptionWithDestination{Dest: dest}) if err != nil { return nil, newError("unable to create security protocol client from security engine").Base(err) } } tcpSettings := streamSettings.ProtocolSettings.(*Config) if tcpSettings.HeaderSettings != nil { headerConfig, err := serial.GetInstanceOf(tcpSettings.HeaderSettings) if err != nil { return nil, newError("failed to get header settings").Base(err).AtError() } auth, err := internet.CreateConnectionAuthenticator(headerConfig) if err != nil { return nil, newError("failed to create header authenticator").Base(err).AtError() } conn = auth.Client(conn) } return internet.Connection(conn), nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, Dial)) } ================================================ FILE: transport/internet/tcp/errors.generated.go ================================================ package tcp import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tcp/hub.go ================================================ package tcp import ( "context" gotls "crypto/tls" "strings" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) // Listener is an internet.Listener that listens for TCP connections. type Listener struct { listener net.Listener tlsConfig *gotls.Config authConfig internet.ConnectionAuthenticator config *Config addConn internet.ConnHandler } // ListenTCP creates a new Listener based on configurations. func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) { l := &Listener{ addConn: handler, } tcpSettings := streamSettings.ProtocolSettings.(*Config) l.config = tcpSettings if l.config != nil { if streamSettings.SocketSettings == nil { streamSettings.SocketSettings = &internet.SocketConfig{} } streamSettings.SocketSettings.AcceptProxyProtocol = l.config.AcceptProxyProtocol } var listener net.Listener var err error if address.Family().IsDomain() { listener, err = internet.ListenSystem(ctx, &net.UnixAddr{ Name: address.Domain(), Net: "unix", }, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to listen Unix Domain Socket on ", address).Base(err) } newError("listening Unix Domain Socket on ", address).WriteToLog(session.ExportIDToError(ctx)) } else { listener, err = internet.ListenSystem(ctx, &net.TCPAddr{ IP: address.IP(), Port: int(port), }, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to listen TCP on ", address, ":", port).Base(err) } newError("listening TCP on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx)) } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) } l.listener = listener if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { l.tlsConfig = config.GetTLSConfig() } if tcpSettings.HeaderSettings != nil { headerConfig, err := serial.GetInstanceOf(tcpSettings.HeaderSettings) if err != nil { return nil, newError("invalid header settings").Base(err).AtError() } auth, err := internet.CreateConnectionAuthenticator(headerConfig) if err != nil { return nil, newError("invalid header settings.").Base(err).AtError() } l.authConfig = auth } go l.keepAccepting() return l, nil } func (v *Listener) keepAccepting() { for { conn, err := v.listener.Accept() if err != nil { errStr := err.Error() if strings.Contains(errStr, "closed") { break } newError("failed to accepted raw connections").Base(err).AtWarning().WriteToLog() if strings.Contains(errStr, "too many") { time.Sleep(time.Millisecond * 500) } continue } if v.tlsConfig != nil { conn = tls.Server(conn, v.tlsConfig) } if v.authConfig != nil { conn = v.authConfig.Server(conn) } v.addConn(internet.Connection(conn)) } } // Addr implements internet.Listener.Addr. func (v *Listener) Addr() net.Addr { return v.listener.Addr() } // Close implements internet.Listener.Close. func (v *Listener) Close() error { return v.listener.Close() } func init() { common.Must(internet.RegisterTransportListener(protocolName, ListenTCP)) } ================================================ FILE: transport/internet/tcp/sockopt_freebsd.go ================================================ //go:build freebsd // +build freebsd package tcp import ( "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) // GetOriginalDestination from tcp conn func GetOriginalDestination(conn internet.Connection) (net.Destination, error) { la := conn.LocalAddr() ra := conn.RemoteAddr() ip, port, err := internet.OriginalDst(la, ra) if err != nil { return net.Destination{}, newError("failed to get destination").Base(err) } dest := net.TCPDestination(net.IPAddress(ip), net.Port(port)) if !dest.IsValid() { return net.Destination{}, newError("failed to parse destination.") } return dest, nil } ================================================ FILE: transport/internet/tcp/sockopt_linux.go ================================================ //go:build linux // +build linux package tcp import ( "syscall" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) const SO_ORIGINAL_DST = 80 // nolint: revive,stylecheck func GetOriginalDestination(conn internet.Connection) (net.Destination, error) { sysrawconn, f := conn.(syscall.Conn) if !f { return net.Destination{}, newError("unable to get syscall.Conn") } rawConn, err := sysrawconn.SyscallConn() if err != nil { return net.Destination{}, newError("failed to get sys fd").Base(err) } var dest net.Destination err = rawConn.Control(func(fd uintptr) { var remoteIP net.IP switch addr := conn.RemoteAddr().(type) { case *net.TCPAddr: remoteIP = addr.IP case *net.UDPAddr: remoteIP = addr.IP default: newError("failed to call getsockopt").WriteToLog() return } if remoteIP.To4() != nil { // ipv4 addr, err := syscall.GetsockoptIPv6Mreq(int(fd), syscall.IPPROTO_IP, SO_ORIGINAL_DST) if err != nil { newError("failed to call getsockopt").Base(err).WriteToLog() return } ip := net.IPAddress(addr.Multiaddr[4:8]) port := uint16(addr.Multiaddr[2])<<8 + uint16(addr.Multiaddr[3]) dest = net.TCPDestination(ip, net.Port(port)) } else { // ipv6 addr, err := syscall.GetsockoptIPv6MTUInfo(int(fd), syscall.IPPROTO_IPV6, SO_ORIGINAL_DST) if err != nil { newError("failed to call getsockopt").Base(err).WriteToLog() return } ip := net.IPAddress(addr.Addr.Addr[:]) port := net.PortFromBytes([]byte{byte(addr.Addr.Port), byte(addr.Addr.Port >> 8)}) dest = net.TCPDestination(ip, port) } }) if err != nil { return net.Destination{}, newError("failed to control connection").Base(err) } if !dest.IsValid() { return net.Destination{}, newError("failed to call getsockopt") } return dest, nil } ================================================ FILE: transport/internet/tcp/sockopt_linux_test.go ================================================ //go:build linux // +build linux package tcp_test import ( "context" "strings" "testing" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" "github.com/v2fly/v2ray-core/v5/transport/internet" . "github.com/v2fly/v2ray-core/v5/transport/internet/tcp" ) func TestGetOriginalDestination(t *testing.T) { tcpServer := tcp.Server{} dest, err := tcpServer.Start() common.Must(err) defer tcpServer.Close() config, err := internet.ToMemoryStreamConfig(nil) common.Must(err) conn, err := Dial(context.Background(), dest, config) common.Must(err) defer conn.Close() originalDest, err := GetOriginalDestination(conn) if !(dest == originalDest || strings.Contains(err.Error(), "failed to call getsockopt")) { t.Error("unexpected state") } } ================================================ FILE: transport/internet/tcp/sockopt_other.go ================================================ //go:build !linux && !freebsd // +build !linux,!freebsd package tcp import ( "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func GetOriginalDestination(conn internet.Connection) (net.Destination, error) { return net.Destination{}, nil } ================================================ FILE: transport/internet/tcp/tcp.go ================================================ package tcp //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/tcp_hub.go ================================================ package internet import ( "context" "github.com/v2fly/v2ray-core/v5/common/net" ) var transportListenerCache = make(map[string]ListenFunc) func RegisterTransportListener(protocol string, listener ListenFunc) error { if _, found := transportListenerCache[protocol]; found { return newError(protocol, " listener already registered.").AtError() } transportListenerCache[protocol] = listener return nil } type ConnHandler func(Connection) type ListenFunc func(ctx context.Context, address net.Address, port net.Port, settings *MemoryStreamConfig, handler ConnHandler) (Listener, error) type Listener interface { Close() error Addr() net.Addr } // ListenUnix is the UDS version of ListenTCP func ListenUnix(ctx context.Context, address net.Address, settings *MemoryStreamConfig, handler ConnHandler) (Listener, error) { if settings == nil { s, err := ToMemoryStreamConfig(nil) if err != nil { return nil, newError("failed to create default unix stream settings").Base(err) } settings = s } protocol := settings.ProtocolName if originalProtocolName := getOriginalMessageName(settings); originalProtocolName != "" { protocol = originalProtocolName } listenFunc := transportListenerCache[protocol] if listenFunc == nil { return nil, newError(protocol, " unix istener not registered.").AtError() } listener, err := listenFunc(ctx, address, net.Port(0), settings, handler) if err != nil { return nil, newError("failed to listen on unix address: ", address).Base(err) } return listener, nil } func ListenTCP(ctx context.Context, address net.Address, port net.Port, settings *MemoryStreamConfig, handler ConnHandler) (Listener, error) { if settings == nil { s, err := ToMemoryStreamConfig(nil) if err != nil { return nil, newError("failed to create default stream settings").Base(err) } settings = s } if address.Family().IsDomain() && address.Domain() == "localhost" { address = net.LocalHostIP } if address.Family().IsDomain() { return nil, newError("domain address is not allowed for listening: ", address.Domain()) } protocol := settings.ProtocolName if originalProtocolName := getOriginalMessageName(settings); originalProtocolName != "" { protocol = originalProtocolName } listenFunc := transportListenerCache[protocol] if listenFunc == nil { return nil, newError(protocol, " listener not registered.").AtError() } listener, err := listenFunc(ctx, address, port, settings, handler) if err != nil { return nil, newError("failed to listen on address: ", address, ":", port).Base(err) } return listener, nil } // ListenSystem listens on a local address for incoming TCP connections. // // v2ray:api:beta func ListenSystem(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) { return effectiveListener.Listen(ctx, addr, sockopt) } // ListenSystemPacket listens on a local address for incoming UDP connections. // // v2ray:api:beta func ListenSystemPacket(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.PacketConn, error) { return effectiveListener.ListenPacket(ctx, addr, sockopt) } ================================================ FILE: transport/internet/tls/config.go ================================================ package tls import ( "crypto/hmac" "crypto/tls" "crypto/x509" "encoding/base64" "os" "strings" "sync" "time" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/transport/internet" ) var globalSessionCache = tls.NewLRUClientSessionCache(128) const exp8357 = "experiment:8357" // ParseCertificate converts a cert.Certificate to Certificate. func ParseCertificate(c *cert.Certificate) *Certificate { if c != nil { certPEM, keyPEM := c.ToPEM() return &Certificate{ Certificate: certPEM, Key: keyPEM, } } return nil } func (c *Config) loadSelfCertPool(usage Certificate_Usage) (*x509.CertPool, error) { root := x509.NewCertPool() for _, cert := range c.Certificate { if cert.Usage == usage { if !root.AppendCertsFromPEM(cert.Certificate) { return nil, newError("failed to append cert").AtWarning() } } } return root, nil } // BuildCertificates builds a list of TLS certificates from proto definition. func (c *Config) BuildCertificates() []tls.Certificate { certs := make([]tls.Certificate, 0, len(c.Certificate)) for _, entry := range c.Certificate { if entry.Usage != Certificate_ENCIPHERMENT { continue } keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key) if err != nil { newError("ignoring invalid X509 key pair").Base(err).AtWarning().WriteToLog() continue } certs = append(certs, keyPair) } return certs } func isCertificateExpired(c *tls.Certificate) bool { if c.Leaf == nil && len(c.Certificate) > 0 { if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil { c.Leaf = pc } } // If leaf is not there, the certificate is probably not used yet. We trust user to provide a valid certificate. return c.Leaf != nil && c.Leaf.NotAfter.Before(time.Now().Add(time.Minute*2)) } func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, error) { parent, err := cert.ParseCertificate(rawCA.Certificate, rawCA.Key) if err != nil { return nil, newError("failed to parse raw certificate").Base(err) } newCert, err := cert.Generate(parent, cert.CommonName(domain), cert.DNSNames(domain)) if err != nil { return nil, newError("failed to generate new certificate for ", domain).Base(err) } newCertPEM, newKeyPEM := newCert.ToPEM() cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM) return &cert, err } func (c *Config) getCustomCA() []*Certificate { certs := make([]*Certificate, 0, len(c.Certificate)) for _, certificate := range c.Certificate { if certificate.Usage == Certificate_AUTHORITY_ISSUE { certs = append(certs, certificate) } } return certs } func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { var access sync.RWMutex return func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { domain := hello.ServerName certExpired := false access.RLock() certificate, found := c.NameToCertificate[domain] access.RUnlock() if found { if !isCertificateExpired(certificate) { return certificate, nil } certExpired = true } if certExpired { newCerts := make([]tls.Certificate, 0, len(c.Certificates)) access.Lock() for _, certificate := range c.Certificates { cert := certificate if !isCertificateExpired(&cert) { newCerts = append(newCerts, cert) } else if cert.Leaf != nil { expTime := cert.Leaf.NotAfter.Format(time.RFC3339) newError("old certificate for ", domain, " (expire on ", expTime, ") discard").AtInfo().WriteToLog() } } c.Certificates = newCerts access.Unlock() } var issuedCertificate *tls.Certificate // Create a new certificate from existing CA if possible for _, rawCert := range ca { if rawCert.Usage == Certificate_AUTHORITY_ISSUE { newCert, err := issueCertificate(rawCert, domain) if err != nil { newError("failed to issue new certificate for ", domain).Base(err).WriteToLog() continue } parsed, err := x509.ParseCertificate(newCert.Certificate[0]) if err == nil { newCert.Leaf = parsed expTime := parsed.NotAfter.Format(time.RFC3339) newError("new certificate for ", domain, " (expire on ", expTime, ") issued").AtInfo().WriteToLog() } else { newError("failed to parse new certificate for ", domain).Base(err).WriteToLog() } access.Lock() c.Certificates = append(c.Certificates, *newCert) issuedCertificate = &c.Certificates[len(c.Certificates)-1] access.Unlock() break } } if issuedCertificate == nil { return nil, newError("failed to create a new certificate for ", domain) } access.Lock() c.BuildNameToCertificate() access.Unlock() return issuedCertificate, nil } } func (c *Config) IsExperiment8357() bool { return strings.HasPrefix(c.ServerName, exp8357) } func (c *Config) parseServerName() string { if c.IsExperiment8357() { return c.ServerName[len(exp8357):] } return c.ServerName } func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { if c.PinnedPeerCertificateChainSha256 != nil { hashValue := GenerateCertChainHash(rawCerts) for _, v := range c.PinnedPeerCertificateChainSha256 { if hmac.Equal(hashValue, v) { return nil } } return newError("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue)) } return nil } type alwaysFlushWriter struct { file *os.File } func (a *alwaysFlushWriter) Write(p []byte) (n int, err error) { n, err = a.file.Write(p) a.file.Sync() return n, err } // GetTLSConfig converts this Config into tls.Config. func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { root, err := c.getCertPool() if err != nil { newError("failed to load system root certificate").AtError().Base(err).WriteToLog() } if c == nil { return &tls.Config{ ClientSessionCache: globalSessionCache, RootCAs: root, InsecureSkipVerify: false, NextProtos: nil, SessionTicketsDisabled: true, } } clientRoot, err := c.loadSelfCertPool(Certificate_AUTHORITY_VERIFY_CLIENT) if err != nil { newError("failed to load client root certificate").AtError().Base(err).WriteToLog() } config := &tls.Config{ ClientSessionCache: globalSessionCache, RootCAs: root, InsecureSkipVerify: c.AllowInsecure, NextProtos: c.NextProtocol, SessionTicketsDisabled: !c.EnableSessionResumption, VerifyPeerCertificate: c.verifyPeerCert, ClientCAs: clientRoot, } if c.AllowInsecureIfPinnedPeerCertificate && c.PinnedPeerCertificateChainSha256 != nil { config.InsecureSkipVerify = true } for _, opt := range opts { opt(config) } config.Certificates = c.BuildCertificates() config.BuildNameToCertificate() caCerts := c.getCustomCA() if len(caCerts) > 0 { config.GetCertificate = getGetCertificateFunc(config, caCerts) } if sn := c.parseServerName(); len(sn) > 0 { config.ServerName = sn } if len(config.NextProtos) == 0 { config.NextProtos = []string{"h2", "http/1.1"} } if c.VerifyClientCertificate { config.ClientAuth = tls.RequireAndVerifyClientCert } switch c.MinVersion { case Config_TLS1_0: config.MinVersion = tls.VersionTLS10 case Config_TLS1_1: config.MinVersion = tls.VersionTLS11 case Config_TLS1_2: config.MinVersion = tls.VersionTLS12 case Config_TLS1_3: config.MinVersion = tls.VersionTLS13 } switch c.MaxVersion { case Config_TLS1_0: config.MaxVersion = tls.VersionTLS10 case Config_TLS1_1: config.MaxVersion = tls.VersionTLS11 case Config_TLS1_2: config.MaxVersion = tls.VersionTLS12 case Config_TLS1_3: config.MaxVersion = tls.VersionTLS13 } if len(c.EchConfig) > 0 || len(c.Ech_DOHserver) > 0 { err := ApplyECH(c, config) //nolint: staticcheck if err != nil { //nolint: staticcheck newError("unable to set ECH").AtError().Base(err).WriteToLog() } } if len(c.Ciphersuites) > 0 { config.CipherSuites = make([]uint16, 0, len(c.Ciphersuites)) for _, cs := range c.Ciphersuites { config.CipherSuites = append(config.CipherSuites, uint16(cs)) } } return config } // Option for building TLS config. type Option func(*tls.Config) // WithDestination sets the server name in TLS config. func WithDestination(dest net.Destination) Option { return func(config *tls.Config) { if config.ServerName == "" { switch dest.Address.Family() { case net.AddressFamilyDomain: config.ServerName = dest.Address.Domain() case net.AddressFamilyIPv4, net.AddressFamilyIPv6: config.ServerName = dest.Address.IP().String() } } } } // WithNextProto sets the ALPN values in TLS config. func WithNextProto(protocol ...string) Option { return func(config *tls.Config) { if len(config.NextProtos) == 0 { config.NextProtos = protocol } } } // ConfigFromStreamSettings fetches Config from stream settings. Nil if not found. func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config { if settings == nil { return nil } if settings.SecuritySettings == nil { return nil } // Fail close for unknown TLS settings type. // For TLS Clients, Security Engine should be used, instead of this. config := settings.SecuritySettings.(*Config) return config } ================================================ FILE: transport/internet/tls/config.pb.go ================================================ package tls import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Certificate_Usage int32 const ( Certificate_ENCIPHERMENT Certificate_Usage = 0 Certificate_AUTHORITY_VERIFY Certificate_Usage = 1 Certificate_AUTHORITY_ISSUE Certificate_Usage = 2 Certificate_AUTHORITY_VERIFY_CLIENT Certificate_Usage = 3 ) // Enum value maps for Certificate_Usage. var ( Certificate_Usage_name = map[int32]string{ 0: "ENCIPHERMENT", 1: "AUTHORITY_VERIFY", 2: "AUTHORITY_ISSUE", 3: "AUTHORITY_VERIFY_CLIENT", } Certificate_Usage_value = map[string]int32{ "ENCIPHERMENT": 0, "AUTHORITY_VERIFY": 1, "AUTHORITY_ISSUE": 2, "AUTHORITY_VERIFY_CLIENT": 3, } ) func (x Certificate_Usage) Enum() *Certificate_Usage { p := new(Certificate_Usage) *p = x return p } func (x Certificate_Usage) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Certificate_Usage) Descriptor() protoreflect.EnumDescriptor { return file_transport_internet_tls_config_proto_enumTypes[0].Descriptor() } func (Certificate_Usage) Type() protoreflect.EnumType { return &file_transport_internet_tls_config_proto_enumTypes[0] } func (x Certificate_Usage) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Certificate_Usage.Descriptor instead. func (Certificate_Usage) EnumDescriptor() ([]byte, []int) { return file_transport_internet_tls_config_proto_rawDescGZIP(), []int{0, 0} } type Config_TLSVersion int32 const ( Config_Default Config_TLSVersion = 0 Config_TLS1_0 Config_TLSVersion = 1 Config_TLS1_1 Config_TLSVersion = 2 Config_TLS1_2 Config_TLSVersion = 3 Config_TLS1_3 Config_TLSVersion = 4 ) // Enum value maps for Config_TLSVersion. var ( Config_TLSVersion_name = map[int32]string{ 0: "Default", 1: "TLS1_0", 2: "TLS1_1", 3: "TLS1_2", 4: "TLS1_3", } Config_TLSVersion_value = map[string]int32{ "Default": 0, "TLS1_0": 1, "TLS1_1": 2, "TLS1_2": 3, "TLS1_3": 4, } ) func (x Config_TLSVersion) Enum() *Config_TLSVersion { p := new(Config_TLSVersion) *p = x return p } func (x Config_TLSVersion) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Config_TLSVersion) Descriptor() protoreflect.EnumDescriptor { return file_transport_internet_tls_config_proto_enumTypes[1].Descriptor() } func (Config_TLSVersion) Type() protoreflect.EnumType { return &file_transport_internet_tls_config_proto_enumTypes[1] } func (x Config_TLSVersion) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Config_TLSVersion.Descriptor instead. func (Config_TLSVersion) EnumDescriptor() ([]byte, []int) { return file_transport_internet_tls_config_proto_rawDescGZIP(), []int{1, 0} } type Certificate struct { state protoimpl.MessageState `protogen:"open.v1"` // TLS certificate in x509 format. Certificate []byte `protobuf:"bytes,1,opt,name=Certificate,proto3" json:"Certificate,omitempty"` // TLS key in x509 format. Key []byte `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"` Usage Certificate_Usage `protobuf:"varint,3,opt,name=usage,proto3,enum=v2ray.core.transport.internet.tls.Certificate_Usage" json:"usage,omitempty"` CertificateFile string `protobuf:"bytes,96001,opt,name=certificate_file,json=certificateFile,proto3" json:"certificate_file,omitempty"` KeyFile string `protobuf:"bytes,96002,opt,name=key_file,json=keyFile,proto3" json:"key_file,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Certificate) Reset() { *x = Certificate{} mi := &file_transport_internet_tls_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Certificate) String() string { return protoimpl.X.MessageStringOf(x) } func (*Certificate) ProtoMessage() {} func (x *Certificate) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tls_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Certificate.ProtoReflect.Descriptor instead. func (*Certificate) Descriptor() ([]byte, []int) { return file_transport_internet_tls_config_proto_rawDescGZIP(), []int{0} } func (x *Certificate) GetCertificate() []byte { if x != nil { return x.Certificate } return nil } func (x *Certificate) GetKey() []byte { if x != nil { return x.Key } return nil } func (x *Certificate) GetUsage() Certificate_Usage { if x != nil { return x.Usage } return Certificate_ENCIPHERMENT } func (x *Certificate) GetCertificateFile() string { if x != nil { return x.CertificateFile } return "" } func (x *Certificate) GetKeyFile() string { if x != nil { return x.KeyFile } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // Whether or not to allow self-signed certificates. AllowInsecure bool `protobuf:"varint,1,opt,name=allow_insecure,json=allowInsecure,proto3" json:"allow_insecure,omitempty"` // List of certificates to be served on server. Certificate []*Certificate `protobuf:"bytes,2,rep,name=certificate,proto3" json:"certificate,omitempty"` // Override server name. ServerName string `protobuf:"bytes,3,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"` // Lists of string as ALPN values. NextProtocol []string `protobuf:"bytes,4,rep,name=next_protocol,json=nextProtocol,proto3" json:"next_protocol,omitempty"` // Whether or not to enable session (ticket) resumption. EnableSessionResumption bool `protobuf:"varint,5,opt,name=enable_session_resumption,json=enableSessionResumption,proto3" json:"enable_session_resumption,omitempty"` // If true, root certificates on the system will not be loaded for // verification. DisableSystemRoot bool `protobuf:"varint,6,opt,name=disable_system_root,json=disableSystemRoot,proto3" json:"disable_system_root,omitempty"` // @Document A pinned certificate chain sha256 hash. // @Document If the server's hash does not match this value, the connection will be aborted. // @Document This value replace allow_insecure. // @Critical PinnedPeerCertificateChainSha256 [][]byte `protobuf:"bytes,7,rep,name=pinned_peer_certificate_chain_sha256,json=pinnedPeerCertificateChainSha256,proto3" json:"pinned_peer_certificate_chain_sha256,omitempty"` // If true, the client is required to present a certificate. VerifyClientCertificate bool `protobuf:"varint,8,opt,name=verify_client_certificate,json=verifyClientCertificate,proto3" json:"verify_client_certificate,omitempty"` // Minimum TLS version to support. MinVersion Config_TLSVersion `protobuf:"varint,9,opt,name=min_version,json=minVersion,proto3,enum=v2ray.core.transport.internet.tls.Config_TLSVersion" json:"min_version,omitempty"` // Maximum TLS version to support. MaxVersion Config_TLSVersion `protobuf:"varint,10,opt,name=max_version,json=maxVersion,proto3,enum=v2ray.core.transport.internet.tls.Config_TLSVersion" json:"max_version,omitempty"` // Whether or not to allow self-signed certificates when pinned_peer_certificate_chain_sha256 is present. AllowInsecureIfPinnedPeerCertificate bool `protobuf:"varint,11,opt,name=allow_insecure_if_pinned_peer_certificate,json=allowInsecureIfPinnedPeerCertificate,proto3" json:"allow_insecure_if_pinned_peer_certificate,omitempty"` // ECH Config in bytes format EchConfig []byte `protobuf:"bytes,16,opt,name=ech_config,json=echConfig,proto3" json:"ech_config,omitempty"` // DOH server to query HTTPS record for ECH Ech_DOHserver string `protobuf:"bytes,17,opt,name=ech_DOHserver,json=echDOHserver,proto3" json:"ech_DOHserver,omitempty"` // domain to query for https record EchQueryDomain string `protobuf:"bytes,18,opt,name=ech_query_domain,json=echQueryDomain,proto3" json:"ech_query_domain,omitempty"` // cipher suites to to be offered or accepted. // This is an developer option. Ciphersuites []uint32 `protobuf:"varint,19,rep,packed,name=ciphersuites,proto3" json:"ciphersuites,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_tls_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tls_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_tls_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetAllowInsecure() bool { if x != nil { return x.AllowInsecure } return false } func (x *Config) GetCertificate() []*Certificate { if x != nil { return x.Certificate } return nil } func (x *Config) GetServerName() string { if x != nil { return x.ServerName } return "" } func (x *Config) GetNextProtocol() []string { if x != nil { return x.NextProtocol } return nil } func (x *Config) GetEnableSessionResumption() bool { if x != nil { return x.EnableSessionResumption } return false } func (x *Config) GetDisableSystemRoot() bool { if x != nil { return x.DisableSystemRoot } return false } func (x *Config) GetPinnedPeerCertificateChainSha256() [][]byte { if x != nil { return x.PinnedPeerCertificateChainSha256 } return nil } func (x *Config) GetVerifyClientCertificate() bool { if x != nil { return x.VerifyClientCertificate } return false } func (x *Config) GetMinVersion() Config_TLSVersion { if x != nil { return x.MinVersion } return Config_Default } func (x *Config) GetMaxVersion() Config_TLSVersion { if x != nil { return x.MaxVersion } return Config_Default } func (x *Config) GetAllowInsecureIfPinnedPeerCertificate() bool { if x != nil { return x.AllowInsecureIfPinnedPeerCertificate } return false } func (x *Config) GetEchConfig() []byte { if x != nil { return x.EchConfig } return nil } func (x *Config) GetEch_DOHserver() string { if x != nil { return x.Ech_DOHserver } return "" } func (x *Config) GetEchQueryDomain() string { if x != nil { return x.EchQueryDomain } return "" } func (x *Config) GetCiphersuites() []uint32 { if x != nil { return x.Ciphersuites } return nil } var File_transport_internet_tls_config_proto protoreflect.FileDescriptor const file_transport_internet_tls_config_proto_rawDesc = "" + "\n" + "#transport/internet/tls/config.proto\x12!v2ray.core.transport.internet.tls\x1a common/protoext/extensions.proto\"\xd8\x02\n" + "\vCertificate\x12 \n" + "\vCertificate\x18\x01 \x01(\fR\vCertificate\x12\x10\n" + "\x03Key\x18\x02 \x01(\fR\x03Key\x12J\n" + "\x05usage\x18\x03 \x01(\x0e24.v2ray.core.transport.internet.tls.Certificate.UsageR\x05usage\x12>\n" + "\x10certificate_file\x18\x81\xee\x05 \x01(\tB\x11\x82\xb5\x18\r\"\vCertificateR\x0fcertificateFile\x12&\n" + "\bkey_file\x18\x82\xee\x05 \x01(\tB\t\x82\xb5\x18\x05\"\x03KeyR\akeyFile\"a\n" + "\x05Usage\x12\x10\n" + "\fENCIPHERMENT\x10\x00\x12\x14\n" + "\x10AUTHORITY_VERIFY\x10\x01\x12\x13\n" + "\x0fAUTHORITY_ISSUE\x10\x02\x12\x1b\n" + "\x17AUTHORITY_VERIFY_CLIENT\x10\x03\"\xc4\a\n" + "\x06Config\x12-\n" + "\x0eallow_insecure\x18\x01 \x01(\bB\x06\x82\xb5\x18\x02(\x01R\rallowInsecure\x12P\n" + "\vcertificate\x18\x02 \x03(\v2..v2ray.core.transport.internet.tls.CertificateR\vcertificate\x12\x1f\n" + "\vserver_name\x18\x03 \x01(\tR\n" + "serverName\x12#\n" + "\rnext_protocol\x18\x04 \x03(\tR\fnextProtocol\x12:\n" + "\x19enable_session_resumption\x18\x05 \x01(\bR\x17enableSessionResumption\x12.\n" + "\x13disable_system_root\x18\x06 \x01(\bR\x11disableSystemRoot\x12N\n" + "$pinned_peer_certificate_chain_sha256\x18\a \x03(\fR pinnedPeerCertificateChainSha256\x12:\n" + "\x19verify_client_certificate\x18\b \x01(\bR\x17verifyClientCertificate\x12U\n" + "\vmin_version\x18\t \x01(\x0e24.v2ray.core.transport.internet.tls.Config.TLSVersionR\n" + "minVersion\x12U\n" + "\vmax_version\x18\n" + " \x01(\x0e24.v2ray.core.transport.internet.tls.Config.TLSVersionR\n" + "maxVersion\x12W\n" + ")allow_insecure_if_pinned_peer_certificate\x18\v \x01(\bR$allowInsecureIfPinnedPeerCertificate\x12\x1d\n" + "\n" + "ech_config\x18\x10 \x01(\fR\techConfig\x12#\n" + "\rech_DOHserver\x18\x11 \x01(\tR\fechDOHserver\x12(\n" + "\x10ech_query_domain\x18\x12 \x01(\tR\x0eechQueryDomain\x12\"\n" + "\fciphersuites\x18\x13 \x03(\rR\fciphersuites\"I\n" + "\n" + "TLSVersion\x12\v\n" + "\aDefault\x10\x00\x12\n" + "\n" + "\x06TLS1_0\x10\x01\x12\n" + "\n" + "\x06TLS1_1\x10\x02\x12\n" + "\n" + "\x06TLS1_2\x10\x03\x12\n" + "\n" + "\x06TLS1_3\x10\x04:\x17\x82\xb5\x18\x13\n" + "\bsecurity\x12\x03tls\x90\xff)\x01B\x84\x01\n" + "%com.v2ray.core.transport.internet.tlsP\x01Z5github.com/v2fly/v2ray-core/v5/transport/internet/tls\xaa\x02!V2Ray.Core.Transport.Internet.Tlsb\x06proto3" var ( file_transport_internet_tls_config_proto_rawDescOnce sync.Once file_transport_internet_tls_config_proto_rawDescData []byte ) func file_transport_internet_tls_config_proto_rawDescGZIP() []byte { file_transport_internet_tls_config_proto_rawDescOnce.Do(func() { file_transport_internet_tls_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_tls_config_proto_rawDesc), len(file_transport_internet_tls_config_proto_rawDesc))) }) return file_transport_internet_tls_config_proto_rawDescData } var file_transport_internet_tls_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_transport_internet_tls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_transport_internet_tls_config_proto_goTypes = []any{ (Certificate_Usage)(0), // 0: v2ray.core.transport.internet.tls.Certificate.Usage (Config_TLSVersion)(0), // 1: v2ray.core.transport.internet.tls.Config.TLSVersion (*Certificate)(nil), // 2: v2ray.core.transport.internet.tls.Certificate (*Config)(nil), // 3: v2ray.core.transport.internet.tls.Config } var file_transport_internet_tls_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.transport.internet.tls.Certificate.usage:type_name -> v2ray.core.transport.internet.tls.Certificate.Usage 2, // 1: v2ray.core.transport.internet.tls.Config.certificate:type_name -> v2ray.core.transport.internet.tls.Certificate 1, // 2: v2ray.core.transport.internet.tls.Config.min_version:type_name -> v2ray.core.transport.internet.tls.Config.TLSVersion 1, // 3: v2ray.core.transport.internet.tls.Config.max_version:type_name -> v2ray.core.transport.internet.tls.Config.TLSVersion 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_transport_internet_tls_config_proto_init() } func file_transport_internet_tls_config_proto_init() { if File_transport_internet_tls_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tls_config_proto_rawDesc), len(file_transport_internet_tls_config_proto_rawDesc)), NumEnums: 2, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_tls_config_proto_goTypes, DependencyIndexes: file_transport_internet_tls_config_proto_depIdxs, EnumInfos: file_transport_internet_tls_config_proto_enumTypes, MessageInfos: file_transport_internet_tls_config_proto_msgTypes, }.Build() File_transport_internet_tls_config_proto = out.File file_transport_internet_tls_config_proto_goTypes = nil file_transport_internet_tls_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/tls/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.tls; option csharp_namespace = "V2Ray.Core.Transport.Internet.Tls"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tls"; option java_package = "com.v2ray.core.transport.internet.tls"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Certificate { // TLS certificate in x509 format. bytes Certificate = 1; // TLS key in x509 format. bytes Key = 2; enum Usage { ENCIPHERMENT = 0; AUTHORITY_VERIFY = 1; AUTHORITY_ISSUE = 2; AUTHORITY_VERIFY_CLIENT = 3; } Usage usage = 3; string certificate_file = 96001 [(v2ray.core.common.protoext.field_opt).convert_time_read_file_into = "Certificate"]; string key_file = 96002 [(v2ray.core.common.protoext.field_opt).convert_time_read_file_into = "Key"]; } message Config { option (v2ray.core.common.protoext.message_opt).type = "security"; option (v2ray.core.common.protoext.message_opt).short_name = "tls"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; // Whether or not to allow self-signed certificates. bool allow_insecure = 1 [(v2ray.core.common.protoext.field_opt).forbidden = true]; // List of certificates to be served on server. repeated Certificate certificate = 2; // Override server name. string server_name = 3; // Lists of string as ALPN values. repeated string next_protocol = 4; // Whether or not to enable session (ticket) resumption. bool enable_session_resumption = 5; // If true, root certificates on the system will not be loaded for // verification. bool disable_system_root = 6; /* @Document A pinned certificate chain sha256 hash. @Document If the server's hash does not match this value, the connection will be aborted. @Document This value replace allow_insecure. @Critical */ repeated bytes pinned_peer_certificate_chain_sha256 = 7; // If true, the client is required to present a certificate. bool verify_client_certificate = 8; enum TLSVersion { Default = 0; TLS1_0 = 1; TLS1_1 = 2; TLS1_2 = 3; TLS1_3 = 4; } // Minimum TLS version to support. TLSVersion min_version = 9; // Maximum TLS version to support. TLSVersion max_version = 10; // Whether or not to allow self-signed certificates when pinned_peer_certificate_chain_sha256 is present. bool allow_insecure_if_pinned_peer_certificate = 11; // ECH Config in bytes format bytes ech_config = 16; // DOH server to query HTTPS record for ECH string ech_DOHserver = 17; // domain to query for https record string ech_query_domain = 18; // cipher suites to to be offered or accepted. // This is an developer option. repeated uint32 ciphersuites = 19; } ================================================ FILE: transport/internet/tls/config_other.go ================================================ //go:build !windows // +build !windows package tls import ( "crypto/x509" "sync" ) type rootCertsCache struct { sync.Mutex pool *x509.CertPool } func (c *rootCertsCache) load() (*x509.CertPool, error) { c.Lock() defer c.Unlock() if c.pool != nil { return c.pool, nil } pool, err := x509.SystemCertPool() if err != nil { return nil, err } c.pool = pool return pool, nil } var rootCerts rootCertsCache func (c *Config) getCertPool() (*x509.CertPool, error) { if c.DisableSystemRoot { return c.loadSelfCertPool(Certificate_AUTHORITY_VERIFY) } if len(c.Certificate) == 0 { return rootCerts.load() } pool, err := x509.SystemCertPool() if err != nil { return nil, newError("system root").AtWarning().Base(err) } for _, cert := range c.Certificate { if !pool.AppendCertsFromPEM(cert.Certificate) { return nil, newError("append cert to root").AtWarning().Base(err) } } return pool, err } ================================================ FILE: transport/internet/tls/config_test.go ================================================ package tls_test import ( gotls "crypto/tls" "crypto/x509" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" . "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func TestCertificateIssuing(t *testing.T) { certificate := ParseCertificate(cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))) certificate.Usage = Certificate_AUTHORITY_ISSUE c := &Config{ Certificate: []*Certificate{ certificate, }, } tlsConfig := c.GetTLSConfig() v2rayCert, err := tlsConfig.GetCertificate(&gotls.ClientHelloInfo{ ServerName: "www.v2fly.org", }) common.Must(err) x509Cert, err := x509.ParseCertificate(v2rayCert.Certificate[0]) common.Must(err) if !x509Cert.NotAfter.After(time.Now()) { t.Error("NotAfter: ", x509Cert.NotAfter) } } func TestExpiredCertificate(t *testing.T) { caCert := cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign)) expiredCert := cert.MustGenerate(caCert, cert.NotAfter(time.Now().Add(time.Minute*-2)), cert.CommonName("www.v2fly.org"), cert.DNSNames("www.v2fly.org")) certificate := ParseCertificate(caCert) certificate.Usage = Certificate_AUTHORITY_ISSUE certificate2 := ParseCertificate(expiredCert) c := &Config{ Certificate: []*Certificate{ certificate, certificate2, }, } tlsConfig := c.GetTLSConfig() v2rayCert, err := tlsConfig.GetCertificate(&gotls.ClientHelloInfo{ ServerName: "www.v2fly.org", }) common.Must(err) x509Cert, err := x509.ParseCertificate(v2rayCert.Certificate[0]) common.Must(err) if !x509Cert.NotAfter.After(time.Now()) { t.Error("NotAfter: ", x509Cert.NotAfter) } } func TestInsecureCertificates(t *testing.T) { c := &Config{} tlsConfig := c.GetTLSConfig() if len(tlsConfig.CipherSuites) > 0 { t.Fatal("Unexpected tls cipher suites list: ", tlsConfig.CipherSuites) } } func BenchmarkCertificateIssuing(b *testing.B) { certificate := ParseCertificate(cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))) certificate.Usage = Certificate_AUTHORITY_ISSUE c := &Config{ Certificate: []*Certificate{ certificate, }, } tlsConfig := c.GetTLSConfig() lenCerts := len(tlsConfig.Certificates) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = tlsConfig.GetCertificate(&gotls.ClientHelloInfo{ ServerName: "www.v2fly.org", }) delete(tlsConfig.NameToCertificate, "www.v2fly.org") tlsConfig.Certificates = tlsConfig.Certificates[:lenCerts] } } ================================================ FILE: transport/internet/tls/config_windows.go ================================================ //go:build windows // +build windows package tls import "crypto/x509" func (c *Config) getCertPool() (*x509.CertPool, error) { if c.DisableSystemRoot { return c.loadSelfCertPool(Certificate_AUTHORITY_VERIFY) } return nil, nil } ================================================ FILE: transport/internet/tls/ech.go ================================================ //go:build go1.23 // +build go1.23 package tls import ( "bytes" "context" "crypto/tls" "io" "net/http" "sync" "time" "github.com/miekg/dns" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func ApplyECH(c *Config, config *tls.Config) error { var ECHConfig []byte var err error var domain string if len(c.EchConfig) > 0 { ECHConfig = c.EchConfig } else { // ECH config > DOH lookup if c.EchQueryDomain == "" { domain = config.ServerName } else { domain = c.EchQueryDomain } addr := net.ParseAddress(domain) if !addr.Family().IsDomain() { return newError("Using DOH for ECH needs SNI") } ECHConfig, err = QueryRecord(addr.Domain(), c.Ech_DOHserver) if err != nil { return err } } config.EncryptedClientHelloConfigList = ECHConfig return nil } type record struct { record []byte expire time.Time } var ( dnsCache = make(map[string]record) mutex sync.RWMutex ) func QueryRecord(domain string, server string) ([]byte, error) { mutex.Lock() rec, found := dnsCache[domain] if found && rec.expire.After(time.Now()) { mutex.Unlock() return rec.record, nil } mutex.Unlock() newError("Trying to query ECH config for domain: ", domain, " with ECH server: ", server).AtDebug().WriteToLog() record, ttl, err := dohQuery(server, domain) if err != nil { return []byte{}, err } if ttl < 600 { ttl = 600 } mutex.Lock() defer mutex.Unlock() rec.record = record rec.expire = time.Now().Add(time.Second * time.Duration(ttl)) dnsCache[domain] = rec return record, nil } func dohQuery(server string, domain string) ([]byte, uint32, error) { m := new(dns.Msg) m.SetQuestion(dns.Fqdn(domain), dns.TypeHTTPS) m.Id = 0 msg, err := m.Pack() if err != nil { return []byte{}, 0, err } tr := &http.Transport{ IdleConnTimeout: 90 * time.Second, ForceAttemptHTTP2: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { dest, err := net.ParseDestination(network + ":" + addr) if err != nil { return nil, err } conn, err := internet.DialSystem(ctx, dest, nil) if err != nil { return nil, err } return conn, nil }, } client := &http.Client{ Timeout: 5 * time.Second, Transport: tr, } req, err := http.NewRequest("POST", server, bytes.NewReader(msg)) if err != nil { return []byte{}, 0, err } req.Header.Set("Content-Type", "application/dns-message") resp, err := client.Do(req) if err != nil { return []byte{}, 0, err } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return []byte{}, 0, err } if resp.StatusCode != http.StatusOK { return []byte{}, 0, newError("query failed with response code:", resp.StatusCode) } respMsg := new(dns.Msg) err = respMsg.Unpack(respBody) if err != nil { return []byte{}, 0, err } if len(respMsg.Answer) > 0 { for _, answer := range respMsg.Answer { if https, ok := answer.(*dns.HTTPS); ok && https.Hdr.Name == dns.Fqdn(domain) { for _, v := range https.Value { if echConfig, ok := v.(*dns.SVCBECHConfig); ok { newError(context.Background(), "Get ECH config:", echConfig.String(), " TTL:", respMsg.Answer[0].Header().Ttl).AtDebug().WriteToLog() return echConfig.ECH, answer.Header().Ttl, nil } } } } } return []byte{}, 0, newError("no ech record found") } ================================================ FILE: transport/internet/tls/ech_go122.go ================================================ //go:build !go1.23 // +build !go1.23 package tls import ( "crypto/tls" ) func ApplyECH(c *Config, config *tls.Config) error { //nolint: staticcheck return newError("using ECH require go 1.23 or higher") } ================================================ FILE: transport/internet/tls/engine.go ================================================ package tls import ( "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet/security" ) type Engine struct { config *Config } func (e *Engine) Client(conn net.Conn, opts ...security.Option) (security.Conn, error) { var options []Option for _, v := range opts { switch s := v.(type) { case security.OptionWithALPN: options = append(options, WithNextProto(s.ALPNs...)) case security.OptionWithDestination: options = append(options, WithDestination(s.Dest)) default: return nil, newError("unknown option") } } tlsConn := Client(conn, e.config.GetTLSConfig(options...)) return tlsConn, nil } func NewTLSSecurityEngineFromConfig(config *Config) (security.Engine, error) { return &Engine{config: config}, nil } ================================================ FILE: transport/internet/tls/errors.generated.go ================================================ package tls import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tls/pin.go ================================================ package tls import ( "crypto/sha256" "encoding/base64" "encoding/pem" ) func CalculatePEMCertChainSHA256Hash(certContent []byte) string { var certChain [][]byte for { block, remain := pem.Decode(certContent) if block == nil { break } certChain = append(certChain, block.Bytes) certContent = remain } certChainHash := GenerateCertChainHash(certChain) certChainHashB64 := base64.StdEncoding.EncodeToString(certChainHash) return certChainHashB64 } func GenerateCertChainHash(rawCerts [][]byte) []byte { var hashValue []byte for _, certValue := range rawCerts { out := sha256.Sum256(certValue) if hashValue == nil { hashValue = out[:] } else { newHashValue := sha256.Sum256(append(hashValue, out[:]...)) hashValue = newHashValue[:] } } return hashValue } ================================================ FILE: transport/internet/tls/pin_test.go ================================================ package tls import ( "testing" "github.com/stretchr/testify/assert" ) func TestCalculateCertHash(t *testing.T) { /* This is used to make sure that the hash signature generated is consistent Do NOT change this test to suit your modification. */ const CertBundle = ` -----BEGIN CERTIFICATE----- MIIFJjCCBA6gAwIBAgISBL8FgUdEcVYEjdMkTZPgC3ocMA0GCSqGSIb3DQEBCwUA MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD EwJSMzAeFw0yMTAzMjkwMTM2MzlaFw0yMTA2MjcwMTM2MzlaMBsxGTAXBgNVBAMT EHNlY3VyZS5ra2Rldi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB AQDOF/j+s7rHaDMXdhYjffoOFjNZb7n3sCuvubI3qOcgJmr1WPlCEry50KoY8FaB IF2HstMIZceN4NoUK7mr3WAvsQTA47uBfuhp+XQmAQW0T/fYD+XbvxtCENEin+xm JsvTKZLTKbE08E964J4H+1sBmueP6rvy2Wt95z0XkqoQiikpmLE87WdltQcATvVX qqrL64hV0nN4Hdi2Bv1cQ92aR7lZGj8jiQRtTj8y5Ah3Gk3fPoao+yI7gnzembqo fddePzz/u8iEuvYAsIYZKn9bbS7rkYoJazL2/xiDZR7usn0SomzmM6lGXDD3FF4b lyTkLYwgFVgbGWoz1+eOHD5BAgMBAAGjggJLMIICRzAOBgNVHQ8BAf8EBAMCBaAw HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYD VR0OBBYEFOPRdApL8XENLXDuU3oPisykGyp+MB8GA1UdIwQYMBaAFBQusxe3WFbL rlAJQOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDov L3IzLm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5v cmcvMBsGA1UdEQQUMBKCEHNlY3VyZS5ra2Rldi5vcmcwTAYDVR0gBEUwQzAIBgZn gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s ZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdQD2XJQv0Xcw IhRUGAgwlFaO400TGTO/3wwvIAvMTvFk4wAAAXh71yBGAAAEAwBGMEQCIDmziDOn ehPY2KoAFX8fPWiCm4EBTbGJXBWF1LCotPJBAiBLSCg+krXvbyoABnTm8knv0hbG /ZOk8LV6qpw9VoQwGwB3AG9Tdqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kT AAABeHvXIIAAAAQDAEgwRgIhAOkeKc52wR3n5QWZfa3zbbicMMSQrTGbQ+1fHNs7 SsRvAiEAqbflDx1nZRsTA22FfNYfgF6v5Z3/svjiTleWSQad4WswDQYJKoZIhvcN AQELBQADggEBAEj8tg+Agf5sNBM9CvjeXbA0fkpGDaQzXEkwefAea5vPgKoGiWSN pHDkyr0i7+mqa7cMXhmmo20V0/+QDv0nrsCw8pgJuviC3GvK6agT6WfkXM2djJuo osPeXOL9KEF/ATv0EyM5tr9TIoRSSYQoRhuqEdg3Dz9Ii8GXR5HhbYr6Cd7gwNHS kYeivKDmgv31GHb4twPSE9LZ/U+56lNVvSbJ4csupIF3GnxnxrFSmijYNOPoM6mj tzY45d4mjPs0fKCFKSsVM6YT0tX4NwIKsOaeQg30WLtRyDwYm6ma/a/UUUS0FloZ 2/p85glOgzownfoRjzTbqHu8ewtMd6Apc0E= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/ MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7 gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel /xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0 LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8 S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg== -----END CERTIFICATE----- ` t.Run("bundle", func(t *testing.T) { hash := CalculatePEMCertChainSHA256Hash([]byte(CertBundle)) assert.Equal(t, "WF65fBkgltadMnXryOMZ6TEYeV4d5Q0uu4SGXGZ0RjI=", hash) }) const Single = `-----BEGIN CERTIFICATE----- MIIFJjCCBA6gAwIBAgISBL8FgUdEcVYEjdMkTZPgC3ocMA0GCSqGSIb3DQEBCwUA MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD EwJSMzAeFw0yMTAzMjkwMTM2MzlaFw0yMTA2MjcwMTM2MzlaMBsxGTAXBgNVBAMT EHNlY3VyZS5ra2Rldi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB AQDOF/j+s7rHaDMXdhYjffoOFjNZb7n3sCuvubI3qOcgJmr1WPlCEry50KoY8FaB IF2HstMIZceN4NoUK7mr3WAvsQTA47uBfuhp+XQmAQW0T/fYD+XbvxtCENEin+xm JsvTKZLTKbE08E964J4H+1sBmueP6rvy2Wt95z0XkqoQiikpmLE87WdltQcATvVX qqrL64hV0nN4Hdi2Bv1cQ92aR7lZGj8jiQRtTj8y5Ah3Gk3fPoao+yI7gnzembqo fddePzz/u8iEuvYAsIYZKn9bbS7rkYoJazL2/xiDZR7usn0SomzmM6lGXDD3FF4b lyTkLYwgFVgbGWoz1+eOHD5BAgMBAAGjggJLMIICRzAOBgNVHQ8BAf8EBAMCBaAw HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYD VR0OBBYEFOPRdApL8XENLXDuU3oPisykGyp+MB8GA1UdIwQYMBaAFBQusxe3WFbL rlAJQOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDov L3IzLm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5v cmcvMBsGA1UdEQQUMBKCEHNlY3VyZS5ra2Rldi5vcmcwTAYDVR0gBEUwQzAIBgZn gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s ZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdQD2XJQv0Xcw IhRUGAgwlFaO400TGTO/3wwvIAvMTvFk4wAAAXh71yBGAAAEAwBGMEQCIDmziDOn ehPY2KoAFX8fPWiCm4EBTbGJXBWF1LCotPJBAiBLSCg+krXvbyoABnTm8knv0hbG /ZOk8LV6qpw9VoQwGwB3AG9Tdqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kT AAABeHvXIIAAAAQDAEgwRgIhAOkeKc52wR3n5QWZfa3zbbicMMSQrTGbQ+1fHNs7 SsRvAiEAqbflDx1nZRsTA22FfNYfgF6v5Z3/svjiTleWSQad4WswDQYJKoZIhvcN AQELBQADggEBAEj8tg+Agf5sNBM9CvjeXbA0fkpGDaQzXEkwefAea5vPgKoGiWSN pHDkyr0i7+mqa7cMXhmmo20V0/+QDv0nrsCw8pgJuviC3GvK6agT6WfkXM2djJuo osPeXOL9KEF/ATv0EyM5tr9TIoRSSYQoRhuqEdg3Dz9Ii8GXR5HhbYr6Cd7gwNHS kYeivKDmgv31GHb4twPSE9LZ/U+56lNVvSbJ4csupIF3GnxnxrFSmijYNOPoM6mj tzY45d4mjPs0fKCFKSsVM6YT0tX4NwIKsOaeQg30WLtRyDwYm6ma/a/UUUS0FloZ 2/p85glOgzownfoRjzTbqHu8ewtMd6Apc0E= -----END CERTIFICATE----- ` t.Run("single", func(t *testing.T) { hash := CalculatePEMCertChainSHA256Hash([]byte(Single)) assert.Equal(t, "FW3SVMCL6um2wVltOdgJ3DpI82aredw83YoCblkMkVM=", hash) }) } ================================================ FILE: transport/internet/tls/tls.go ================================================ package tls import ( "context" "crypto/tls" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen var _ buf.Writer = (*Conn)(nil) type Conn struct { *tls.Conn } func (c *Conn) GetConnectionApplicationProtocol() (string, error) { if err := c.Handshake(); err != nil { return "", err } return c.ConnectionState().NegotiatedProtocol, nil } func (c *Conn) WriteMultiBuffer(mb buf.MultiBuffer) error { mb = buf.Compact(mb) mb, err := buf.WriteMultiBuffer(c, mb) buf.ReleaseMulti(mb) return err } func (c *Conn) HandshakeAddress() net.Address { if err := c.Handshake(); err != nil { return nil } state := c.ConnectionState() if state.ServerName == "" { return nil } return net.ParseAddress(state.ServerName) } // Client initiates a TLS client handshake on the given connection. func Client(c net.Conn, config *tls.Config) *Conn { tlsConn := tls.Client(c, config) return &Conn{Conn: tlsConn} } /* func copyConfig(c *tls.Config) *utls.Config { return &utls.Config{ NextProtos: c.NextProtos, ServerName: c.ServerName, InsecureSkipVerify: c.InsecureSkipVerify, MinVersion: utls.VersionTLS12, MaxVersion: utls.VersionTLS12, } } func UClient(c net.Conn, config *tls.Config) net.Conn { uConfig := copyConfig(config) return utls.Client(c, uConfig) } */ // Server initiates a TLS server handshake on the given connection. func Server(c net.Conn, config *tls.Config) net.Conn { tlsConn := tls.Server(c, config) return &Conn{Conn: tlsConn} } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewTLSSecurityEngineFromConfig(config.(*Config)) })) } ================================================ FILE: transport/internet/tls/utls/config.pb.go ================================================ package utls import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" tls "github.com/v2fly/v2ray-core/v5/transport/internet/tls" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ForcedALPN int32 const ( ForcedALPN_TRANSPORT_PREFERENCE_TAKE_PRIORITY ForcedALPN = 0 ForcedALPN_NO_ALPN ForcedALPN = 1 ForcedALPN_UTLS_PRESET ForcedALPN = 2 ) // Enum value maps for ForcedALPN. var ( ForcedALPN_name = map[int32]string{ 0: "TRANSPORT_PREFERENCE_TAKE_PRIORITY", 1: "NO_ALPN", 2: "UTLS_PRESET", } ForcedALPN_value = map[string]int32{ "TRANSPORT_PREFERENCE_TAKE_PRIORITY": 0, "NO_ALPN": 1, "UTLS_PRESET": 2, } ) func (x ForcedALPN) Enum() *ForcedALPN { p := new(ForcedALPN) *p = x return p } func (x ForcedALPN) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ForcedALPN) Descriptor() protoreflect.EnumDescriptor { return file_transport_internet_tls_utls_config_proto_enumTypes[0].Descriptor() } func (ForcedALPN) Type() protoreflect.EnumType { return &file_transport_internet_tls_utls_config_proto_enumTypes[0] } func (x ForcedALPN) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ForcedALPN.Descriptor instead. func (ForcedALPN) EnumDescriptor() ([]byte, []int) { return file_transport_internet_tls_utls_config_proto_rawDescGZIP(), []int{0} } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` TlsConfig *tls.Config `protobuf:"bytes,1,opt,name=tls_config,json=tlsConfig,proto3" json:"tls_config,omitempty"` Imitate string `protobuf:"bytes,2,opt,name=imitate,proto3" json:"imitate,omitempty"` NoSNI bool `protobuf:"varint,3,opt,name=noSNI,proto3" json:"noSNI,omitempty"` ForceAlpn ForcedALPN `protobuf:"varint,4,opt,name=force_alpn,json=forceAlpn,proto3,enum=v2ray.core.transport.internet.tls.utls.ForcedALPN" json:"force_alpn,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_tls_utls_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tls_utls_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_tls_utls_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetTlsConfig() *tls.Config { if x != nil { return x.TlsConfig } return nil } func (x *Config) GetImitate() string { if x != nil { return x.Imitate } return "" } func (x *Config) GetNoSNI() bool { if x != nil { return x.NoSNI } return false } func (x *Config) GetForceAlpn() ForcedALPN { if x != nil { return x.ForceAlpn } return ForcedALPN_TRANSPORT_PREFERENCE_TAKE_PRIORITY } var File_transport_internet_tls_utls_config_proto protoreflect.FileDescriptor const file_transport_internet_tls_utls_config_proto_rawDesc = "" + "\n" + "(transport/internet/tls/utls/config.proto\x12&v2ray.core.transport.internet.tls.utls\x1a common/protoext/extensions.proto\x1a#transport/internet/tls/config.proto\"\xef\x01\n" + "\x06Config\x12H\n" + "\n" + "tls_config\x18\x01 \x01(\v2).v2ray.core.transport.internet.tls.ConfigR\ttlsConfig\x12\x18\n" + "\aimitate\x18\x02 \x01(\tR\aimitate\x12\x14\n" + "\x05noSNI\x18\x03 \x01(\bR\x05noSNI\x12Q\n" + "\n" + "force_alpn\x18\x04 \x01(\x0e22.v2ray.core.transport.internet.tls.utls.ForcedALPNR\tforceAlpn:\x18\x82\xb5\x18\x14\n" + "\bsecurity\x12\x04utls\x90\xff)\x01*R\n" + "\n" + "ForcedALPN\x12&\n" + "\"TRANSPORT_PREFERENCE_TAKE_PRIORITY\x10\x00\x12\v\n" + "\aNO_ALPN\x10\x01\x12\x0f\n" + "\vUTLS_PRESET\x10\x02B\x93\x01\n" + "*com.v2ray.core.transport.internet.tls.utlsP\x01Z:github.com/v2fly/v2ray-core/v5/transport/internet/tls/utls\xaa\x02&V2Ray.Core.Transport.Internet.Tls.UTlsb\x06proto3" var ( file_transport_internet_tls_utls_config_proto_rawDescOnce sync.Once file_transport_internet_tls_utls_config_proto_rawDescData []byte ) func file_transport_internet_tls_utls_config_proto_rawDescGZIP() []byte { file_transport_internet_tls_utls_config_proto_rawDescOnce.Do(func() { file_transport_internet_tls_utls_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_tls_utls_config_proto_rawDesc), len(file_transport_internet_tls_utls_config_proto_rawDesc))) }) return file_transport_internet_tls_utls_config_proto_rawDescData } var file_transport_internet_tls_utls_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_transport_internet_tls_utls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_tls_utls_config_proto_goTypes = []any{ (ForcedALPN)(0), // 0: v2ray.core.transport.internet.tls.utls.ForcedALPN (*Config)(nil), // 1: v2ray.core.transport.internet.tls.utls.Config (*tls.Config)(nil), // 2: v2ray.core.transport.internet.tls.Config } var file_transport_internet_tls_utls_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.transport.internet.tls.utls.Config.tls_config:type_name -> v2ray.core.transport.internet.tls.Config 0, // 1: v2ray.core.transport.internet.tls.utls.Config.force_alpn:type_name -> v2ray.core.transport.internet.tls.utls.ForcedALPN 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_transport_internet_tls_utls_config_proto_init() } func file_transport_internet_tls_utls_config_proto_init() { if File_transport_internet_tls_utls_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tls_utls_config_proto_rawDesc), len(file_transport_internet_tls_utls_config_proto_rawDesc)), NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_tls_utls_config_proto_goTypes, DependencyIndexes: file_transport_internet_tls_utls_config_proto_depIdxs, EnumInfos: file_transport_internet_tls_utls_config_proto_enumTypes, MessageInfos: file_transport_internet_tls_utls_config_proto_msgTypes, }.Build() File_transport_internet_tls_utls_config_proto = out.File file_transport_internet_tls_utls_config_proto_goTypes = nil file_transport_internet_tls_utls_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/tls/utls/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.tls.utls; option csharp_namespace = "V2Ray.Core.Transport.Internet.Tls.UTls"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tls/utls"; option java_package = "com.v2ray.core.transport.internet.tls.utls"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "transport/internet/tls/config.proto"; enum ForcedALPN{ TRANSPORT_PREFERENCE_TAKE_PRIORITY = 0; NO_ALPN = 1; UTLS_PRESET = 2; } message Config { option (v2ray.core.common.protoext.message_opt).type = "security"; option (v2ray.core.common.protoext.message_opt).short_name = "utls"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; v2ray.core.transport.internet.tls.Config tls_config = 1; string imitate = 2; bool noSNI = 3; ForcedALPN force_alpn = 4; } ================================================ FILE: transport/internet/tls/utls/errors.generated.go ================================================ package utls import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tls/utls/nameMapper.go ================================================ package utls import utls "github.com/refraction-networking/utls" var clientHelloIDMap = map[string]*utls.ClientHelloID{ "randomized": &utls.HelloRandomized, "randomizedalpn": &utls.HelloRandomizedALPN, "randomizednoalpn": &utls.HelloRandomizedNoALPN, "firefox_auto": &utls.HelloFirefox_Auto, "firefox_55": &utls.HelloFirefox_55, "firefox_56": &utls.HelloFirefox_56, "firefox_63": &utls.HelloFirefox_63, "firefox_65": &utls.HelloFirefox_65, "firefox_99": &utls.HelloFirefox_99, "firefox_102": &utls.HelloFirefox_102, "firefox_105": &utls.HelloFirefox_105, "chrome_auto": &utls.HelloChrome_Auto, "chrome_58": &utls.HelloChrome_58, "chrome_62": &utls.HelloChrome_62, "chrome_70": &utls.HelloChrome_70, "chrome_72": &utls.HelloChrome_72, "chrome_83": &utls.HelloChrome_83, "chrome_87": &utls.HelloChrome_87, "chrome_96": &utls.HelloChrome_96, "chrome_100": &utls.HelloChrome_100, "chrome_102": &utls.HelloChrome_102, "ios_auto": &utls.HelloIOS_Auto, "ios_11_1": &utls.HelloIOS_11_1, "ios_12_1": &utls.HelloIOS_12_1, "ios_13": &utls.HelloIOS_13, "ios_14": &utls.HelloIOS_14, "android_11_okhttp": &utls.HelloAndroid_11_OkHttp, "edge_auto": &utls.HelloEdge_Auto, "edge_85": &utls.HelloEdge_85, "edge_106": &utls.HelloEdge_106, "safari_auto": &utls.HelloSafari_Auto, "safari_16_0": &utls.HelloSafari_16_0, "360_auto": &utls.Hello360_Auto, "360_7_5": &utls.Hello360_7_5, "360_11_0": &utls.Hello360_11_0, "qq_auto": &utls.HelloQQ_Auto, "qq_11_1": &utls.HelloQQ_11_1, } func nameToUTLSPreset(name string) (*utls.ClientHelloID, error) { preset, ok := clientHelloIDMap[name] if !ok { return nil, newError("unknown preset name") } return preset, nil } ================================================ FILE: transport/internet/tls/utls/utls.go ================================================ package utls import ( "context" systls "crypto/tls" utls "github.com/refraction-networking/utls" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet/security" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func NewUTLSSecurityEngineFromConfig(config *Config) (security.Engine, error) { if config.TlsConfig == nil { return nil, newError("mandatory field tls_config is not specified") } return &Engine{config: config}, nil } type Engine struct { config *Config } func (e Engine) Client(conn net.Conn, opts ...security.Option) (security.Conn, error) { var options []tls.Option for _, v := range opts { switch s := v.(type) { case security.OptionWithALPN: if e.config.ForceAlpn == ForcedALPN_TRANSPORT_PREFERENCE_TAKE_PRIORITY { options = append(options, tls.WithNextProto(s.ALPNs...)) } case security.OptionWithDestination: options = append(options, tls.WithDestination(s.Dest)) default: return nil, newError("unknown option") } } tlsConfig := e.config.TlsConfig.GetTLSConfig(options...) utlsConfig, err := uTLSConfigFromTLSConfig(tlsConfig) if err != nil { return nil, newError("unable to generate utls config from tls config").Base(err) } preset, err := nameToUTLSPreset(e.config.Imitate) if err != nil { return nil, newError("unable to get utls preset from name").Base(err) } utlsClientConn := utls.UClient(conn, utlsConfig, *preset) if e.config.NoSNI { err = utlsClientConn.RemoveSNIExtension() if err != nil { return nil, newError("unable to remove server name indication from utls client hello").Base(err) } } err = utlsClientConn.BuildHandshakeState() if err != nil { return nil, newError("unable to build utls handshake state").Base(err) } // ALPN is necessary for protocols like websocket to work. The uTLS setting may be overwritten on call into // BuildHandshakeState, so we need to check the original tls settings. if tlsConfig.NextProtos != nil { for n, v := range utlsClientConn.Extensions { if aplnExtension, ok := v.(*utls.ALPNExtension); ok { if e.config.ForceAlpn == ForcedALPN_TRANSPORT_PREFERENCE_TAKE_PRIORITY { aplnExtension.AlpnProtocols = tlsConfig.NextProtos break } if e.config.ForceAlpn == ForcedALPN_NO_ALPN { utlsClientConn.Extensions = append(utlsClientConn.Extensions[:n], utlsClientConn.Extensions[n+1:]...) break } } } } err = utlsClientConn.BuildHandshakeState() if err != nil { return nil, newError("unable to build utls handshake state after modification").Base(err) } err = utlsClientConn.Handshake() if err != nil { return nil, newError("unable to finish utls handshake").Base(err) } return uTLSClientConnection{utlsClientConn}, nil } type uTLSClientConnection struct { *utls.UConn } func (u uTLSClientConnection) GetConnectionApplicationProtocol() (string, error) { if err := u.Handshake(); err != nil { return "", err } return u.ConnectionState().NegotiatedProtocol, nil } func uTLSConfigFromTLSConfig(config *systls.Config) (*utls.Config, error) { // nolint: unparam uconfig := &utls.Config{ Rand: config.Rand, Time: config.Time, RootCAs: config.RootCAs, NextProtos: config.NextProtos, ServerName: config.ServerName, VerifyPeerCertificate: config.VerifyPeerCertificate, InsecureSkipVerify: config.InsecureSkipVerify, ClientAuth: utls.ClientAuthType(config.ClientAuth), ClientCAs: config.ClientCAs, } return uconfig, nil } func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewUTLSSecurityEngineFromConfig(config.(*Config)) })) } ================================================ FILE: transport/internet/tlsmirror/data.pb.go ================================================ package tlsmirror import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type EnrollmentConfirmationReq struct { state protoimpl.MessageState `protogen:"open.v1"` ServerIdentifier []byte `protobuf:"bytes,1,opt,name=server_identifier,json=serverIdentifier,proto3" json:"server_identifier,omitempty"` CarrierTlsConnectionClientRandom []byte `protobuf:"bytes,2,opt,name=carrier_tls_connection_client_random,json=carrierTlsConnectionClientRandom,proto3" json:"carrier_tls_connection_client_random,omitempty"` CarrierTlsConnectionServerRandom []byte `protobuf:"bytes,3,opt,name=carrier_tls_connection_server_random,json=carrierTlsConnectionServerRandom,proto3" json:"carrier_tls_connection_server_random,omitempty"` ClientIdentifier []byte `protobuf:"bytes,4,opt,name=client_identifier,json=clientIdentifier,proto3" json:"client_identifier,omitempty"` // Implicated ReplyAddressTag []byte `protobuf:"bytes,5,opt,name=reply_address_tag,json=replyAddressTag,proto3" json:"reply_address_tag,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EnrollmentConfirmationReq) Reset() { *x = EnrollmentConfirmationReq{} mi := &file_transport_internet_tlsmirror_data_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EnrollmentConfirmationReq) String() string { return protoimpl.X.MessageStringOf(x) } func (*EnrollmentConfirmationReq) ProtoMessage() {} func (x *EnrollmentConfirmationReq) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_data_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EnrollmentConfirmationReq.ProtoReflect.Descriptor instead. func (*EnrollmentConfirmationReq) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_data_proto_rawDescGZIP(), []int{0} } func (x *EnrollmentConfirmationReq) GetServerIdentifier() []byte { if x != nil { return x.ServerIdentifier } return nil } func (x *EnrollmentConfirmationReq) GetCarrierTlsConnectionClientRandom() []byte { if x != nil { return x.CarrierTlsConnectionClientRandom } return nil } func (x *EnrollmentConfirmationReq) GetCarrierTlsConnectionServerRandom() []byte { if x != nil { return x.CarrierTlsConnectionServerRandom } return nil } func (x *EnrollmentConfirmationReq) GetClientIdentifier() []byte { if x != nil { return x.ClientIdentifier } return nil } func (x *EnrollmentConfirmationReq) GetReplyAddressTag() []byte { if x != nil { return x.ReplyAddressTag } return nil } type EnrollmentConfirmationResp struct { state protoimpl.MessageState `protogen:"open.v1"` Enrolled bool `protobuf:"varint,1,opt,name=enrolled,proto3" json:"enrolled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EnrollmentConfirmationResp) Reset() { *x = EnrollmentConfirmationResp{} mi := &file_transport_internet_tlsmirror_data_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EnrollmentConfirmationResp) String() string { return protoimpl.X.MessageStringOf(x) } func (*EnrollmentConfirmationResp) ProtoMessage() {} func (x *EnrollmentConfirmationResp) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_data_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EnrollmentConfirmationResp.ProtoReflect.Descriptor instead. func (*EnrollmentConfirmationResp) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_data_proto_rawDescGZIP(), []int{1} } func (x *EnrollmentConfirmationResp) GetEnrolled() bool { if x != nil { return x.Enrolled } return false } var File_transport_internet_tlsmirror_data_proto protoreflect.FileDescriptor const file_transport_internet_tlsmirror_data_proto_rawDesc = "" + "\n" + "'transport/internet/tlsmirror/data.proto\x12'v2ray.core.transport.internet.tlsmirror\"\xc1\x02\n" + "\x19EnrollmentConfirmationReq\x12+\n" + "\x11server_identifier\x18\x01 \x01(\fR\x10serverIdentifier\x12N\n" + "$carrier_tls_connection_client_random\x18\x02 \x01(\fR carrierTlsConnectionClientRandom\x12N\n" + "$carrier_tls_connection_server_random\x18\x03 \x01(\fR carrierTlsConnectionServerRandom\x12+\n" + "\x11client_identifier\x18\x04 \x01(\fR\x10clientIdentifier\x12*\n" + "\x11reply_address_tag\x18\x05 \x01(\fR\x0freplyAddressTag\"8\n" + "\x1aEnrollmentConfirmationResp\x12\x1a\n" + "\benrolled\x18\x01 \x01(\bR\benrolledB\x96\x01\n" + "+com.v2ray.core.transport.internet.tlsmirrorP\x01Z;github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror\xaa\x02'V2Ray.Core.Transport.Internet.Tlsmirrorb\x06proto3" var ( file_transport_internet_tlsmirror_data_proto_rawDescOnce sync.Once file_transport_internet_tlsmirror_data_proto_rawDescData []byte ) func file_transport_internet_tlsmirror_data_proto_rawDescGZIP() []byte { file_transport_internet_tlsmirror_data_proto_rawDescOnce.Do(func() { file_transport_internet_tlsmirror_data_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_data_proto_rawDesc), len(file_transport_internet_tlsmirror_data_proto_rawDesc))) }) return file_transport_internet_tlsmirror_data_proto_rawDescData } var file_transport_internet_tlsmirror_data_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_transport_internet_tlsmirror_data_proto_goTypes = []any{ (*EnrollmentConfirmationReq)(nil), // 0: v2ray.core.transport.internet.tlsmirror.EnrollmentConfirmationReq (*EnrollmentConfirmationResp)(nil), // 1: v2ray.core.transport.internet.tlsmirror.EnrollmentConfirmationResp } var file_transport_internet_tlsmirror_data_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_tlsmirror_data_proto_init() } func file_transport_internet_tlsmirror_data_proto_init() { if File_transport_internet_tlsmirror_data_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_data_proto_rawDesc), len(file_transport_internet_tlsmirror_data_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_tlsmirror_data_proto_goTypes, DependencyIndexes: file_transport_internet_tlsmirror_data_proto_depIdxs, MessageInfos: file_transport_internet_tlsmirror_data_proto_msgTypes, }.Build() File_transport_internet_tlsmirror_data_proto = out.File file_transport_internet_tlsmirror_data_proto_goTypes = nil file_transport_internet_tlsmirror_data_proto_depIdxs = nil } ================================================ FILE: transport/internet/tlsmirror/data.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.tlsmirror; option csharp_namespace = "V2Ray.Core.Transport.Internet.Tlsmirror"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror"; option java_package = "com.v2ray.core.transport.internet.tlsmirror"; option java_multiple_files = true; message EnrollmentConfirmationReq{ bytes server_identifier = 1; bytes carrier_tls_connection_client_random = 2; bytes carrier_tls_connection_server_random = 3; bytes client_identifier = 4; // Implicated bytes reply_address_tag = 5; } message EnrollmentConfirmationResp{ bool enrolled = 1; } ================================================ FILE: transport/internet/tlsmirror/httponconnection/errors.generated.go ================================================ package httponconnection import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tlsmirror/httponconnection/singleconnhttp.go ================================================ package httponconnection import ( "bufio" "net" "net/http" "golang.org/x/net/http2" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type HttpRequestTransport interface { http.RoundTripper } func newHTTPRequestTransportH1(conn net.Conn) HttpRequestTransport { return &httpRequestTransportH1{ conn: conn, bufReader: bufio.NewReader(conn), } } type httpRequestTransportH1 struct { conn net.Conn bufReader *bufio.Reader } func (h *httpRequestTransportH1) RoundTrip(req *http.Request) (*http.Response, error) { req.Proto = "HTTP/1.1" req.ProtoMajor = 1 req.ProtoMinor = 1 err := req.Write(h.conn) if err != nil { return nil, err } return http.ReadResponse(h.bufReader, req) } func newHTTPRequestTransportH2(conn net.Conn) HttpRequestTransport { transport := &http2.Transport{} clientConn, err := transport.NewClientConn(conn) if err != nil { return nil } return &httpRequestTransportH2{ transport: transport, clientConnection: clientConn, } } type httpRequestTransportH2 struct { transport *http2.Transport clientConnection *http2.ClientConn } func (h *httpRequestTransportH2) RoundTrip(request *http.Request) (*http.Response, error) { request.ProtoMajor = 2 request.ProtoMinor = 0 response, err := h.clientConnection.RoundTrip(request) if err != nil { return nil, err } return response, nil } func newSingleConnectionHTTPTransport(conn net.Conn, alpn string) (HttpRequestTransport, error) { switch alpn { case "h2": return newHTTPRequestTransportH2(conn), nil case "http/1.1", "": return newHTTPRequestTransportH1(conn), nil default: return nil, newError("unknown alpn: " + alpn).AtWarning() } } func NewSingleConnectionHTTPTransport(conn net.Conn, alpn string) (HttpRequestTransport, error) { return newSingleConnectionHTTPTransport(conn, alpn) } ================================================ FILE: transport/internet/tlsmirror/interface.go ================================================ package tlsmirror import ( "context" "github.com/v2fly/v2ray-core/v5/common" ) type TLSRecord struct { RecordType byte LegacyProtocolVersion [2]byte RecordLength uint16 Fragment []byte // Annotations are used to store additional information about the record. Never sent over the wire. InsertedMessage bool } type RecordReader interface { ReadNextRecord(rejectProfile PartialTLSRecordRejectProfile) (*TLSRecord, error) } type RecordWriter interface { WriteRecord(record *TLSRecord) error } type Peeker interface { Peek(n int) ([]byte, error) } type PartialTLSRecordRejectProfile interface { TestIfReject(record *TLSRecord, readyFields int) error } type MessageHook func(message *TLSRecord) (drop bool, ok error) type ExplicitNonceDetection func(cipherSuite uint16) bool type InsertableTLSConn interface { common.Closable GetHandshakeRandom() ([]byte, []byte, error) InsertC2SMessage(message *TLSRecord) error InsertS2CMessage(message *TLSRecord) error GetApplicationDataExplicitNonceReservedOverheadHeaderLength() (int, error) } const TrafficGeneratorManagedConnectionContextKey = "TrafficGeneratorManagedConnection-ku63HMMD-kduCPhr8-DN4y6WEa" type TrafficGeneratorManagedConnection interface { RecallTrafficGenerator() error WaitConnectionReady() context.Context IsConnectionInvalidated() bool } type ConnectionEnrollmentConfirmation interface { VerifyConnectionEnrollment(req *EnrollmentConfirmationReq) (*EnrollmentConfirmationResp, error) } const EnrollmentVerificationControlConnectionPostfix = ".tlsmirror-controlconnection.v2fly.arpa" type InsertableTLSConnForEnrollment interface { InsertableTLSConnEnrollmentEventReceiver } type InsertableTLSConnEnrollmentEventReceiver interface { ConnectionEnrollmentConfirmation } type RemoveConnectionFunc func() error type ConnectionEnrollmentConfirmationProcessor interface { ConnectionEnrollmentConfirmation AddConnection(ctx context.Context, clientRandom, ServerRandom []byte, conn InsertableTLSConnForEnrollment) (RemoveConnectionFunc, error) } type ConnectionEnrollmentConfirmationClientInstanceConfig struct { DefaultOutboundTag string } type ConnectionEnrollmentConfirmationClientInstanceConfigReceiver interface { OnConnectionEnrollmentConfirmationClientInstanceConfigReady(ConnectionEnrollmentConfirmationClientInstanceConfig) } type ConnectionEnrollmentConfirmationServerInstanceConfig struct { EnrollmentProcessor ConnectionEnrollmentConfirmationProcessor } type ConnectionEnrollmentConfirmationServerInstanceConfigReceiver interface { OnConnectionEnrollmentConfirmationServerInstanceConfigReady(ConnectionEnrollmentConfirmationServerInstanceConfig) } type ConnectionLoopbackPrevention struct { Key string } ================================================ FILE: transport/internet/tlsmirror/mirrorbase/base.go ================================================ package mirrorbase //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/tlsmirror/mirrorbase/conn.go ================================================ package mirrorbase import ( "bufio" "bytes" "context" "io" "net" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/crypto" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcommon" ) // NewMirroredTLSConn creates a new mirrored TLS connection. // No stable interface func NewMirroredTLSConn(ctx context.Context, clientConn net.Conn, serverConn net.Conn, onC2SMessage, onS2CMessage tlsmirror.MessageHook, closable common.Closable, explicitNonceDetection tlsmirror.ExplicitNonceDetection, onC2SMessageTx, onS2CMessageTx tlsmirror.MessageHook, ) tlsmirror.InsertableTLSConn { explicitNonceDetectionReady, explicitNonceDetectionOver := context.WithCancel(ctx) c := &conn{ ctx: ctx, clientConn: clientConn, serverConn: serverConn, c2sInsert: make(chan *tlsmirror.TLSRecord, 100), s2cInsert: make(chan *tlsmirror.TLSRecord, 100), OnC2SMessage: onC2SMessage, OnS2CMessage: onS2CMessage, explicitNonceDetection: explicitNonceDetection, explicitNonceDetectionReady: explicitNonceDetectionReady, explicitNonceDetectionOver: explicitNonceDetectionOver, OnC2SMessageTx: onC2SMessageTx, OnS2CMessageTx: onS2CMessageTx, } c.ctx, c.done = context.WithCancel(ctx) go c.c2sWorker() go c.s2cWorker() go func() { <-c.ctx.Done() if closable != nil { closable.Close() } c.clientConn.Close() c.serverConn.Close() }() return c } type conn struct { ctx context.Context done context.CancelFunc clientConn net.Conn serverConn net.Conn OnC2SMessage tlsmirror.MessageHook OnS2CMessage tlsmirror.MessageHook explicitNonceDetection tlsmirror.ExplicitNonceDetection OnC2SMessageTx tlsmirror.MessageHook OnS2CMessageTx tlsmirror.MessageHook c2sInsert chan *tlsmirror.TLSRecord s2cInsert chan *tlsmirror.TLSRecord isClientRandomReady bool ClientRandom [32]byte isServerRandomReady bool ServerRandom [32]byte tls12ExplicitNonce *bool explicitNonceDetectionReady context.Context explicitNonceDetectionOver context.CancelFunc c2sExplicitNonceCounterGenerator crypto.BytesGenerator s2cExplicitNonceCounterGenerator crypto.BytesGenerator } func (c *conn) GetHandshakeRandom() ([]byte, []byte, error) { // TODO: the value of c.isClientRandomReady, c.isServerRandomReady, c.ClientRandom, c.ServerRandom has incorrect memory consistency assumptions. if !c.isClientRandomReady || !c.isServerRandomReady { return nil, nil, newError("client random or server random not ready") } return c.ClientRandom[:], c.ServerRandom[:], nil } func (c *conn) Close() error { c.done() return nil } func (c *conn) InsertC2SMessage(message *tlsmirror.TLSRecord) error { duplicatedRecord := mirrorcommon.DuplicateRecord(*message) c.c2sInsert <- &duplicatedRecord return nil } func (c *conn) InsertS2CMessage(message *tlsmirror.TLSRecord) error { duplicatedRecord := mirrorcommon.DuplicateRecord(*message) c.s2cInsert <- &duplicatedRecord return nil } type bufPeeker struct { buffer []byte } func (b *bufPeeker) Peek(n int) ([]byte, error) { if len(b.buffer) < n { return nil, newError("not enough data") } return b.buffer[:n], nil } type readerWithInitialData struct { initialData []byte innerReader io.Reader } func (r *readerWithInitialData) initialDataDrained() bool { return len(r.initialData) == 0 } func (r *readerWithInitialData) Read(p []byte) (n int, err error) { if len(r.initialData) > 0 { n = copy(p, r.initialData) r.initialData = r.initialData[n:] return n, nil } return r.innerReader.Read(p) } func (c *conn) c2sWorker() { c2sHandshake, handshakeReminder, c2sReminderData, err := c.captureHandshake(c.clientConn, c.serverConn) if err != nil { c.done() return } _ = c2sHandshake _ = handshakeReminder _ = c2sReminderData serverConnectionWriter := bufio.NewWriter(c.serverConn) _, err = io.Copy(serverConnectionWriter, bytes.NewReader(handshakeReminder)) if err != nil { c.done() newError("failed to copy handshake reminder").Base(err).AtWarning().WriteToLog() return } clientHello, err := mirrorcommon.UnpackTLSClientHello(c2sHandshake.Fragment) if err != nil { c.done() newError("failed to unpack client hello").Base(err).AtWarning().WriteToLog() return } c.ClientRandom = clientHello.ClientRandom c.isClientRandomReady = true clientSocketReader := &readerWithInitialData{initialData: c2sReminderData, innerReader: c.clientConn} clientSocket := bufio.NewReaderSize(clientSocketReader, 65536) recordReader := mirrorcommon.NewTLSRecordStreamReader(clientSocket) recordWriter := mirrorcommon.NewTLSRecordStreamWriter(serverConnectionWriter) if len(c2sReminderData) == 0 { err := serverConnectionWriter.Flush() if err != nil { c.done() newError("failed to flush server connection writer").Base(err).AtWarning().WriteToLog() return } } go func() { for c.ctx.Err() == nil { var record *tlsmirror.TLSRecord select { case <-c.ctx.Done(): return case record = <-c.c2sInsert: // implicit memory consistency synchronization capture read for c.tls12ExplicitNonce } // memory consistency synchronization for value c.tls12ExplicitNonce is required!!! if *c.tls12ExplicitNonce { if record.RecordType == mirrorcommon.TLSRecord_RecordType_application_data || record.RecordType == mirrorcommon.TLSRecord_RecordType_alert { if len(record.Fragment) >= 8 { nonce := c.c2sExplicitNonceCounterGenerator() copy(record.Fragment, nonce) } } } if c.OnC2SMessageTx != nil { drop, err := c.OnC2SMessageTx(record) if err != nil { c.done() newError("failed to process C2S message").Base(err).AtWarning().WriteToLog() return } if drop { continue } } err := recordWriter.WriteRecord(record, false) if err != nil { c.done() newError("failed to write C2S message").Base(err).AtWarning().WriteToLog() return } if record.RecordType == mirrorcommon.TLSRecord_RecordType_alert { c.done() newError("alert sent, ending copy").AtWarning().WriteToLog() return } } }() explicitNonceSessionAndChangeCipherSpecWasLastMessage := false for c.ctx.Err() == nil { record, err := recordReader.ReadNextRecord() if err != nil { c.done() drainCopy(c.clientConn, nil, c.serverConn) newError("failed to read TLS record").Base(err).AtWarning().WriteToLog() return } if record.RecordType == mirrorcommon.TLSRecord_RecordType_change_cipher_spec { // implicit memory consistency synchronization capture read for c.tls12ExplicitNonce if c.explicitNonceDetectionReady.Err() == nil { c.done() drainCopy(c.clientConn, nil, c.serverConn) newError("received client to server change cipher spec before server hello").Base(err).AtWarning().WriteToLog() return } // memory consistency synchronization for value c.tls12ExplicitNonce is required!!! if *c.tls12ExplicitNonce { explicitNonceSessionAndChangeCipherSpecWasLastMessage = true } } if record.RecordType == mirrorcommon.TLSRecord_RecordType_handshake && explicitNonceSessionAndChangeCipherSpecWasLastMessage { // verify if the first 8 bytes are 0x00 if len(record.Fragment) < 8 || !bytes.Equal(record.Fragment[:8], []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) { newError("unexpected explicit nonce header at tls 12 finish").AtWarning().WriteToLog() c.done() drainCopy(c.clientConn, nil, c.serverConn) return } c.c2sExplicitNonceCounterGenerator = reverseBytesGeneratorByteOrder(crypto.GenerateIncreasingNonce([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) } if c.OnC2SMessage != nil { drop, err := c.OnC2SMessage(record) if err != nil { c.done() newError("failed to process C2S message").Base(err).AtWarning().WriteToLog() drainCopy(c.clientConn, nil, c.serverConn) return } if drop { continue } } duplicatedRecord := mirrorcommon.DuplicateRecord(*record) c.c2sInsert <- &duplicatedRecord } drainCopy(c.serverConn, nil, c.clientConn) } func (c *conn) s2cWorker() { // TODO: stick packets together, if they arrived so s2cHandshake, handshakeReminder, s2cReminderData, err := c.captureHandshake(c.serverConn, c.clientConn) if err != nil { c.done() return } _ = s2cHandshake _ = handshakeReminder _ = s2cReminderData clientConnectionWriter := bufio.NewWriter(c.clientConn) _, err = io.Copy(clientConnectionWriter, bytes.NewReader(handshakeReminder)) if err != nil { c.done() newError("failed to copy handshake reminder").Base(err).AtWarning().WriteToLog() return } serverHello, err := mirrorcommon.UnpackTLSServerHello(s2cHandshake.Fragment) if err != nil { c.done() newError("failed to unpack server hello").Base(err).AtWarning().WriteToLog() drainCopy(c.clientConn, nil, c.serverConn) return } c.ServerRandom = serverHello.ServerRandom c.isServerRandomReady = true isTLS12ExplicitNonce := c.explicitNonceDetection(serverHello.CipherSuite) c.tls12ExplicitNonce = &isTLS12ExplicitNonce // implicit memory consistency synchronization release write for c.tls12ExplicitNonce c.explicitNonceDetectionOver() _, err = c.OnS2CMessage(&s2cHandshake) if err != nil { newError("failed to process S2C server hello message").Base(err).AtWarning().WriteToLog() drainCopy(c.clientConn, nil, c.serverConn) c.done() return } serverSocketReader := &readerWithInitialData{initialData: s2cReminderData, innerReader: c.serverConn} serverSocket := bufio.NewReaderSize(serverSocketReader, 65536) recordReader := mirrorcommon.NewTLSRecordStreamReader(serverSocket) recordWriter := mirrorcommon.NewTLSRecordStreamWriter(clientConnectionWriter) if len(s2cReminderData) == 0 { err := clientConnectionWriter.Flush() if err != nil { newError("failed to flush client connection writer").Base(err).AtWarning().WriteToLog() drainCopy(c.clientConn, nil, c.serverConn) c.done() return } } go func() { for c.ctx.Err() == nil { var record *tlsmirror.TLSRecord select { case <-c.ctx.Done(): return case record = <-c.s2cInsert: // implicit memory consistency synchronization capture read for c.tls12ExplicitNonce } // memory consistency synchronization for value c.tls12ExplicitNonce is required!!! if *c.tls12ExplicitNonce { if record.RecordType == mirrorcommon.TLSRecord_RecordType_application_data || record.RecordType == mirrorcommon.TLSRecord_RecordType_alert { if len(record.Fragment) >= 8 { nonce := c.s2cExplicitNonceCounterGenerator() copy(record.Fragment, nonce) } } } if c.OnS2CMessageTx != nil { drop, err := c.OnS2CMessageTx(record) if err != nil { c.done() newError("failed to process S2C message").Base(err).AtWarning().WriteToLog() return } if drop { continue } } err := recordWriter.WriteRecord(record, false) if err != nil { c.done() newError("failed to write S2C message").Base(err).AtWarning().WriteToLog() return } if record.RecordType == mirrorcommon.TLSRecord_RecordType_alert { c.done() newError("alert sent, ending copy").AtWarning().WriteToLog() return } } }() explicitNonceSessionAndChangeCipherSpecWasLastMessage := false for c.ctx.Err() == nil { record, err := recordReader.ReadNextRecord() if err != nil { newError("failed to read TLS record").Base(err).AtWarning().WriteToLog() c.done() drainCopy(c.clientConn, nil, c.serverConn) return } if record.RecordType == mirrorcommon.TLSRecord_RecordType_change_cipher_spec { if *c.tls12ExplicitNonce { explicitNonceSessionAndChangeCipherSpecWasLastMessage = true } } if record.RecordType == mirrorcommon.TLSRecord_RecordType_handshake && explicitNonceSessionAndChangeCipherSpecWasLastMessage { // verify if the first 8 bytes are 0x00 if len(record.Fragment) < 8 || !bytes.Equal(record.Fragment[:8], []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) { newError("unexpected explicit nonce header at tls 12 finish").AtWarning().WriteToLog() c.done() drainCopy(c.clientConn, nil, c.serverConn) return } c.s2cExplicitNonceCounterGenerator = reverseBytesGeneratorByteOrder(crypto.GenerateIncreasingNonce([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) } if c.OnS2CMessage != nil { drop, err := c.OnS2CMessage(record) if err != nil { c.done() newError("failed to process S2C message").Base(err).AtWarning().WriteToLog() drainCopy(c.clientConn, nil, c.serverConn) return } if drop { continue } } duplicatedRecord := mirrorcommon.DuplicateRecord(*record) c.s2cInsert <- &duplicatedRecord } drainCopy(c.clientConn, nil, c.serverConn) } func drainCopy(dst io.Writer, initData []byte, src io.Reader) { if initData != nil { _, err := io.Copy(dst, bytes.NewReader(initData)) if err != nil { newError("failed to drain copy").Base(err).AtWarning().WriteToLog() } } _, err := io.Copy(dst, src) if err != nil { newError("failed to drain copy").Base(err).AtWarning().WriteToLog() } } type rejectionDecisionMaker struct{} func (r *rejectionDecisionMaker) TestIfReject(record *tlsmirror.TLSRecord, readyFields int) error { if readyFields >= 1 { if record.RecordType != mirrorcommon.TLSRecord_RecordType_handshake { return newError("unexpected record type").AtWarning() } } if readyFields >= 2 { switch record.LegacyProtocolVersion[0] { case 0x01: case 0x02: case 0x03: if record.LegacyProtocolVersion[1] > 0x03 { return newError("unexpected minor protocol version").AtWarning() } default: return newError("unexpected major protocol version").AtWarning() } } return nil } func (c *conn) captureHandshake(sourceConn, mirrorConn net.Conn) (handshake tlsmirror.TLSRecord, handshakeReminder, rest []byte, reterr error) { var readBuffer [65536]byte var nextRead int for c.ctx.Err() == nil { n, err := sourceConn.Read(readBuffer[nextRead:]) if err != nil { c.done() reterr = newError("failed to read from source connection").Base(err).AtWarning() return handshake, nil, nil, reterr } handshakeRejectionDecisionMaker := &rejectionDecisionMaker{} result, tryAgainLen, processed, err := mirrorcommon.PeekTLSRecord(&bufPeeker{buffer: readBuffer[:nextRead+n]}, handshakeRejectionDecisionMaker) if processed == 0 { if tryAgainLen == 0 { // TODO: directly copy drainCopy(mirrorConn, readBuffer[:nextRead+n], sourceConn) c.done() reterr = newError("failed to peek tls record").Base(err).AtWarning() return handshake, nil, nil, reterr } _, err = io.Copy(mirrorConn, bytes.NewReader(readBuffer[nextRead:nextRead+n])) if err != nil { c.done() newError("failed to copy to server connection").Base(err).AtWarning().WriteToLog() return handshake, nil, nil, reterr } nextRead += n } else { // Parse the client hello if result.RecordType != mirrorcommon.TLSRecord_RecordType_handshake { c.done() reterr = newError("unexpected record type").AtWarning() return handshake, nil, nil, reterr } handshake = result handshakeReminder = readBuffer[nextRead:processed] rest = readBuffer[processed : nextRead+n] return handshake, handshakeReminder, rest, nil } } reterr = newError("context is done") return handshake, nil, nil, reterr } func (c *conn) GetApplicationDataExplicitNonceReservedOverheadHeaderLength() (int, error) { if c.tls12ExplicitNonce == nil { return 0, newError("explicit nonce info is not ready") } if *c.tls12ExplicitNonce { return 8, nil } return 0, nil } ================================================ FILE: transport/internet/tlsmirror/mirrorbase/crypto.go ================================================ package mirrorbase import ( "github.com/v2fly/v2ray-core/v5/common/crypto" ) func reverseBytesGeneratorByteOrder(generator crypto.BytesGenerator) crypto.BytesGenerator { var reverseResult [8]byte return func() []byte { result := generator() if len(result) != 8 { panic("reverseBytesGeneratorByteOrder requires a generator that returns exactly 8 bytes") } for i := 0; i < 8; i++ { reverseResult[i] = result[7-i] } return reverseResult[:] } } ================================================ FILE: transport/internet/tlsmirror/mirrorbase/crypto_test.go ================================================ package mirrorbase import ( "testing" "github.com/google/go-cmp/cmp" "github.com/v2fly/v2ray-core/v5/common/crypto" ) func TestTLS12ExplicitNonceGeneration(t *testing.T) { generator := reverseBytesGeneratorByteOrder(crypto.GenerateIncreasingNonce([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) firstValue := generator() if diff := cmp.Diff(firstValue, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}); diff != "" { t.Errorf("Unexpected first value: %s", diff) } secondValue := generator() if diff := cmp.Diff(secondValue, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}); diff != "" { t.Errorf("Unexpected second value: %s", diff) } thirdValue := generator() if diff := cmp.Diff(thirdValue, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}); diff != "" { t.Errorf("Unexpected third value: %s", diff) } } ================================================ FILE: transport/internet/tlsmirror/mirrorbase/errors.generated.go ================================================ package mirrorbase import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tlsmirror/mirrorcommon/handshake.go ================================================ package mirrorcommon import ( "bytes" "github.com/v2fly/struc" ) type TLSClientHello struct { HandshakeType uint8 Length [3]byte Version uint16 ClientRandom [32]byte // There are other entries, however we do not need them yet } func UnpackTLSClientHello(data []byte) (TLSClientHello, error) { var clientHello TLSClientHello err := struc.Unpack(bytes.NewReader(data), &clientHello) return clientHello, err } type TLSServerHello struct { HandshakeType uint8 Length [3]byte Version uint16 ServerRandom [32]byte SessionIDLength uint8 `struc:"sizeof=SessionID"` SessionID []byte CipherSuite uint16 // There are other entries, however we do not need them yet } func UnpackTLSServerHello(data []byte) (TLSServerHello, error) { var serverHello TLSServerHello err := struc.Unpack(bytes.NewReader(data), &serverHello) return serverHello, err } ================================================ FILE: transport/internet/tlsmirror/mirrorcommon/loopback_protect.go ================================================ package mirrorcommon import ( "context" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) func SetLoopbackProtectionFlagForContext(ctx context.Context, enrollmentID []byte) context.Context { loopbackProtectionKey := tlsmirror.ConnectionLoopbackPrevention{Key: string(enrollmentID)} return context.WithValue(ctx, loopbackProtectionKey, true) } func SetSecondaryLoopbackProtectionFlagForContext(ctx context.Context, enrollmentID []byte) context.Context { loopbackProtectionKey := tlsmirror.ConnectionLoopbackPrevention{Key: string(enrollmentID)} return context.WithValue(ctx, loopbackProtectionKey, false) } func IsLoopbackProtectionEnabled(ctx context.Context, enrollmentID []byte) bool { loopbackProtectionKey := tlsmirror.ConnectionLoopbackPrevention{Key: string(enrollmentID)} val := ctx.Value(loopbackProtectionKey) enabled, ok := val.(bool) return ok && enabled } func IsSecondaryLoopbackProtectionEnabled(ctx context.Context, enrollmentID []byte) bool { loopbackProtectionKey := tlsmirror.ConnectionLoopbackPrevention{Key: string(enrollmentID)} val := ctx.Value(loopbackProtectionKey) enabled, ok := val.(bool) return ok && !enabled } ================================================ FILE: transport/internet/tlsmirror/mirrorcommon/record.go ================================================ package mirrorcommon import ( "fmt" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) // PeekTLSRecord reads a TLS record from peeker. // It returns the record, the number of bytes read from peeker, and any error encountered. // It does not consume content from peeker, and the content peeked is borrowed from peeker. func PeekTLSRecord(peeker tlsmirror.Peeker, rejectionProfile tlsmirror.PartialTLSRecordRejectProfile) (result tlsmirror.TLSRecord, tryAgainLength, processed int, err error) { var record tlsmirror.TLSRecord header, err := peeker.Peek(5) if err != nil { return record, 0, 0, err } if len(header) < 5 { return record, 5, 0, fmt.Errorf("tls: record header too short") } record.RecordType = header[0] record.LegacyProtocolVersion[0] = header[1] record.LegacyProtocolVersion[1] = header[2] record.RecordLength = uint16(header[3])<<8 | uint16(header[4]) if record.RecordLength > 16384 { return record, 0, 0, fmt.Errorf("tls: record length %d is too large", record.RecordLength) } if rejectionProfile != nil { err = rejectionProfile.TestIfReject(&record, 2) if err != nil { return record, 0, 0, err } } fragment, err := peeker.Peek(int(5 + record.RecordLength)) if err != nil { return record, int(5 + record.RecordLength), 0, err } if len(fragment) < 5+int(record.RecordLength) { return record, int(5 + record.RecordLength), 0, fmt.Errorf("tls: record fragment too short") } record.Fragment = fragment[5:] return record, 0, int(5 + record.RecordLength), nil } func PackTLSRecord(record tlsmirror.TLSRecord) []byte { buf := make([]byte, 5+len(record.Fragment)) buf[0] = record.RecordType buf[1] = record.LegacyProtocolVersion[0] buf[2] = record.LegacyProtocolVersion[1] buf[3] = byte(record.RecordLength >> 8) buf[4] = byte(record.RecordLength) copy(buf[5:], record.Fragment) return buf } func DuplicateRecord(record tlsmirror.TLSRecord) tlsmirror.TLSRecord { newRecord := record newRecord.Fragment = make([]byte, len(record.Fragment)) copy(newRecord.Fragment, record.Fragment) return newRecord } ================================================ FILE: transport/internet/tlsmirror/mirrorcommon/record_consts.go ================================================ package mirrorcommon const ( TLSRecord_RecordType_application_data = 23 TLSRecord_RecordType_handshake = 22 TLSRecord_RecordType_change_cipher_spec = 20 TLSRecord_RecordType_alert = 21 ) ================================================ FILE: transport/internet/tlsmirror/mirrorcommon/recordstream.go ================================================ package mirrorcommon import ( "bufio" "errors" "io" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) func NewTLSRecordStreamReader(reader *bufio.Reader) *TLSRecordStreamReader { return &TLSRecordStreamReader{bufferedReader: reader} } type TLSRecordStreamReader struct { bufferedReader *bufio.Reader consumedSize int64 } func (t *TLSRecordStreamReader) ReadNextRecord() (*tlsmirror.TLSRecord, error) { record, tryAgainLength, processedLength, err := PeekTLSRecord(t.bufferedReader, nil) if err == nil { _, err := t.bufferedReader.Discard(processedLength) t.consumedSize += int64(processedLength) if err != nil { return nil, err } return &record, nil } if errors.Is(err, io.EOF) { return nil, err } else { // nolint: gocritic if tryAgainLength == 0 { return nil, err } } if tryAgainLength > 0 { _, err := t.bufferedReader.Read(make([]byte, tryAgainLength)) if err != nil { return nil, err } err = t.bufferedReader.UnreadByte() if err != nil { return nil, err } } return t.ReadNextRecord() } func (t *TLSRecordStreamReader) GetConsumedSize() int64 { return t.consumedSize } func NewTLSRecordStreamWriter(writer *bufio.Writer) *TLSRecordStreamWriter { return &TLSRecordStreamWriter{bufferedWriter: writer} } type TLSRecordStreamWriter struct { bufferedWriter *bufio.Writer } func (t *TLSRecordStreamWriter) WriteRecord(record *tlsmirror.TLSRecord, holdFlush bool) error { _, err := t.bufferedWriter.Write(PackTLSRecord(*record)) if err != nil { return err } if holdFlush { return nil } return t.bufferedWriter.Flush() } ================================================ FILE: transport/internet/tlsmirror/mirrorcrypto/decryptor.go ================================================ package mirrorcrypto import ( "crypto/cipher" "github.com/v2fly/v2ray-core/v5/common/crypto" ) type Decryptor struct { nonceGenerator crypto.BytesGenerator aead cipher.AEAD nextNonce []byte } func NewDecryptor(encryptionKey []byte, nonceMask []byte) *Decryptor { wrappedAead := aeadAESGCMTLS13(encryptionKey, nonceMask) return &Decryptor{ nonceGenerator: generateInitialAEADNonce(), aead: wrappedAead, } } func (d *Decryptor) Open(dst, src []byte) ([]byte, error) { if d.nextNonce == nil { d.nextNonce = d.nonceGenerator() } dst, err := d.aead.Open(dst[:0], d.nextNonce, src, nil) if err != nil { return nil, err } d.nextNonce = nil return dst, nil } func (d *Decryptor) NonceSize() int { return d.aead.NonceSize() } ================================================ FILE: transport/internet/tlsmirror/mirrorcrypto/derive_key.go ================================================ package mirrorcrypto import ( "crypto/hkdf" "crypto/sha256" ) func DeriveEncryptionKey(primaryKey, clientRandom, serverRandom []byte, tag string) ([]byte, []byte, error) { if len(primaryKey) != 32 { return nil, nil, newError("invalid primary key size: ", len(primaryKey)) } if len(clientRandom) != 32 { return nil, nil, newError("invalid client random size: ", len(clientRandom)) } if len(serverRandom) != 32 { return nil, nil, newError("invalid server random size: ", len(serverRandom)) } // Concatenate the primary key, client random, and server random combined := append(primaryKey, clientRandom...) // nolint: gocritic combined = append(combined, serverRandom...) encryptionKey, err := hkdf.Expand(sha256.New, combined, "v2ray-sp76YMKM-EkGrFUNL-rTJRJMkU:tlsmirror-encryption"+tag, 16) if err != nil { return nil, nil, newError("unable to derive encryption key").Base(err) } nonceMask, err := hkdf.Expand(sha256.New, combined, "v2ray-sp76YMKM-EkGrFUNL-rTJRJMkU:tlsmirror-noncemask"+tag, 12) if err != nil { return nil, nil, newError("unable to derive nonce mask").Base(err) } return encryptionKey, nonceMask, nil } func DeriveSecondaryKey(primaryKey []byte, tag string) ([]byte, error) { if len(primaryKey) != 32 { return nil, newError("invalid primary key size: ", len(primaryKey)) } // Use HKDF to derive a secondary key secondaryKey, err := hkdf.Expand(sha256.New, primaryKey, "v2ray-sv77RCEY-e8AhYsbD-BmFC7XRK:tlsmirror-secondary"+tag, 16) if err != nil { return nil, newError("unable to derive secondary key").Base(err) } return secondaryKey, nil } func DeriveSequenceWatermarkingKey(primaryKey, clientRandom, serverRandom []byte, tag string) ([]byte, []byte, error) { if len(primaryKey) != 32 { return nil, nil, newError("invalid primary key size: ", len(primaryKey)) } if len(clientRandom) != 32 { return nil, nil, newError("invalid client random size: ", len(clientRandom)) } if len(serverRandom) != 32 { return nil, nil, newError("invalid server random size: ", len(serverRandom)) } // Concatenate the primary key, client random, and server random combined := append(primaryKey, clientRandom...) // nolint: gocritic combined = append(combined, serverRandom...) encryptionKey, err := hkdf.Expand(sha256.New, combined, "v2ray-xv64FXUU-GxMn8UYz-bTy6UDeE:tlsmirror-sequence-watermark-encryption"+tag, 32) if err != nil { return nil, nil, newError("unable to derive encryption key").Base(err) } nonceMask, err := hkdf.Expand(sha256.New, combined, "v2ray-xv64FXUU-GxMn8UYz-bTy6UDeE:tlsmirror-sequence-watermark-noncemask"+tag, 24) if err != nil { return nil, nil, newError("unable to derive nonce mask").Base(err) } return encryptionKey, nonceMask, nil } ================================================ FILE: transport/internet/tlsmirror/mirrorcrypto/encrypter.go ================================================ package mirrorcrypto import ( "crypto/cipher" "github.com/v2fly/v2ray-core/v5/common/crypto" ) type Encryptor struct { nonceGenerator crypto.BytesGenerator aead cipher.AEAD } func NewEncryptor(encryptionKey []byte, nonceMask []byte) *Encryptor { wrappedAead := aeadAESGCMTLS13(encryptionKey, nonceMask) return &Encryptor{ nonceGenerator: generateInitialAEADNonce(), aead: wrappedAead, } } func (e *Encryptor) Seal(dst, src []byte) ([]byte, error) { nonce := e.nonceGenerator() dst = e.aead.Seal(dst, nonce, src, nil) return dst, nil } func (e *Encryptor) NonceSize() int { return e.aead.NonceSize() } ================================================ FILE: transport/internet/tlsmirror/mirrorcrypto/errors.generated.go ================================================ package mirrorcrypto import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tlsmirror/mirrorcrypto/mirrorcrypto.go ================================================ package mirrorcrypto import "github.com/v2fly/v2ray-core/v5/common/crypto" //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen func generateInitialAEADNonce() crypto.BytesGenerator { return crypto.GenerateIncreasingNonce([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) } ================================================ FILE: transport/internet/tlsmirror/mirrorcrypto/tls_cipher_suites.go ================================================ package mirrorcrypto import ( "crypto/cipher" "golang.org/x/crypto/chacha20poly1305" ) const ( aeadNonceLength = 12 ) type aead interface { cipher.AEAD // explicitNonceLen returns the number of bytes of explicit nonce // included in each record. This is eight for older AEADs and // zero for modern ones. explicitNonceLen() int } // xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce // before each call. type xorNonceAEAD struct { nonceMask [aeadNonceLength]byte aead cipher.AEAD } func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() } func (f *xorNonceAEAD) explicitNonceLen() int { return 0 } func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { for i, b := range nonce { f.nonceMask[4+i] ^= b } result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData) for i, b := range nonce { f.nonceMask[4+i] ^= b } return result } func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { for i, b := range nonce { f.nonceMask[4+i] ^= b } result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData) for i, b := range nonce { f.nonceMask[4+i] ^= b } return result, err } func aeadChaCha20Poly1305(key, nonceMask []byte) aead { if len(nonceMask) != aeadNonceLength { panic("tls: internal error: wrong nonce length") } aead, err := chacha20poly1305.New(key) if err != nil { panic(err) } ret := &xorNonceAEAD{aead: aead} copy(ret.nonceMask[:], nonceMask) return ret } ================================================ FILE: transport/internet/tlsmirror/mirrorcrypto/tls_cipher_suites_linkname.go ================================================ package mirrorcrypto import ( "crypto/cipher" _ "unsafe" ) // This linkname is necessary to avoid duplicating too many internal packages. //go:linkname aeadAESGCMTLS13 crypto/tls.aeadAESGCMTLS13 func aeadAESGCMTLS13(key, nonceMask []byte) cipher.AEAD ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/cancelContextOnCloseConn.go ================================================ package mirrorenrollment import ( "context" "net" ) func NewCancelContextOnCloseConn(conn net.Conn, done context.CancelFunc) net.Conn { return &cancelContextOnCloseConn{ Conn: conn, done: done, } } type cancelContextOnCloseConn struct { net.Conn done context.CancelFunc } func (c *cancelContextOnCloseConn) Close() error { c.done() return c.Conn.Close() } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/clicommand/enrollmentlink_cli.go ================================================ package clicommand import ( "flag" "io" "os" "google.golang.org/protobuf/encoding/protojson" anypb "google.golang.org/protobuf/types/known/anypb" "github.com/v2fly/v2ray-core/v5/main/commands/all/engineering" "github.com/v2fly/v2ray-core/v5/main/commands/base" mirrorenrollment "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment" ) var ( inputPath *string outputPath *string mode *string ) // machine generated var cmdEnrollmentLink = &base.Command{ UsageLine: "{{.Exec}} engineering tlsmirror-enrollment-link", Flag: func() flag.FlagSet { fs := flag.NewFlagSet("tlsmirror-enrollment-link", flag.ExitOnError) inputPath = fs.String("c", "", "input file path (optional, defaults to stdin)") outputPath = fs.String("o", "", "output file path (default stdout)") mode = fs.String("mode", "link", "conversion mode: 'link' to convert JSON -> link, 'json' to convert link -> JSON") return *fs }(), Run: func(cmd *base.Command, args []string) { if err := cmd.Flag.Parse(args); err != nil { base.Fatalf("failed to parse flags: %v", err) } var content []byte var err error if *inputPath == "" { // Read from stdin when -c is omitted. content, err = io.ReadAll(os.Stdin) if err != nil { base.Fatalf("failed to read from stdin: %v", err) } } else { fd, err := os.Open(*inputPath) if err != nil { base.Fatalf("failed to open input file %q: %v", *inputPath, err) } defer fd.Close() content, err = io.ReadAll(fd) if err != nil { base.Fatalf("failed to read input file %q: %v", *inputPath, err) } } var outBytes []byte switch *mode { case "link": // Expect protobuf JSON for google.protobuf.Any, convert to a data URL link. var any anypb.Any if err := protojson.Unmarshal(content, &any); err != nil { base.Fatalf("failed to unmarshal JSON into google.protobuf.Any: %v", err) } link, err := mirrorenrollment.LinkFromAny(&any) if err != nil { base.Fatalf("failed to create link from Any: %v", err) } outBytes = []byte(link) case "json": // Expect link (data URL or other supported forms), convert to protobuf JSON. link := string(content) any, err := mirrorenrollment.AnyFromLink(link) if err != nil { base.Fatalf("failed to parse link into Any: %v", err) } b, err := protojson.Marshal(any) if err != nil { base.Fatalf("failed to marshal Any to JSON: %v", err) } outBytes = b default: base.Fatalf("unknown mode: %s", *mode) } if *outputPath == "" { if _, err := os.Stdout.Write(outBytes); err != nil { base.Fatalf("failed to write output to stdout: %v", err) } return } if err := os.WriteFile(*outputPath, outBytes, 0o644); err != nil { base.Fatalf("failed to write output file %q: %v", *outputPath, err) } }, } func init() { engineering.AddCommand(cmdEnrollmentLink) } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/client.go ================================================ package mirrorenrollment import ( "context" "net" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" v2net "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcommon" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation" ) func NewEnrollmentConfirmationClient( ctx context.Context, config *Config, serverIdentity []byte, ) (*EnrollmentConfirmationClient, error) { if ctx == nil { return nil, newError("context cannot be nil") } if config == nil { return nil, newError("config cannot be nil") } for _, handler := range config.BootstrapEgressUrl { if handler == "" { return nil, newError("bootstrap ingress URL cannot be empty") } anyPb, err := AnyFromLink(handler) if err != nil { return nil, newError("invalid bootstrap ingress URL").Base(err).AtError() } if anyPb == nil { return nil, newError("bootstrap ingress URL did not produce valid Any") } config.BootstrapEgressConfig = append(config.BootstrapEgressConfig, anyPb) } ecc := &EnrollmentConfirmationClient{ ctx: ctx, config: config, serverIdentity: serverIdentity, } if err := ecc.init(); err != nil { return nil, newError("failed to initialize enrollment confirmation client").Base(err).AtError() } return ecc, nil } type EnrollmentConfirmationClient struct { ctx context.Context config *Config serverIdentity []byte primaryEnrollmentConfirmationClient tlsmirror.ConnectionEnrollmentConfirmation bootstrapEnrollmentConfirmationClients []tlsmirror.ConnectionEnrollmentConfirmation } func (c *EnrollmentConfirmationClient) VerifyConnectionEnrollment(req *tlsmirror.EnrollmentConfirmationReq) (*tlsmirror.EnrollmentConfirmationResp, error) { resp, err := c.primaryEnrollmentConfirmationClient.VerifyConnectionEnrollment(req) if err == nil { if resp.Enrolled { newError("enrollment confirmation verification with primary enrollment successful").Base(err).WriteToLog() } else { newError("enrollment confirmation verification with primary enrollment over, not enrolled").Base(err).WriteToLog() } return resp, nil } newError("enrollment confirmation verification with primary enrollment failed").Base(err).WriteToLog() for _, bootstrapClient := range c.bootstrapEnrollmentConfirmationClients { resp, err := bootstrapClient.VerifyConnectionEnrollment(req) if err == nil { if resp.Enrolled { newError("enrollment confirmation verification with bootstrap enrollment successful").Base(err).WriteToLog() } else { newError("enrollment confirmation verification with bootstrap enrollment over, not enrolled").Base(err).WriteToLog() } return resp, nil } newError("enrollment confirmation verification with bootstrap enrollment failed").Base(err).WriteToLog() } return nil, newError("all enrollment confirmation clients failed").Base(err).AtError() } func (c *EnrollmentConfirmationClient) init() error { rtt, _, err := httpenrollmentconfirmation.NewClientRoundTripperForEnrollmentConfirmation( func(network, addr string) (net.Conn, error) { transportEnvironment := envctx.EnvironmentFromContext(c.ctx).(environment.TransportEnvironment) dialer := transportEnvironment.OutboundDialer() if dialer == nil { return nil, newError("no outbound dialer available in transport environment") } dest, err := v2net.ParseDestination(addr) if err != nil { return nil, newError("failed to parse destination address").Base(err).AtError() } dest.Network = v2net.Network_TCP loopbackProtectedCtx := mirrorcommon.SetLoopbackProtectionFlagForContext(c.ctx, c.serverIdentity) primaryConfirmationOutbound := c.config.PrimaryEgressOutbound if primaryConfirmationOutbound == "" { primaryConfirmationOutbound = transportEnvironment.SelfProxyTag() loopbackProtectedCtx = mirrorcommon.SetSecondaryLoopbackProtectionFlagForContext(c.ctx, c.serverIdentity) } connContext, done := context.WithCancel(loopbackProtectedCtx) conn, err := dialer(connContext, dest, primaryConfirmationOutbound) if err != nil { done() return nil, newError("failed to dial to destination").Base(err).AtError() } contextLinkedConn := NewCancelContextOnCloseConn(conn, done) return contextLinkedConn, nil }, c.serverIdentity) if err != nil { return newError("failed to create HTTP round tripper for enrollment confirmation").Base(err).AtError() } c.primaryEnrollmentConfirmationClient, err = httpenrollmentconfirmation.NewHTTPEnrollmentConfirmationClientFromHTTPRoundTripper(rtt) if err != nil { return newError("failed to create HTTP enrollment confirmation client").Base(err).AtError() } for _, bootstrapEnrollmentConfirmationConfig := range c.config.BootstrapEgressConfig { enrollment, err := serial.GetInstanceOf(bootstrapEnrollmentConfirmationConfig) if err != nil { return newError("failed to get instance of bootstrap enrollment confirmation config").Base(err).AtError() } loopbackProtectedCtx := mirrorcommon.SetLoopbackProtectionFlagForContext(c.ctx, c.serverIdentity) enrollmentInst, err := common.CreateObject(loopbackProtectedCtx, enrollment) if err != nil { return newError("failed to create bootstrap enrollment confirmation config").Base(err).AtError() } enrollmentConfirmation, ok := enrollmentInst.(tlsmirror.ConnectionEnrollmentConfirmation) if !ok { return newError("bootstrap enrollment confirmation config is not a valid ConnectionEnrollmentConfirmation") } if configReceiver, ok := enrollmentConfirmation.(tlsmirror.ConnectionEnrollmentConfirmationClientInstanceConfigReceiver); ok { configReceiver.OnConnectionEnrollmentConfirmationClientInstanceConfigReady(tlsmirror.ConnectionEnrollmentConfirmationClientInstanceConfig{ DefaultOutboundTag: c.config.BootstrapEgressOutbound, }) } c.bootstrapEnrollmentConfirmationClients = append(c.bootstrapEnrollmentConfirmationClients, enrollmentConfirmation) } return nil } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/config.pb.go ================================================ package mirrorenrollment import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // This will be handled by the TLS Mirror server, the enrollment part only accepts existing connections. PrimaryIngressOutbound string `protobuf:"bytes,1,opt,name=primary_ingress_outbound,json=primaryIngressOutbound,proto3" json:"primary_ingress_outbound,omitempty"` PrimaryEgressOutbound string `protobuf:"bytes,2,opt,name=primary_egress_outbound,json=primaryEgressOutbound,proto3" json:"primary_egress_outbound,omitempty"` BootstrapIngressUrl []string `protobuf:"bytes,3,rep,name=bootstrap_ingress_url,json=bootstrapIngressUrl,proto3" json:"bootstrap_ingress_url,omitempty"` BootstrapEgressUrl []string `protobuf:"bytes,4,rep,name=bootstrap_egress_url,json=bootstrapEgressUrl,proto3" json:"bootstrap_egress_url,omitempty"` BootstrapIngressConfig []*anypb.Any `protobuf:"bytes,5,rep,name=bootstrap_ingress_config,json=bootstrapIngressConfig,proto3" json:"bootstrap_ingress_config,omitempty"` BootstrapEgressConfig []*anypb.Any `protobuf:"bytes,6,rep,name=bootstrap_egress_config,json=bootstrapEgressConfig,proto3" json:"bootstrap_egress_config,omitempty"` BootstrapEgressOutbound string `protobuf:"bytes,7,opt,name=bootstrap_egress_outbound,json=bootstrapEgressOutbound,proto3" json:"bootstrap_egress_outbound,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_tlsmirror_mirrorenrollment_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_mirrorenrollment_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_mirrorenrollment_config_proto_rawDescGZIP(), []int{0} } func (x *Config) GetPrimaryIngressOutbound() string { if x != nil { return x.PrimaryIngressOutbound } return "" } func (x *Config) GetPrimaryEgressOutbound() string { if x != nil { return x.PrimaryEgressOutbound } return "" } func (x *Config) GetBootstrapIngressUrl() []string { if x != nil { return x.BootstrapIngressUrl } return nil } func (x *Config) GetBootstrapEgressUrl() []string { if x != nil { return x.BootstrapEgressUrl } return nil } func (x *Config) GetBootstrapIngressConfig() []*anypb.Any { if x != nil { return x.BootstrapIngressConfig } return nil } func (x *Config) GetBootstrapEgressConfig() []*anypb.Any { if x != nil { return x.BootstrapEgressConfig } return nil } func (x *Config) GetBootstrapEgressOutbound() string { if x != nil { return x.BootstrapEgressOutbound } return "" } var File_transport_internet_tlsmirror_mirrorenrollment_config_proto protoreflect.FileDescriptor const file_transport_internet_tlsmirror_mirrorenrollment_config_proto_rawDesc = "" + "\n" + ":transport/internet/tlsmirror/mirrorenrollment/config.proto\x128v2ray.core.transport.internet.tlsmirror.mirrorenrollment\x1a common/protoext/extensions.proto\x1a\x19google/protobuf/any.proto\"\xba\x03\n" + "\x06Config\x128\n" + "\x18primary_ingress_outbound\x18\x01 \x01(\tR\x16primaryIngressOutbound\x126\n" + "\x17primary_egress_outbound\x18\x02 \x01(\tR\x15primaryEgressOutbound\x122\n" + "\x15bootstrap_ingress_url\x18\x03 \x03(\tR\x13bootstrapIngressUrl\x120\n" + "\x14bootstrap_egress_url\x18\x04 \x03(\tR\x12bootstrapEgressUrl\x12N\n" + "\x18bootstrap_ingress_config\x18\x05 \x03(\v2\x14.google.protobuf.AnyR\x16bootstrapIngressConfig\x12L\n" + "\x17bootstrap_egress_config\x18\x06 \x03(\v2\x14.google.protobuf.AnyR\x15bootstrapEgressConfig\x12:\n" + "\x19bootstrap_egress_outbound\x18\a \x01(\tR\x17bootstrapEgressOutboundB\xc9\x01\n" + " google.protobuf.Any 1, // 1: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.Config.bootstrap_egress_config:type_name -> google.protobuf.Any 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_transport_internet_tlsmirror_mirrorenrollment_config_proto_init() } func file_transport_internet_tlsmirror_mirrorenrollment_config_proto_init() { if File_transport_internet_tlsmirror_mirrorenrollment_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_mirrorenrollment_config_proto_rawDesc), len(file_transport_internet_tlsmirror_mirrorenrollment_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_tlsmirror_mirrorenrollment_config_proto_goTypes, DependencyIndexes: file_transport_internet_tlsmirror_mirrorenrollment_config_proto_depIdxs, MessageInfos: file_transport_internet_tlsmirror_mirrorenrollment_config_proto_msgTypes, }.Build() File_transport_internet_tlsmirror_mirrorenrollment_config_proto = out.File file_transport_internet_tlsmirror_mirrorenrollment_config_proto_goTypes = nil file_transport_internet_tlsmirror_mirrorenrollment_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.tlsmirror.mirrorenrollment; option csharp_namespace = "V2Ray.Core.Transport.Internet.Tlsmirror.MirrorEnrollment"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment"; option java_package = "com.v2ray.core.transport.internet.tlsmirror.mirrorenrollment"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "google/protobuf/any.proto"; message Config { // This will be handled by the TLS Mirror server, the enrollment part only accepts existing connections. string primary_ingress_outbound = 1; string primary_egress_outbound = 2; repeated string bootstrap_ingress_url = 3; repeated string bootstrap_egress_url = 4; repeated google.protobuf.Any bootstrap_ingress_config = 5; repeated google.protobuf.Any bootstrap_egress_config = 6; string bootstrap_egress_outbound = 7; } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/enrollment.go ================================================ package mirrorenrollment //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/enrollmentlink.go ================================================ package mirrorenrollment import ( "encoding/base64" "fmt" "strings" "google.golang.org/protobuf/proto" anypb "google.golang.org/protobuf/types/known/anypb" ) const ( // MIME type used for enrollment data URLs. enrollmentDataMIME = "application/vnd.v2ray.tlsmirror-enrollment" ) // LinkFromAny converts a protobuf Any into a data URL string. // The produced link has form: // data:application/vnd.v2ray.tlsmirror-enrollment;base64, // where payload is the marshaled Any message encoded with standard base64 (with padding). func LinkFromAny(a *anypb.Any) (string, error) { // Machine generated code if a == nil { return "", newError("nil Any") } b, err := proto.Marshal(a) if err != nil { return "", newError("failed to marshal Any").Base(err) } enc := base64.StdEncoding.EncodeToString(b) dataURL := "data:" + enrollmentDataMIME + ";base64," + enc return dataURL, nil } // AnyFromLink converts a string link (now primarily a data URL) back to *anypb.Any. // Accepted formats (strict): only data URLs matching the exact MIME type and base64 encoding. func AnyFromLink(link string) (*anypb.Any, error) { // Machine generated code if link == "" { return nil, newError("empty link") } // Must be a data URL. if !strings.HasPrefix(link, "data:") { return nil, newError("input must be a data URL") } // Parse and split header and payload at the first comma. comma := strings.Index(link, ",") if comma == -1 || comma+1 >= len(link) { return nil, newError("invalid data URL: missing payload") } meta := link[len("data:"):comma] payload := link[comma+1:] // Meta should be like ";base64" possibly with additional params. metaParts := strings.Split(meta, ";") if len(metaParts) < 2 { return nil, newError("invalid data URL metadata") } // First part must match our expected MIME. if metaParts[0] != enrollmentDataMIME { return nil, newError("unexpected MIME type in data URL: " + metaParts[0]) } // Ensure "base64" is present in parameters. isBase64 := false for _, p := range metaParts[1:] { if p == "base64" { isBase64 = true break } } if !isBase64 { return nil, newError("data URL must be base64 encoded") } // No URL parsing/decoding of payload: payload is raw base64. raw, err := base64.StdEncoding.DecodeString(payload) if err != nil { return nil, newError("failed to base64-decode data URL payload").Base(err) } var any anypb.Any if err := proto.Unmarshal(raw, &any); err != nil { return nil, newError("failed to unmarshal Any from decoded payload").Base(err) } return &any, nil } // helper to format an error when newError is not available at call site func fmtErr(format string, a ...interface{}) error { return fmt.Errorf(format, a...) } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/enrollmentlink_test.go ================================================ package mirrorenrollment import ( "crypto/rand" "encoding/base64" "fmt" "strings" "testing" "google.golang.org/protobuf/proto" anypb "google.golang.org/protobuf/types/known/anypb" ) func TestLinkRoundTrip(t *testing.T) { orig := &anypb.Any{ TypeUrl: "type.test/example", Value: []byte("sample-payload-bytes"), } link, err := LinkFromAny(orig) if err != nil { t.Fatalf("LinkFromAny failed: %v", err) } if !strings.HasPrefix(link, "data:") { t.Fatalf("expected data URL prefix, got: %s", link) } decoded, err := AnyFromLink(link) if err != nil { t.Fatalf("AnyFromLink failed: %v", err) } if !proto.Equal(decoded, orig) { t.Fatalf("decoded Any does not match original.\nOriginal: %#v\nDecoded: %#v", orig, decoded) } } func TestLinkFromAnyProducesValidDataURL(t *testing.T) { orig := &anypb.Any{TypeUrl: "type.test/inspect", Value: []byte("inspect-bytes")} link, err := LinkFromAny(orig) if err != nil { t.Fatalf("LinkFromAny failed: %v", err) } if !strings.HasPrefix(link, "data:") { t.Fatalf("expected data URL prefix, got: %s", link) } comma := strings.Index(link, ",") if comma == -1 { t.Fatalf("no comma in data URL: %s", link) } meta := link[len("data:"):comma] payload := link[comma+1:] if !strings.HasPrefix(meta, enrollmentDataMIME) { t.Fatalf("unexpected MIME in meta: %s", meta) } if !strings.Contains(meta, "base64") { t.Fatalf("expected base64 param in meta: %s", meta) } raw, err := base64.StdEncoding.DecodeString(payload) if err != nil { t.Fatalf("failed to decode payload with std encoding: %v", err) } var any anypb.Any if err := proto.Unmarshal(raw, &any); err != nil { t.Fatalf("unmarshal payload failed: %v", err) } if !proto.Equal(&any, orig) { t.Fatalf("unmarshaled Any does not equal original\nwant=%v\ngot=%v", orig, &any) } } func TestLegacyTLSMirrorScheme(t *testing.T) { orig := &anypb.Any{ TypeUrl: "type.test/legacy", Value: []byte("legacy-bytes"), } marshaled, err := proto.Marshal(orig) if err != nil { t.Fatalf("failed to marshal orig Any: %v", err) } enc := base64.RawURLEncoding.EncodeToString(marshaled) legacyLink := "tlsmirror-enrollment:" + enc if _, err := AnyFromLink(legacyLink); err == nil { t.Fatalf("expected AnyFromLink to reject legacy link format, but it succeeded") } } func TestPlainBase64Input(t *testing.T) { orig := &anypb.Any{ TypeUrl: "type.test/plain", Value: []byte("plain-bytes"), } marshaled, err := proto.Marshal(orig) if err != nil { t.Fatalf("failed to marshal orig Any: %v", err) } enc := base64.StdEncoding.EncodeToString(marshaled) // Pass plain base64 (no scheme) - should be rejected in strict mode if _, err := AnyFromLink(enc); err == nil { t.Fatalf("expected AnyFromLink to reject plain base64 input, but it succeeded") } } func TestInvalidInput(t *testing.T) { if _, err := AnyFromLink("not-a-valid-base64@@"); err == nil { t.Fatalf("expected error for invalid input, got nil") } if _, err := LinkFromAny(nil); err == nil { t.Fatalf("expected error for LinkFromAny(nil), got nil") } } func TestRejectWrongMIME(t *testing.T) { orig := &anypb.Any{TypeUrl: "type.test/wrongmime", Value: []byte("x")} b, _ := proto.Marshal(orig) enc := base64.StdEncoding.EncodeToString(b) link := fmt.Sprintf("data:application/octet-stream;base64,%s", enc) if _, err := AnyFromLink(link); err == nil { t.Fatalf("expected rejection for wrong MIME type, but got success") } } func TestRejectMissingBase64Param(t *testing.T) { orig := &anypb.Any{TypeUrl: "type.test/nobase64", Value: []byte("x")} b, _ := proto.Marshal(orig) enc := base64.StdEncoding.EncodeToString(b) link := fmt.Sprintf("data:%s,%s", enrollmentDataMIME, enc) // no ;base64 if _, err := AnyFromLink(link); err == nil { t.Fatalf("expected rejection for missing base64 param, but got success") } } func TestAcceptExtraParams(t *testing.T) { orig := &anypb.Any{TypeUrl: "type.test/params", Value: []byte("p")} b, _ := proto.Marshal(orig) enc := base64.StdEncoding.EncodeToString(b) link := fmt.Sprintf("data:%s;v=1;base64;foo=bar,%s", enrollmentDataMIME, enc) any, err := AnyFromLink(link) if err != nil { t.Fatalf("expected success for extra params, got error: %v", err) } if !proto.Equal(any, orig) { t.Fatalf("decoded Any mismatch; want %v got %v", orig, any) } } func TestRejectNonBase64Payload(t *testing.T) { link := "data:" + enrollmentDataMIME + ";base64,not-base64-@@@" if _, err := AnyFromLink(link); err == nil { t.Fatalf("expected error for non-base64 payload, got nil") } } func TestRejectURLEncodedPayload(t *testing.T) { // percent-encoded payload orig := &anypb.Any{TypeUrl: "type.test/urlenc", Value: []byte("url-bytes")} b, _ := proto.Marshal(orig) enc := base64.StdEncoding.EncodeToString(b) // percent-encode first few chars pct := "%" + enc[:2] + enc[2:] link := fmt.Sprintf("data:%s;base64,%s", enrollmentDataMIME, pct) if _, err := AnyFromLink(link); err == nil { t.Fatalf("expected rejection for percent-encoded payload, got success") } } func TestCaseSensitiveMIME(t *testing.T) { orig := &anypb.Any{TypeUrl: "type.test/case", Value: []byte("c")} b, _ := proto.Marshal(orig) enc := base64.StdEncoding.EncodeToString(b) link := fmt.Sprintf("data:%s;base64,%s", strings.ToUpper(enrollmentDataMIME), enc) if _, err := AnyFromLink(link); err == nil { t.Fatalf("expected rejection for case mismatch MIME, got success") } } func TestLargePayloadRoundTrip(t *testing.T) { // create a ~100KB payload size := 100 * 1024 p := make([]byte, size) if _, err := rand.Read(p); err != nil { t.Fatalf("failed to generate random payload: %v", err) } orig := &anypb.Any{TypeUrl: "type.test/large", Value: p} link, err := LinkFromAny(orig) if err != nil { t.Fatalf("LinkFromAny failed: %v", err) } any, err := AnyFromLink(link) if err != nil { t.Fatalf("AnyFromLink failed for large payload: %v", err) } if !proto.Equal(any, orig) { t.Fatalf("large payload mismatch after roundtrip") } } func TestRejectRawURLEncodingInDataURL(t *testing.T) { orig := &anypb.Any{TypeUrl: "type.test/rawurl", Value: []byte("raw")} b, _ := proto.Marshal(orig) enc := base64.RawURLEncoding.EncodeToString(b) link := fmt.Sprintf("data:%s;base64,%s", enrollmentDataMIME, enc) if _, err := AnyFromLink(link); err == nil { t.Fatalf("expected rejection for raw URL base64 payload, got success") } } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/errors.generated.go ================================================ package mirrorenrollment import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation/client.go ================================================ package httpenrollmentconfirmation import ( "bytes" "encoding/base32" "io" "net/http" protov2 "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) func NewHTTPEnrollmentConfirmationClientFromHTTPRoundTripper(tripper http.RoundTripper) (tlsmirror.ConnectionEnrollmentConfirmation, error) { if tripper == nil { return nil, newError("nil tripper") } return &client{ httpRoundTripper: tripper, }, nil } type client struct { httpRoundTripper http.RoundTripper } func (c *client) VerifyConnectionEnrollment(req *tlsmirror.EnrollmentConfirmationReq) (*tlsmirror.EnrollmentConfirmationResp, error) { requestMessage, err := protov2.Marshal(req) if err != nil { return nil, newError("failed to marshal enrollment confirmation request").Base(err) } serverID := base32.NewEncoding("0123456789abcdefghijklmnopqrstuv").WithPadding(base32.NoPadding).EncodeToString(req.ServerIdentifier) httpReq, err := http.NewRequest("POST", "http://"+serverID+tlsmirror.EnrollmentVerificationControlConnectionPostfix, bytes.NewReader(requestMessage)) if err != nil { return nil, newError("failed to create HTTP request").Base(err) } httpResp, err := c.httpRoundTripper.RoundTrip(httpReq) defer func() { if httpResp != nil && httpResp.Body != nil { httpResp.Body.Close() } }() if err != nil { return nil, newError("failed to send HTTP request").Base(err) } if httpResp.StatusCode != http.StatusOK { return nil, newError("unexpected HTTP response status: ", httpResp.StatusCode) } responseMessage, err := io.ReadAll(httpResp.Body) if err != nil { return nil, newError("failed to read HTTP response body").Base(err) } resp := &tlsmirror.EnrollmentConfirmationResp{} if err := protov2.Unmarshal(responseMessage, resp); err != nil { return nil, newError("failed to unmarshal enrollment confirmation response").Base(err) } return resp, nil } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation/clientbuilder.go ================================================ package httpenrollmentconfirmation import ( "context" "encoding/base32" "net" "net/http" "sync" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/httponconnection" ) func NewClientRoundTripperForEnrollmentConfirmation( dial func(network, addr string) (net.Conn, error), serverIdentity []byte, ) (http.RoundTripper, RoundTripperMetadata, error) { if dial == nil { return nil, nil, newError("nil dial function") } if len(serverIdentity) == 0 { return nil, nil, newError("nil or empty server identity") } cr := &clientRoundtripper{ dial: dial, serverIdentity: serverIdentity, } return cr, cr, nil } type RoundTripperMetadata interface { IsCreatingSecondaryNewConnection() bool } type clientRoundtripper struct { dial func(network, addr string) (net.Conn, error) serverIdentity []byte currentConnInnerConn common.Closable currentConn http.RoundTripper currentConnLock sync.RWMutex pendingNewConnection int // DO NOT ATTEMPT TO ACQUIRE ANY LOCK WHILE HOLDING THIS LOCK pendingNewConnectionLock sync.RWMutex } func (c *clientRoundtripper) IsCreatingSecondaryNewConnection() bool { defer c.pendingNewConnectionLock.RUnlock() c.pendingNewConnectionLock.RLock() return c.pendingNewConnection >= 1 } func (c *clientRoundtripper) RoundTrip(request *http.Request) (*http.Response, error) { if c.IsCreatingSecondaryNewConnection() { return nil, newError("another connection is being established, cannot create a secondary connection") } return c.roundTrip(request) } func (c *clientRoundtripper) roundTrip(request *http.Request) (*http.Response, error) { c.currentConnLock.RLock() if c.currentConn == nil { c.currentConnLock.RUnlock() c.pendingNewConnectionLock.Lock() c.pendingNewConnection += 1 c.pendingNewConnectionLock.Unlock() decreaseCount := func() { c.pendingNewConnectionLock.Lock() c.pendingNewConnection -= 1 c.pendingNewConnectionLock.Unlock() } if err := c.createNewConnection(); err != nil { decreaseCount() return nil, err // Failed to create a new connection } resp, err := c.roundTrip(request) decreaseCount() return resp, err } defer c.currentConnLock.RUnlock() timeoutContext, _ := context.WithTimeout(context.Background(), time.Second*30) //nolint:govet request = request.WithContext(timeoutContext) resp, err := c.currentConn.RoundTrip(request) // Use the current connection to perform the round trip if err != nil { if resp != nil && resp.Body != nil { resp.Body.Close() } defer func() { c.currentConnLock.RUnlock() c.currentConnLock.Lock() if c.currentConn != nil { c.currentConnInnerConn.Close() c.currentConnInnerConn = nil c.currentConn = nil } c.currentConnLock.Unlock() c.currentConnLock.RLock() }() return nil, newError("unable to roundtrip for enrollment verification").Base(err) } return resp, nil } func (c *clientRoundtripper) createNewConnection() error { c.currentConnLock.Lock() defer c.currentConnLock.Unlock() if c.currentConn != nil { return nil // Connection already exists } serverID := base32.NewEncoding("0123456789abcdefghijklmnopqrstuv").WithPadding(base32.NoPadding).EncodeToString(c.serverIdentity) conn, err := c.dial("tcp", serverID+tlsmirror.EnrollmentVerificationControlConnectionPostfix+":80") if err != nil { return newError("failed to dial server: ", err) } c.currentConnInnerConn = conn c.currentConn, err = httponconnection.NewSingleConnectionHTTPTransport(conn, "h2") if err != nil { _ = conn.Close() // Close the connection if transport creation fails return newError("failed to create HTTP transport: ", err) } return nil } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation/enrollment.go ================================================ package httpenrollmentconfirmation //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation/errors.generated.go ================================================ package httpenrollmentconfirmation import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation/hub.go ================================================ package httpenrollmentconfirmation import ( "context" "net" "net/http" "golang.org/x/net/http2" ) func NewHTTPConnectionHub(handler http.Handler) *HTTPConnectionHub { return &HTTPConnectionHub{ handler: handler, h2server: &http2.Server{}, } } type HTTPConnectionHub struct { handler http.Handler h2server *http2.Server } func (h *HTTPConnectionHub) ServeConnection(ctx context.Context, conn net.Conn) error { go h.h2server.ServeConn(conn, &http2.ServeConnOpts{ Handler: h.handler, }) return nil } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation/server.go ================================================ package httpenrollmentconfirmation import ( "io" "net/http" "strconv" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) func NewHTTPEnrollmentConfirmationServerFromConnectionEnrollmentConfirmation(confirmation tlsmirror.ConnectionEnrollmentConfirmation) (http.Handler, error) { if confirmation == nil { return nil, newError("nil confirmation") } return &server{ ConnectionEnrollmentConfirmation: confirmation, }, nil } type server struct { tlsmirror.ConnectionEnrollmentConfirmation } func (s *server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { requestBody, err := io.ReadAll(request.Body) if err != nil { http.Error(writer, "failed to read request body: "+err.Error(), http.StatusInternalServerError) return } req := &tlsmirror.EnrollmentConfirmationReq{} if err := proto.Unmarshal(requestBody, req); err != nil { http.Error(writer, "failed to unmarshal request: "+err.Error(), http.StatusBadRequest) return } resp, err := s.VerifyConnectionEnrollment(req) if err != nil { http.Error(writer, "failed to verify connection enrollment: "+err.Error(), http.StatusInternalServerError) return } responseBody, err := proto.Marshal(resp) if err != nil { http.Error(writer, "failed to marshal response: "+err.Error(), http.StatusInternalServerError) return } writer.Header().Set("Content-Type", "application/octet-stream") writer.Header().Set("Content-Length", strconv.Itoa(len(responseBody))) writer.WriteHeader(http.StatusOK) if _, err := writer.Write(responseBody); err != nil { http.Error(writer, "failed to write response: "+err.Error(), http.StatusInternalServerError) return } if flusher, ok := writer.(http.Flusher); ok { flusher.Flush() } else { http.Error(writer, "response writer does not support flushing", http.StatusInternalServerError) return } } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/keyderivation.go ================================================ package mirrorenrollment import ( "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcrypto" ) type EnrollmentKey struct { EnrollmentRequestKey []byte EnrollmentResponseKey []byte } func DeriveEnrollmentKeyWithClientAndServerRandom(primaryKey []byte, clientRandom []byte, serverRandom []byte) (*EnrollmentKey, error) { requestKey, responseKey, err := mirrorcrypto.DeriveEncryptionKey(primaryKey, clientRandom, serverRandom, ":connection-enrollment-re78HQNM-CmpRnPbr-PNJVRMhu") if err != nil { return nil, newError("failed to derive connection enrollment key").Base(err).AtError() } return &EnrollmentKey{ EnrollmentRequestKey: requestKey, EnrollmentResponseKey: responseKey, }, nil } func DeriveEnrollmentServerIdentifier(primaryKey []byte) ([]byte, error) { if len(primaryKey) != 32 { return nil, newError("invalid primary key size: ", len(primaryKey)) } // Use HKDF to derive a secondary key serverID, err := mirrorcrypto.DeriveSecondaryKey(primaryKey, ":connection-enrollment-server-identifier-av38NNGF-TJvRw7C3-p8KM8yKd") if err != nil { return nil, newError("unable to server identifier").Base(err) } return serverID, nil } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/client.go ================================================ package roundtripperenrollmentconfirmation import ( "context" csrand "crypto/rand" "io" "net" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" v2net "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet/request" "github.com/v2fly/v2ray-core/v5/transport/internet/security" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { if ctx == nil { return nil, newError("context cannot be nil") } if config == nil { return nil, newError("config cannot be nil") } rttClientConfig, err := serial.GetInstanceOf(config.RoundTripperClient) if err != nil { return nil, newError("failed to get instance of RoundTripperClient").Base(err) } rttClientI, err := common.CreateObject(ctx, rttClientConfig) if err != nil { return nil, newError("failed to create RoundTripperClient").Base(err) } rttClient, ok := rttClientI.(request.RoundTripperClient) if !ok { return nil, newError("RoundTripperClient is not a valid request.RoundTripperClient") } clientTemporaryIdentifier := make([]byte, 16) if _, err := csrand.Read(clientTemporaryIdentifier); err != nil { return nil, newError("failed to generate client temporary identifier").Base(err) } c := &Client{ ctx: ctx, config: config, rttClient: rttClient, clientTemporaryIdentifier: clientTemporaryIdentifier, } rttClient.OnTransportClientAssemblyReady(c) return c, nil } type Client struct { config *ClientConfig rttClient request.RoundTripperClient clientTemporaryIdentifier []byte ctx context.Context defaultOutboundTag string } func (c *Client) OnConnectionEnrollmentConfirmationClientInstanceConfigReady(config tlsmirror.ConnectionEnrollmentConfirmationClientInstanceConfig) { c.defaultOutboundTag = config.DefaultOutboundTag } func (c *Client) Dial(ctx context.Context) (net.Conn, error) { transportEnvironment := envctx.EnvironmentFromContext(c.ctx).(environment.TransportEnvironment) dialer := transportEnvironment.OutboundDialer() if dialer == nil { return nil, newError("no outbound dialer available in transport environment") } dest, err := v2net.ParseDestination(c.config.Dest) if err != nil { return nil, newError("failed to parse destination address").Base(err).AtError() } dest.Network = v2net.Network_TCP conn, err := dialer(c.ctx, dest, c.config.OutboundTag) if err != nil { return nil, newError("failed to dial to destination").Base(err).AtError() } if c.config.SecurityConfig != nil { securityConfigSetting, err := serial.GetInstanceOf(c.config.SecurityConfig) if err != nil { return nil, newError("unable to get security config instance").Base(err) } securityEngine, err := common.CreateObject(c.ctx, securityConfigSetting) if err != nil { return nil, newError("unable to create security engine from security settings").Base(err) } securityEngineTyped, ok := securityEngine.(security.Engine) if !ok { return nil, newError("type assertion error when create security engine from security settings") } conn, err = securityEngineTyped.Client(conn, security.OptionWithDestination{Dest: dest}) if err != nil { return nil, newError("unable to create security protocol client from security engine").Base(err) } } return conn, nil } func (c *Client) Tripper() request.Tripper { return c.rttClient } func (c *Client) AutoImplDialer() request.Dialer { return c } func (c *Client) VerifyConnectionEnrollment(req *tlsmirror.EnrollmentConfirmationReq) (*tlsmirror.EnrollmentConfirmationResp, error) { connectionTagServerID := req.ServerIdentifier if c.config.ServerIdentity != nil { connectionTagServerID = c.config.ServerIdentity } var replyAddressTag [16]byte _, err := io.ReadFull(csrand.Reader, replyAddressTag[:]) if err != nil { return nil, newError("failed to generate reply address tag").Base(err) } connectionTag := append(replyAddressTag[:], connectionTagServerID...) //nolint:gocritic req.ClientIdentifier = c.clientTemporaryIdentifier req.ReplyAddressTag = replyAddressTag[:] wrappedData, err := proto.Marshal(req) if err != nil { return nil, newError("failed to marshal enrollment confirmation request").Base(err) } wreq := request.Request{ Data: wrappedData, ConnectionTag: connectionTag, } resp, err := c.rttClient.RoundTrip(c.ctx, wreq) if err != nil { return nil, newError("failed to perform round trip").Base(err) } confirmationResp := &tlsmirror.EnrollmentConfirmationResp{} if err := proto.Unmarshal(resp.Data, confirmationResp); err != nil { return nil, newError("failed to unmarshal enrollment confirmation response").Base(err) } return confirmationResp, nil } func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewClient(ctx, config.(*ClientConfig)) })) } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/config.pb.go ================================================ package roundtripperenrollmentconfirmation import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ClientConfig struct { state protoimpl.MessageState `protogen:"open.v1"` RoundTripperClient *anypb.Any `protobuf:"bytes,1,opt,name=round_tripper_client,json=roundTripperClient,proto3" json:"round_tripper_client,omitempty"` SecurityConfig *anypb.Any `protobuf:"bytes,2,opt,name=security_config,json=securityConfig,proto3" json:"security_config,omitempty"` Dest string `protobuf:"bytes,3,opt,name=dest,proto3" json:"dest,omitempty"` OutboundTag string `protobuf:"bytes,4,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"` ServerIdentity []byte `protobuf:"bytes,5,opt,name=server_identity,json=serverIdentity,proto3" json:"server_identity,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientConfig) Reset() { *x = ClientConfig{} mi := &file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientConfig) ProtoMessage() {} func (x *ClientConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. func (*ClientConfig) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescGZIP(), []int{0} } func (x *ClientConfig) GetRoundTripperClient() *anypb.Any { if x != nil { return x.RoundTripperClient } return nil } func (x *ClientConfig) GetSecurityConfig() *anypb.Any { if x != nil { return x.SecurityConfig } return nil } func (x *ClientConfig) GetDest() string { if x != nil { return x.Dest } return "" } func (x *ClientConfig) GetOutboundTag() string { if x != nil { return x.OutboundTag } return "" } func (x *ClientConfig) GetServerIdentity() []byte { if x != nil { return x.ServerIdentity } return nil } type ServerInverseRoleConfig struct { state protoimpl.MessageState `protogen:"open.v1"` RoundTripperClient *anypb.Any `protobuf:"bytes,1,opt,name=round_tripper_client,json=roundTripperClient,proto3" json:"round_tripper_client,omitempty"` SecurityConfig *anypb.Any `protobuf:"bytes,2,opt,name=security_config,json=securityConfig,proto3" json:"security_config,omitempty"` Dest string `protobuf:"bytes,3,opt,name=dest,proto3" json:"dest,omitempty"` OutboundTag string `protobuf:"bytes,4,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"` ServerIdentity []byte `protobuf:"bytes,5,opt,name=server_identity,json=serverIdentity,proto3" json:"server_identity,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerInverseRoleConfig) Reset() { *x = ServerInverseRoleConfig{} mi := &file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerInverseRoleConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerInverseRoleConfig) ProtoMessage() {} func (x *ServerInverseRoleConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerInverseRoleConfig.ProtoReflect.Descriptor instead. func (*ServerInverseRoleConfig) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescGZIP(), []int{1} } func (x *ServerInverseRoleConfig) GetRoundTripperClient() *anypb.Any { if x != nil { return x.RoundTripperClient } return nil } func (x *ServerInverseRoleConfig) GetSecurityConfig() *anypb.Any { if x != nil { return x.SecurityConfig } return nil } func (x *ServerInverseRoleConfig) GetDest() string { if x != nil { return x.Dest } return "" } func (x *ServerInverseRoleConfig) GetOutboundTag() string { if x != nil { return x.OutboundTag } return "" } func (x *ServerInverseRoleConfig) GetServerIdentity() []byte { if x != nil { return x.ServerIdentity } return nil } type ServerConfig struct { state protoimpl.MessageState `protogen:"open.v1"` RoundTripperServer *anypb.Any `protobuf:"bytes,2,opt,name=round_tripper_server,json=roundTripperServer,proto3" json:"round_tripper_server,omitempty"` Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServerConfig) Reset() { *x = ServerConfig{} mi := &file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServerConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerConfig) ProtoMessage() {} func (x *ServerConfig) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. func (*ServerConfig) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescGZIP(), []int{2} } func (x *ServerConfig) GetRoundTripperServer() *anypb.Any { if x != nil { return x.RoundTripperServer } return nil } func (x *ServerConfig) GetListen() string { if x != nil { return x.Listen } return "" } var File_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto protoreflect.FileDescriptor const file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDesc = "" + "\n" + "]transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/config.proto\x12[v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation\x1a common/protoext/extensions.proto\x1a\x19google/protobuf/any.proto\"\xf5\x01\n" + "\fClientConfig\x12F\n" + "\x14round_tripper_client\x18\x01 \x01(\v2\x14.google.protobuf.AnyR\x12roundTripperClient\x12=\n" + "\x0fsecurity_config\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x0esecurityConfig\x12\x12\n" + "\x04dest\x18\x03 \x01(\tR\x04dest\x12!\n" + "\foutbound_tag\x18\x04 \x01(\tR\voutboundTag\x12'\n" + "\x0fserver_identity\x18\x05 \x01(\fR\x0eserverIdentity\"\x80\x02\n" + "\x17ServerInverseRoleConfig\x12F\n" + "\x14round_tripper_client\x18\x01 \x01(\v2\x14.google.protobuf.AnyR\x12roundTripperClient\x12=\n" + "\x0fsecurity_config\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x0esecurityConfig\x12\x12\n" + "\x04dest\x18\x03 \x01(\tR\x04dest\x12!\n" + "\foutbound_tag\x18\x04 \x01(\tR\voutboundTag\x12'\n" + "\x0fserver_identity\x18\x05 \x01(\fR\x0eserverIdentity\"n\n" + "\fServerConfig\x12F\n" + "\x14round_tripper_server\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x12roundTripperServer\x12\x16\n" + "\x06listen\x18\x03 \x01(\tR\x06listenB\xb2\x02\n" + "_com.v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmationP\x01Zogithub.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation\xaa\x02[V2Ray.Core.Transport.Internet.Tlsmirror.MirrorEnrollment.RoundTripperEnrollmentConfirmationb\x06proto3" var ( file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescOnce sync.Once file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescData []byte ) func file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescGZIP() []byte { file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescOnce.Do(func() { file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDesc), len(file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDesc))) }) return file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDescData } var file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_goTypes = []any{ (*ClientConfig)(nil), // 0: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation.ClientConfig (*ServerInverseRoleConfig)(nil), // 1: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation.ServerInverseRoleConfig (*ServerConfig)(nil), // 2: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation.ServerConfig (*anypb.Any)(nil), // 3: google.protobuf.Any } var file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_depIdxs = []int32{ 3, // 0: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation.ClientConfig.round_tripper_client:type_name -> google.protobuf.Any 3, // 1: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation.ClientConfig.security_config:type_name -> google.protobuf.Any 3, // 2: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation.ServerInverseRoleConfig.round_tripper_client:type_name -> google.protobuf.Any 3, // 3: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation.ServerInverseRoleConfig.security_config:type_name -> google.protobuf.Any 3, // 4: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation.ServerConfig.round_tripper_server:type_name -> google.protobuf.Any 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_init() } func file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_init() { if File_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDesc), len(file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_goTypes, DependencyIndexes: file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_depIdxs, MessageInfos: file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_msgTypes, }.Build() File_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto = out.File file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_goTypes = nil file_transport_internet_tlsmirror_mirrorenrollment_roundtripperenrollmentconfirmation_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation; option csharp_namespace = "V2Ray.Core.Transport.Internet.Tlsmirror.MirrorEnrollment.RoundTripperEnrollmentConfirmation"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation"; option java_package = "com.v2ray.core.transport.internet.tlsmirror.mirrorenrollment.roundtripperenrollmentconfirmation"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "google/protobuf/any.proto"; message ClientConfig { google.protobuf.Any round_tripper_client = 1; google.protobuf.Any security_config = 2; string dest = 3; string outbound_tag = 4; bytes server_identity = 5; } message ServerInverseRoleConfig { google.protobuf.Any round_tripper_client = 1; google.protobuf.Any security_config = 2; string dest = 3; string outbound_tag = 4; bytes server_identity = 5; } message ServerConfig { google.protobuf.Any round_tripper_server = 2; string listen = 3; } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/errors.generated.go ================================================ package roundtripperenrollmentconfirmation import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/rttconfirmation.go ================================================ package roundtripperenrollmentconfirmation //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/server.go ================================================ package roundtripperenrollmentconfirmation import ( "context" "net" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" v2net "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet/request" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { s := &Server{ ctx: ctx, config: config, } if err := s.init(); err != nil { return nil, newError("failed to initialize RoundTripperEnrollmentConfirmation server").Base(err).AtError() } return s, nil } type Server struct { config *ServerConfig ctx context.Context enrollmentProcessor tlsmirror.ConnectionEnrollmentConfirmationProcessor rttServer request.RoundTripperServer } func (s *Server) OnConnectionEnrollmentConfirmationServerInstanceConfigReady(config tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfig) { s.enrollmentProcessor = config.EnrollmentProcessor } func (s *Server) Listen(ctx context.Context) (v2net.Listener, error) { transportEnvironment := envctx.EnvironmentFromContext(s.ctx).(environment.TransportEnvironment) listener := transportEnvironment.Listener() addr, err := v2net.ParseDestination(s.config.Listen) if err != nil { panic(newError("invalid listen address " + s.config.Listen).Base(err).AtError()) } netaddr := &net.TCPAddr{IP: addr.Address.IP(), Port: int(addr.Port)} l, err := listener.Listen(s.ctx, netaddr, nil) if err != nil { panic(newError("failed to listen on " + s.config.Listen).Base(err).AtError()) } return l, nil } func (s *Server) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) { enrollmentReq := &tlsmirror.EnrollmentConfirmationReq{} err = proto.Unmarshal(req.Data, enrollmentReq) if err != nil { return request.Response{}, newError("failed to unmarshal enrollment confirmation request").Base(err).AtError() } enrollmentResp, err := s.enrollmentProcessor.VerifyConnectionEnrollment(enrollmentReq) if err != nil { return request.Response{}, newError("failed to process enrollment confirmation request").Base(err).AtError() } respData, err := proto.Marshal(enrollmentResp) if err != nil { return request.Response{}, newError("failed to marshal enrollment confirmation response").Base(err).AtError() } return request.Response{ Data: respData, }, nil } func (s *Server) TripperReceiver() request.TripperReceiver { return s } func (s *Server) SessionReceiver() request.SessionReceiver { return nil } func (s *Server) AutoImplListener() request.Listener { return s } func (s *Server) init() error { if s.config == nil { return newError("nil ServerConfig") } if s.config.RoundTripperServer == nil { return newError("nil RoundTripperServer in ServerConfig") } RoundTripperServerConfig, err := serial.GetInstanceOf(s.config.RoundTripperServer) if err != nil { return newError("failed to get instance of RoundTripperServer").Base(err).AtError() } RoundTripperServerObj, err := common.CreateObject(s.ctx, RoundTripperServerConfig) if err != nil { return newError("failed to create RoundTripperServer").Base(err).AtError() } RoundTripperServerTyped, ok := RoundTripperServerObj.(request.RoundTripperServer) if !ok { return newError("RoundTripperServer is not a valid request.RoundTripperServer") } s.rttServer = RoundTripperServerTyped s.rttServer.OnTransportServerAssemblyReady(s) if err := s.rttServer.Start(); err != nil { return newError("failed to start RoundTripperServer").Base(err).AtError() } return nil } func init() { common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServer(ctx, config.(*ServerConfig)) })) } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/serverinverserole.go ================================================ package roundtripperenrollmentconfirmation import ( "context" csrand "crypto/rand" "net" "google.golang.org/protobuf/proto" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" v2net "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet/request" "github.com/v2fly/v2ray-core/v5/transport/internet/security" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) func NewServerInverseRole(ctx context.Context, config *ServerInverseRoleConfig) (*ServerInverseRole, error) { if ctx == nil { return nil, newError("context cannot be nil") } if config == nil { return nil, newError("config cannot be nil") } rttClientConfig, err := serial.GetInstanceOf(config.RoundTripperClient) if err != nil { return nil, newError("failed to get instance of RoundTripperClient").Base(err) } rttClientI, err := common.CreateObject(ctx, rttClientConfig) if err != nil { return nil, newError("failed to create RoundTripperClient").Base(err) } rttClient, ok := rttClientI.(request.RoundTripperClient) if !ok { return nil, newError("RoundTripperClient is not a valid request.RoundTripperClient") } clientTemporaryIdentifier := make([]byte, 16) if _, err := csrand.Read(clientTemporaryIdentifier); err != nil { return nil, newError("failed to generate client temporary identifier").Base(err) } c := &ServerInverseRole{ ctx: ctx, config: config, rttClient: rttClient, clientTemporaryIdentifier: clientTemporaryIdentifier, } rttClient.OnTransportClientAssemblyReady(c) go c.worker(ctx) return c, nil } type ServerInverseRole struct { config *ServerInverseRoleConfig rttClient request.RoundTripperClient clientTemporaryIdentifier []byte ctx context.Context defaultOutboundTag string enrollmentProcessor tlsmirror.ConnectionEnrollmentConfirmationProcessor } func (c *ServerInverseRole) worker(ctx context.Context) { for ctx.Err() == nil { err := c.pollRemoteForEnrollment(ctx) if err != nil { newError("error polling remote for enrollment").Base(err).AtWarning().WriteToLog() } } newError("inverse role server quitted").AtWarning().WriteToLog() } func (c *ServerInverseRole) Dial(ctx context.Context) (net.Conn, error) { transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) dialer := transportEnvironment.OutboundDialer() if dialer == nil { return nil, newError("no outbound dialer available in transport environment") } dest, err := v2net.ParseDestination(c.config.Dest) if err != nil { return nil, newError("failed to parse destination address").Base(err).AtError() } dest.Network = v2net.Network_TCP conn, err := dialer(ctx, dest, c.config.OutboundTag) if err != nil { return nil, newError("failed to dial to destination").Base(err).AtError() } if c.config.SecurityConfig != nil { securityConfigSetting, err := serial.GetInstanceOf(c.config.SecurityConfig) if err != nil { return nil, newError("unable to get security config instance").Base(err) } securityEngine, err := common.CreateObject(c.ctx, securityConfigSetting) if err != nil { return nil, newError("unable to create security engine from security settings").Base(err) } securityEngineTyped, ok := securityEngine.(security.Engine) if !ok { return nil, newError("type assertion error when create security engine from security settings") } conn, err = securityEngineTyped.Client(conn, security.OptionWithDestination{Dest: dest}) if err != nil { return nil, newError("unable to create security protocol client from security engine").Base(err) } } return conn, nil } func (c *ServerInverseRole) Tripper() request.Tripper { return c.rttClient } func (c *ServerInverseRole) AutoImplDialer() request.Dialer { return c } func (s *ServerInverseRole) OnConnectionEnrollmentConfirmationServerInstanceConfigReady(config tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfig) { s.enrollmentProcessor = config.EnrollmentProcessor } func (s *ServerInverseRole) pollRemoteForEnrollment(ctx context.Context) error { pollAs := s.config.ServerIdentity req := request.Request{ ConnectionTag: pollAs, } resp, err := s.rttClient.RoundTrip(ctx, req) if err != nil { return newError("failed to poll remote for enrollment").Base(err) } if resp.Data == nil { return newError("no enrollment confirmation response received from remote") } enrollmentReq := &tlsmirror.EnrollmentConfirmationReq{} err = proto.Unmarshal(resp.Data, enrollmentReq) if err != nil { return newError("failed to unmarshal enrollment confirmation request").Base(err).AtError() } enrollmentResp, err := s.enrollmentProcessor.VerifyConnectionEnrollment(enrollmentReq) if err != nil { return newError("failed to process enrollment confirmation request").Base(err).AtError() } respData, err := proto.Marshal(enrollmentResp) if err != nil { return newError("failed to marshal enrollment confirmation response").Base(err).AtError() } respAs := append(s.config.ServerIdentity, enrollmentReq.ReplyAddressTag...) //nolint:gocritic _, err = s.rttClient.RoundTrip(ctx, request.Request{ Data: respData, ConnectionTag: respAs, }) if err != nil { return newError("failed to send enrollment confirmation response back to remote").Base(err) } newError("successfully processed enrollment confirmation request").AtDebug().WriteToLog() return nil } func init() { common.Must(common.RegisterConfig((*ServerInverseRoleConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewServerInverseRole(ctx, config.(*ServerInverseRoleConfig)) })) } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/server.go ================================================ package mirrorenrollment import ( "context" "net" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation" ) func NewEnrollmentConfirmationServer(ctx context.Context, config *Config, enrollmentProcessor tlsmirror.ConnectionEnrollmentConfirmationProcessor) (*EnrollmentConfirmationServer, error) { if ctx == nil { return nil, newError("context cannot be nil") } if config == nil { return nil, newError("config cannot be nil") } for _, handler := range config.BootstrapIngressUrl { if handler == "" { return nil, newError("bootstrap ingress URL cannot be empty") } anyPb, err := AnyFromLink(handler) if err != nil { return nil, newError("invalid bootstrap ingress URL").Base(err).AtError() } if anyPb == nil { return nil, newError("bootstrap ingress URL did not produce valid Any") } config.BootstrapIngressConfig = append(config.BootstrapIngressConfig, anyPb) } if enrollmentProcessor == nil { return nil, newError("enrollment processor cannot be nil") } enrollmentHandler, err := httpenrollmentconfirmation.NewHTTPEnrollmentConfirmationServerFromConnectionEnrollmentConfirmation(enrollmentProcessor) if err != nil { return nil, newError("failed to create HTTP enrollment confirmation server").Base(err).AtError() } primaryIngressConnectionHandler := httpenrollmentconfirmation.NewHTTPConnectionHub(enrollmentHandler) s := &EnrollmentConfirmationServer{ ctx: ctx, config: config, enrollmentProcessor: enrollmentProcessor, primaryIngressConnectionHandler: primaryIngressConnectionHandler, } if err = s.init(); err != nil { return nil, newError("failed to initialize enrollment confirmation server").Base(err).AtError() } return s, nil } type EnrollmentConfirmationServer struct { ctx context.Context config *Config enrollmentProcessor tlsmirror.ConnectionEnrollmentConfirmationProcessor primaryIngressConnectionHandler *httpenrollmentconfirmation.HTTPConnectionHub bootstrapIngressConnectionHandlers []tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfigReceiver } func (s *EnrollmentConfirmationServer) HandlePrimaryIngressConnection(ctx context.Context, conn net.Conn) error { err := s.primaryIngressConnectionHandler.ServeConnection(ctx, conn) if err != nil { return newError("failed to handle primary ingress connection").Base(err).AtError() } return nil } func (s *EnrollmentConfirmationServer) init() error { for _, handler := range s.config.BootstrapIngressConfig { bootstrapEnrollmentHandler, err := serial.GetInstanceOf(handler) if err != nil { return newError("failed to get instance of bootstrap enrollment handler").Base(err).AtError() } bootstrapEnrollmentHandlerObj, err := common.CreateObject(s.ctx, bootstrapEnrollmentHandler) if err != nil { return newError("failed to create bootstrap enrollment handler").Base(err).AtError() } bootstrapEnrollmentHandlerObjTyped, ok := bootstrapEnrollmentHandlerObj.(tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfigReceiver) if !ok { return newError("bootstrap enrollment handler is not a valid ConnectionEnrollmentConfirmationServerInstanceConfigReceiver") } bootstrapEnrollmentHandlerObjTyped.OnConnectionEnrollmentConfirmationServerInstanceConfigReady( tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfig{ EnrollmentProcessor: s.enrollmentProcessor, }) s.bootstrapIngressConnectionHandlers = append(s.bootstrapIngressConnectionHandlers, bootstrapEnrollmentHandlerObjTyped) } return nil } ================================================ FILE: transport/internet/tlsmirror/mirrorenrollment/serverenrollmentprocessor.go ================================================ package mirrorenrollment import ( "context" "sync" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" ) func NewServerEnrollmentProcessor(primaryKey []byte) (tlsmirror.ConnectionEnrollmentConfirmationProcessor, error) { if len(primaryKey) == 0 { return nil, newError("primary key cannot be empty") } return &serverEnrollmentProcessor{ primaryKey: primaryKey, }, nil } type serverEnrollmentProcessor struct { primaryKey []byte activeConnections sync.Map } func (p *serverEnrollmentProcessor) AddConnection(ctx context.Context, clientRandom, serverRandom []byte, conn tlsmirror.InsertableTLSConnForEnrollment) (tlsmirror.RemoveConnectionFunc, error) { if conn == nil { return nil, newError("nil InsertableTLSConnForEnrollment") } enrollmentKey, err := DeriveEnrollmentKeyWithClientAndServerRandom(p.primaryKey, clientRandom, serverRandom) if err != nil { return nil, newError("failed to derive enrollment key").Base(err).AtError() } if _, loaded := p.activeConnections.LoadOrStore(string(enrollmentKey.EnrollmentRequestKey), conn); loaded { return nil, newError("connection with ID ", enrollmentKey.EnrollmentRequestKey, " already exists") } return func() error { p.activeConnections.Delete(string(enrollmentKey.EnrollmentRequestKey)) return nil }, nil } func (p *serverEnrollmentProcessor) VerifyConnectionEnrollment(req *tlsmirror.EnrollmentConfirmationReq) (*tlsmirror.EnrollmentConfirmationResp, error) { if req == nil { return nil, newError("nil EnrollmentConfirmationReq") } if req.CarrierTlsConnectionClientRandom == nil || req.CarrierTlsConnectionServerRandom == nil { return nil, newError("missing client or server random in EnrollmentConfirmationReq") } enrollmentKey, err := DeriveEnrollmentKeyWithClientAndServerRandom(p.primaryKey, req.CarrierTlsConnectionClientRandom, req.CarrierTlsConnectionServerRandom) if err != nil { return nil, newError("failed to derive enrollment key").Base(err).AtError() } if conn, ok := p.activeConnections.Load(string(enrollmentKey.EnrollmentRequestKey)); ok { insertableConn, ok := conn.(tlsmirror.InsertableTLSConnForEnrollment) if !ok { return nil, newError("connection does not implement InsertableTLSConnForEnrollment") } return insertableConn.VerifyConnectionEnrollment(req) } return &tlsmirror.EnrollmentConfirmationResp{ Enrolled: false, }, nil } ================================================ FILE: transport/internet/tlsmirror/server/ciphersuits_lookup.go ================================================ package server func newEmptyCipherSuiteLookuper() *ciphersuiteLookuper { return &ciphersuiteLookuper{ ciphersuiteMap: make(map[uint16]bool), } } func newCipherSuiteLookuperFromUint32Array(source []uint32) (*ciphersuiteLookuper, error) { if len(source) == 0 { return nil, newError("ciphersuite list is empty") } ciphersuiteUint16Array := make([]uint16, len(source)) for i, ciphersuite := range source { if ciphersuite > 0xFFFF { return nil, newError("ciphersuite value out of range: ", ciphersuite) } ciphersuiteUint16Array[i] = uint16(ciphersuite) } return newCipherSuiteLookuperFromUint16Array(ciphersuiteUint16Array), nil } func newCipherSuiteLookuperFromUint16Array(source []uint16) *ciphersuiteLookuper { ciphersuiteMap := make(map[uint16]bool, len(source)) for _, ciphersuite := range source { ciphersuiteMap[ciphersuite] = true } return &ciphersuiteLookuper{ ciphersuiteMap: ciphersuiteMap, } } type ciphersuiteLookuper struct { ciphersuiteMap map[uint16]bool } func (l *ciphersuiteLookuper) Lookup(ciphersuite uint16) bool { if result, ok := l.ciphersuiteMap[ciphersuite]; ok { return result } return false } ================================================ FILE: transport/internet/tlsmirror/server/client.go ================================================ package server import ( "context" cryptoRand "crypto/rand" "math/big" "time" "github.com/golang/protobuf/proto" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorbase" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcommon" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/tlstrafficgen" ) func newPersistentMirrorTLSDialer(ctx context.Context, config *Config, serverAddress net.Destination, overrideSecuritySetting proto.Message) (*persistentMirrorTLSDialer, error) { persistentDialer := &persistentMirrorTLSDialer{ ctx: ctx, serverAddress: serverAddress, overridingSecuritySettings: overrideSecuritySetting, } err := persistentDialer.init(ctx, config) if err != nil { return nil, newError("failed to initialize persistent mirror TLS dialer").Base(err) } return persistentDialer, nil } type persistentMirrorTLSDialer struct { ctx context.Context config *Config requestNewConnection func(ctx context.Context) error incomingConnections chan net.Conn listener *OutboundListener outbound *Outbound serverAddress net.Destination overridingSecuritySettings proto.Message trafficGenerator *tlstrafficgen.TrafficGenerator obm outbound.Manager explicitNonceCiphersuiteLookup *ciphersuiteLookuper enrollmentConfirmationClient *mirrorenrollment.EnrollmentConfirmationClient enrollmentServerIdentifier []byte } func (d *persistentMirrorTLSDialer) init(ctx context.Context, config *Config) error { if err := core.RequireFeatures(ctx, func(om outbound.Manager) { d.obm = om }); err != nil { return err } d.requestNewConnection = func(ctx context.Context) error { return nil } d.ctx = ctx d.config = config d.incomingConnections = make(chan net.Conn, 4) d.listener = NewOutboundListener() d.outbound = NewOutbound(d.config.CarrierConnectionTag, d.listener) if len(d.config.ExplicitNonceCiphersuites) > 0 { var err error d.explicitNonceCiphersuiteLookup, err = newCipherSuiteLookuperFromUint32Array(d.config.ExplicitNonceCiphersuites) if err != nil { return newError("failed to create explicit nonce ciphersuite lookuper").Base(err) } } else { d.explicitNonceCiphersuiteLookup = newEmptyCipherSuiteLookuper() newError("no explicit nonce ciphersuites configured, all ciphersuites will be treated as non-explicit nonce").AtWarning().WriteToLog() } go func() { err := d.outbound.Start() if err != nil { newError("failed to start outbound listener").Base(err).AtWarning().WriteToLog() return } if err := d.obm.RemoveHandler(context.Background(), d.config.CarrierConnectionTag); err != nil { newError("failed to remove existing handler").WriteToLog() } err = d.obm.AddHandler(context.Background(), d.outbound) if err != nil { newError("failed to add outbound handler").Base(err).AtWarning().WriteToLog() return } for { var ctx context.Context conn, err := d.listener.Accept() if err != nil { break } if ctxGetter, ok := conn.(connectionContextGetter); ok { ctx = ctxGetter.GetConnectionContext() } else { ctx = d.ctx newError("connection does not implement connectionContextGetter, using default context").AtError().WriteToLog() } d.handleIncomingCarrierConnection(ctx, conn) } }() if d.config.EmbeddedTrafficGenerator != nil { if d.overridingSecuritySettings != nil && d.config.EmbeddedTrafficGenerator.SecuritySettings == nil { d.config.EmbeddedTrafficGenerator.SecuritySettings = serial.ToTypedMessage(d.overridingSecuritySettings) } d.trafficGenerator = tlstrafficgen.NewTrafficGenerator(d.ctx, d.config.EmbeddedTrafficGenerator, d.serverAddress, d.config.CarrierConnectionTag) d.requestNewConnection = func(ctx context.Context) error { go func() { err := d.trafficGenerator.GenerateNextTraffic(d.ctx) if err != nil { newError("failed to generate next traffic").Base(err).AtWarning().WriteToLog() } else { newError("traffic generation request sent").AtDebug().WriteToLog() } }() return nil } } if d.config.ConnectionEnrolment != nil { enrollmentServerIdentifier, err := mirrorenrollment.DeriveEnrollmentServerIdentifier(d.config.PrimaryKey) if err != nil { return newError("failed to derive enrollment server identifier").Base(err).AtError() } d.enrollmentServerIdentifier = enrollmentServerIdentifier d.enrollmentConfirmationClient, err = mirrorenrollment.NewEnrollmentConfirmationClient(d.ctx, d.config.ConnectionEnrolment, enrollmentServerIdentifier) if err != nil { return newError("failed to create enrollment confirmation client").Base(err).AtError() } } return nil } func (d *persistentMirrorTLSDialer) handleIncomingCarrierConnection(ctx context.Context, conn net.Conn) { transportEnvironment := envctx.EnvironmentFromContext(d.ctx).(environment.TransportEnvironment) dialer := transportEnvironment.OutboundDialer() forwardConn, err := dialer(d.ctx, d.serverAddress, d.config.ForwardTag) if err != nil { newError("failed to dial to destination").Base(err).AtWarning().WriteToLog() return } var firstWriteDelay time.Duration if d.config.DeferInstanceDerivedWriteTime != nil { firstWriteDelay = time.Duration(d.config.DeferInstanceDerivedWriteTime.BaseNanoseconds) if d.config.DeferInstanceDerivedWriteTime.UniformRandomMultiplierNanoseconds > 0 { uniformRandomAdd := big.NewInt(int64(d.config.DeferInstanceDerivedWriteTime.UniformRandomMultiplierNanoseconds)) uniformRandomAddBigInt, err := cryptoRand.Int(cryptoRand.Reader, uniformRandomAdd) if err != nil { newError("failed to generate random delay").Base(err).AtWarning().WriteToLog() return } uniformRandomAddU64 := uint64(uniformRandomAddBigInt.Int64()) firstWriteDelay += time.Duration(uniformRandomAddU64) } } ctx, cancel := context.WithCancel(ctx) cconnState := &clientConnState{ ctx: ctx, done: cancel, localAddr: conn.LocalAddr(), remoteAddr: conn.RemoteAddr(), handler: d.handleIncomingReadyConnection, primaryKey: d.config.PrimaryKey, readPipe: make(chan []byte, 1), firstWrite: true, firstWriteDelay: firstWriteDelay, transportLayerPadding: d.config.TransportLayerPadding, sequenceWatermarkEnabled: d.config.SequenceWatermarkingEnabled, } cconnState.mirrorConn = mirrorbase.NewMirroredTLSConn(ctx, conn, forwardConn, cconnState.onC2SMessage, cconnState.onS2CMessage, conn, d.explicitNonceCiphersuiteLookup.Lookup, cconnState.onC2SMessageTx, cconnState.onS2CMessageTx) } type connectionContextGetter interface { GetConnectionContext() context.Context } type verifyConnectionEnrollment interface { VerifyConnectionEnrollmentWithProcessor(connectionEnrollmentConfirmationClient tlsmirror.ConnectionEnrollmentConfirmation) error } func (d *persistentMirrorTLSDialer) handleIncomingReadyConnection(conn internet.Connection) { go func() { if d.config.ConnectionEnrolment != nil { if enrollableConn, ok := conn.(verifyConnectionEnrollment); ok { if d.enrollmentConfirmationClient != nil { err := enrollableConn.VerifyConnectionEnrollmentWithProcessor(d.enrollmentConfirmationClient) if err != nil { newError("failed to verify connection enrollment").Base(err).AtWarning().WriteToLog() return } } else { newError("enrollment confirmation client is not set, connection rejected").AtWarning().WriteToLog() return } } else { newError("connection does not implement verifyConnectionEnrollment, connection rejected").AtWarning().WriteToLog() return } } var waitedForReady bool if getter, ok := conn.(connectionContextGetter); ok { ctx := getter.GetConnectionContext() if managedConnectionController := ctx.Value(tlsmirror.TrafficGeneratorManagedConnectionContextKey); managedConnectionController != nil { if controller, ok := managedConnectionController.(tlsmirror.TrafficGeneratorManagedConnection); ok { select { // nolint: staticcheck case <-controller.WaitConnectionReady().Done(): waitedForReady = true // TODO: connection might become invalid and never ready, handle this case if controller.IsConnectionInvalidated() { newError("connection is invalidated, skipping").AtWarning().WriteToLog() return } case <-ctx.Done(): return case <-d.ctx.Done(): return } } } } if !waitedForReady { newError("unable to wait for connection ready, please verify your setup").AtWarning().WriteToLog() } d.incomingConnections <- conn }() } func (d *persistentMirrorTLSDialer) Dial(ctx context.Context, dest net.Destination, settings *internet.MemoryStreamConfig, ) (internet.Connection, error) { if len(d.enrollmentServerIdentifier) > 0 { if mirrorcommon.IsLoopbackProtectionEnabled(ctx, d.enrollmentServerIdentifier) { return nil, newError("loopback protection: refusing to dial to self") } } var recvConn net.Conn select { case conn := <-d.incomingConnections: recvConn = conn default: err := d.requestNewConnection(ctx) if err != nil { return nil, newError("failed to request new connection").Base(err) } timer := time.NewTimer(10 * time.Second) defer timer.Stop() select { // nolint: staticcheck case conn := <-d.incomingConnections: recvConn = conn case <-timer.C: return nil, newError("timeout waiting for incoming connection") case <-ctx.Done(): return nil, newError("context done while waiting for incoming connection") } } if recvConn == nil { return nil, newError("failed to receive connection") } return recvConn, nil } func Dial(ctx context.Context, dest net.Destination, settings *internet.MemoryStreamConfig) (internet.Connection, error) { transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) dialer, err := transportEnvironment.TransientStorage().Get(ctx, "persistentDialer") if err != nil { var securitySetting proto.Message if settings.SecurityType != "" && settings.SecurityType != "none" { securitySetting = settings.SecuritySettings.(proto.Message) } config := settings.ProtocolSettings.(*Config) detachedContext := core.ToBackgroundDetachedContext(ctx) dialer, err = newPersistentMirrorTLSDialer(detachedContext, config, dest, securitySetting) if err != nil { return nil, newError("failed to create persistent mirror TLS dialer").Base(err) } err = transportEnvironment.TransientStorage().Put(ctx, "persistentDialer", dialer) if err != nil { return nil, newError("failed to put persistent dialer").Base(err) } } conn, err := dialer.(*persistentMirrorTLSDialer).Dial(ctx, dest, settings) if err != nil { return nil, newError("failed to dial").Base(err) } return conn, nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, Dial)) } ================================================ FILE: transport/internet/tlsmirror/server/client_conn.go ================================================ package server import ( "bytes" "context" "crypto/cipher" gonet "net" "time" "golang.org/x/crypto/chacha20" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcommon" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcrypto" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment" ) type clientConnState struct { ctx context.Context done context.CancelFunc handler internet.ConnHandler mirrorConn tlsmirror.InsertableTLSConn localAddr net.Addr remoteAddr net.Addr activated bool decryptor *mirrorcrypto.Decryptor encryptor *mirrorcrypto.Encryptor primaryKey []byte readPipe chan []byte readBuffer *bytes.Buffer protocolVersion [2]byte firstWrite bool firstWriteDelay time.Duration transportLayerPadding *TransportLayerPadding sequenceWatermarkEnabled bool sequenceWatermarkTx, sequenceWatermarkRx cipher.Stream } func (s *clientConnState) GetConnectionContext() context.Context { return s.ctx } func (s *clientConnState) Read(b []byte) (n int, err error) { if s.readBuffer != nil { n, _ = s.readBuffer.Read(b) if n > 0 { return n, nil } s.readBuffer = nil } select { case <-s.ctx.Done(): return 0, s.ctx.Err() case data := <-s.readPipe: if s.transportLayerPadding != nil && s.transportLayerPadding.Enabled { var padding int data, padding = Unpack(data) _ = padding if data == nil { return 0, nil } } s.readBuffer = bytes.NewBuffer(data) n, err = s.readBuffer.Read(b) if err != nil { return 0, err } return n, nil } } func (s *clientConnState) Write(b []byte) (n int, err error) { payloadSize := len(b) if s.firstWrite && s.firstWriteDelay > 0 { firstWriteDelayTimer := time.NewTimer(s.firstWriteDelay) defer firstWriteDelayTimer.Stop() select { case <-s.ctx.Done(): return 0, s.ctx.Err() case <-firstWriteDelayTimer.C: s.firstWrite = false } } if s.transportLayerPadding != nil && s.transportLayerPadding.Enabled { b = Pack(bytes.Clone(b), 0) } err = s.WriteMessage(b) if err != nil { return 0, err } n = payloadSize return n, nil } func (s *clientConnState) Close() error { // TODO: Only recall the connection, not close it. s.done() return nil } func (s *clientConnState) LocalAddr() gonet.Addr { return s.remoteAddr } func (s *clientConnState) RemoteAddr() gonet.Addr { return s.remoteAddr } func (s *clientConnState) SetDeadline(t time.Time) error { return nil } func (s *clientConnState) SetReadDeadline(t time.Time) error { return nil } func (s *clientConnState) SetWriteDeadline(t time.Time) error { return nil } func (s *clientConnState) onC2SMessage(message *tlsmirror.TLSRecord) (drop bool, ok error) { if message.RecordType == mirrorcommon.TLSRecord_RecordType_application_data { if s.decryptor == nil { clientRandom, serverRandom, err := s.mirrorConn.GetHandshakeRandom() if err != nil { newError("failed to get handshake random").Base(err).AtWarning().WriteToLog() return false, nil } { encryptionKey, nonceMask, err := mirrorcrypto.DeriveEncryptionKey(s.primaryKey, clientRandom, serverRandom, ":s2c") if err != nil { newError("failed to derive C2S encryption key").Base(err).AtWarning().WriteToLog() return false, nil } s.decryptor = mirrorcrypto.NewDecryptor(encryptionKey, nonceMask) } { encryptionKey, nonceMask, err := mirrorcrypto.DeriveEncryptionKey(s.primaryKey, clientRandom, serverRandom, ":c2s") if err != nil { newError("failed to derive S2C encryption key").Base(err).AtWarning().WriteToLog() return false, nil } s.encryptor = mirrorcrypto.NewEncryptor(encryptionKey, nonceMask) } s.protocolVersion = message.LegacyProtocolVersion if !s.activated { s.handler(s) s.activated = true } } } return false, ok } func (s *clientConnState) onS2CMessage(message *tlsmirror.TLSRecord) (drop bool, ok error) { if s.sequenceWatermarkEnabled { if s.sequenceWatermarkRx != nil { if (message.RecordType == mirrorcommon.TLSRecord_RecordType_application_data || message.RecordType == mirrorcommon.TLSRecord_RecordType_alert) && len(message.Fragment) >= 16 { watermarkRegion := message.Fragment[len(message.Fragment)-16:] s.sequenceWatermarkRx.XORKeyStream(watermarkRegion, watermarkRegion) } } } if message.RecordType == mirrorcommon.TLSRecord_RecordType_application_data { explicitNonceReservedOverheadHeaderLength, err := s.mirrorConn.GetApplicationDataExplicitNonceReservedOverheadHeaderLength() if err != nil { return false, newError("failed to get explicit nonce reserved overhead header length").Base(err) } if s.encryptor == nil { return false, nil } buffer := make([]byte, 0, len(message.Fragment)-s.encryptor.NonceSize()-explicitNonceReservedOverheadHeaderLength) buffer, err = s.decryptor.Open(buffer, message.Fragment[explicitNonceReservedOverheadHeaderLength:]) if err != nil { return false, nil } if s.sequenceWatermarkEnabled && s.sequenceWatermarkRx == nil { clientRandom, serverRandom, err := s.mirrorConn.GetHandshakeRandom() if err != nil { newError("failed to get handshake random").Base(err).AtError().WriteToLog() return true, nil } key, nonce, err := mirrorcrypto.DeriveSequenceWatermarkingKey(s.primaryKey, clientRandom, serverRandom, ":s2c") if err != nil { newError("failed to derive sequence watermarking key").Base(err).AtError().WriteToLog() return true, nil } s.sequenceWatermarkRx, err = chacha20.NewUnauthenticatedCipher(key, nonce) if err != nil { newError("failed to create sequence watermarking cipher").Base(err).AtError().WriteToLog() return true, nil } } s.readPipe <- buffer return true, nil } return false, ok } func (s *clientConnState) WriteMessage(message []byte) error { explicitNonceReservedOverheadHeaderLength, err := s.mirrorConn.GetApplicationDataExplicitNonceReservedOverheadHeaderLength() if err != nil { return newError("failed to get explicit nonce reserved overhead header length").Base(err) } buffer := make([]byte, explicitNonceReservedOverheadHeaderLength, explicitNonceReservedOverheadHeaderLength+len(message)+s.encryptor.NonceSize()) buffer, err = s.encryptor.Seal(buffer, message) if err != nil { return newError("failed to encrypt message").Base(err) } record := tlsmirror.TLSRecord{ RecordType: mirrorcommon.TLSRecord_RecordType_application_data, LegacyProtocolVersion: s.protocolVersion, RecordLength: uint16(len(buffer)), Fragment: buffer, InsertedMessage: true, } return s.mirrorConn.InsertC2SMessage(&record) } func (s *clientConnState) VerifyConnectionEnrollmentWithProcessor(connectionEnrollmentConfirmationClient tlsmirror.ConnectionEnrollmentConfirmation) error { clientRandom, serverRandom, err := s.mirrorConn.GetHandshakeRandom() if err != nil { return newError("failed to get handshake random").Base(err).AtWarning() } enrollmentServerIdentity, err := mirrorenrollment.DeriveEnrollmentServerIdentifier(s.primaryKey) if err != nil { return newError("failed to derive enrollment server identifier").Base(err).AtError() } enrollmentReq := &tlsmirror.EnrollmentConfirmationReq{ ServerIdentifier: enrollmentServerIdentity, CarrierTlsConnectionClientRandom: clientRandom, CarrierTlsConnectionServerRandom: serverRandom, } resp, err := connectionEnrollmentConfirmationClient.VerifyConnectionEnrollment(enrollmentReq) if err != nil { return newError("failed to verify connection enrollment").Base(err).AtError() } if resp == nil { return newError("nil EnrollmentConfirmationResp") } if resp.Enrolled { newError("connection enrolled successfully").AtInfo().WriteToLog() } else { return newError("connection enrollment failed") } return nil } func (s *clientConnState) onC2SMessageTx(message *tlsmirror.TLSRecord) (drop bool, ok error) { if s.sequenceWatermarkEnabled { if s.sequenceWatermarkTx != nil { if (message.RecordType == mirrorcommon.TLSRecord_RecordType_application_data || message.RecordType == mirrorcommon.TLSRecord_RecordType_alert) && len(message.Fragment) >= 16 { watermarkRegion := message.Fragment[len(message.Fragment)-16:] s.sequenceWatermarkTx.XORKeyStream(watermarkRegion, watermarkRegion) } } if message.InsertedMessage && s.sequenceWatermarkTx == nil { clientRandom, serverRandom, err := s.mirrorConn.GetHandshakeRandom() if err != nil { newError("failed to get handshake random").Base(err).AtError().WriteToLog() return true, nil } key, nonce, err := mirrorcrypto.DeriveSequenceWatermarkingKey(s.primaryKey, clientRandom, serverRandom, ":c2s") if err != nil { newError("failed to derive sequence watermarking key").Base(err).AtError().WriteToLog() return true, nil } s.sequenceWatermarkTx, err = chacha20.NewUnauthenticatedCipher(key, nonce) if err != nil { newError("failed to create sequence watermarking cipher").Base(err).AtError().WriteToLog() return true, nil } } } return false, nil } func (s *clientConnState) onS2CMessageTx(message *tlsmirror.TLSRecord) (drop bool, ok error) { return false, nil } ================================================ FILE: transport/internet/tlsmirror/server/config.pb.go ================================================ package server import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" mirrorenrollment "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment" tlstrafficgen "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/tlstrafficgen" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type TimeSpec struct { state protoimpl.MessageState `protogen:"open.v1"` BaseNanoseconds uint64 `protobuf:"varint,1,opt,name=base_nanoseconds,json=baseNanoseconds,proto3" json:"base_nanoseconds,omitempty"` UniformRandomMultiplierNanoseconds uint64 `protobuf:"varint,2,opt,name=uniform_random_multiplier_nanoseconds,json=uniformRandomMultiplierNanoseconds,proto3" json:"uniform_random_multiplier_nanoseconds,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TimeSpec) Reset() { *x = TimeSpec{} mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TimeSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*TimeSpec) ProtoMessage() {} func (x *TimeSpec) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TimeSpec.ProtoReflect.Descriptor instead. func (*TimeSpec) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_server_config_proto_rawDescGZIP(), []int{0} } func (x *TimeSpec) GetBaseNanoseconds() uint64 { if x != nil { return x.BaseNanoseconds } return 0 } func (x *TimeSpec) GetUniformRandomMultiplierNanoseconds() uint64 { if x != nil { return x.UniformRandomMultiplierNanoseconds } return 0 } type TransportLayerPadding struct { state protoimpl.MessageState `protogen:"open.v1"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TransportLayerPadding) Reset() { *x = TransportLayerPadding{} mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TransportLayerPadding) String() string { return protoimpl.X.MessageStringOf(x) } func (*TransportLayerPadding) ProtoMessage() {} func (x *TransportLayerPadding) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TransportLayerPadding.ProtoReflect.Descriptor instead. func (*TransportLayerPadding) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_server_config_proto_rawDescGZIP(), []int{1} } func (x *TransportLayerPadding) GetEnabled() bool { if x != nil { return x.Enabled } return false } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` ForwardAddress string `protobuf:"bytes,1,opt,name=forward_address,json=forwardAddress,proto3" json:"forward_address,omitempty"` ForwardPort uint32 `protobuf:"varint,2,opt,name=forward_port,json=forwardPort,proto3" json:"forward_port,omitempty"` ForwardTag string `protobuf:"bytes,3,opt,name=forward_tag,json=forwardTag,proto3" json:"forward_tag,omitempty"` CarrierConnectionTag string `protobuf:"bytes,4,opt,name=carrier_connection_tag,json=carrierConnectionTag,proto3" json:"carrier_connection_tag,omitempty"` EmbeddedTrafficGenerator *tlstrafficgen.Config `protobuf:"bytes,5,opt,name=embedded_traffic_generator,json=embeddedTrafficGenerator,proto3" json:"embedded_traffic_generator,omitempty"` PrimaryKey []byte `protobuf:"bytes,6,opt,name=primary_key,json=primaryKey,proto3" json:"primary_key,omitempty"` ExplicitNonceCiphersuites []uint32 `protobuf:"varint,7,rep,packed,name=explicit_nonce_ciphersuites,json=explicitNonceCiphersuites,proto3" json:"explicit_nonce_ciphersuites,omitempty"` DeferInstanceDerivedWriteTime *TimeSpec `protobuf:"bytes,8,opt,name=defer_instance_derived_write_time,json=deferInstanceDerivedWriteTime,proto3" json:"defer_instance_derived_write_time,omitempty"` TransportLayerPadding *TransportLayerPadding `protobuf:"bytes,9,opt,name=transport_layer_padding,json=transportLayerPadding,proto3" json:"transport_layer_padding,omitempty"` ConnectionEnrolment *mirrorenrollment.Config `protobuf:"bytes,10,opt,name=connection_enrolment,json=connectionEnrolment,proto3" json:"connection_enrolment,omitempty"` SequenceWatermarkingEnabled bool `protobuf:"varint,11,opt,name=sequence_watermarking_enabled,json=sequenceWatermarkingEnabled,proto3" json:"sequence_watermarking_enabled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_server_config_proto_rawDescGZIP(), []int{2} } func (x *Config) GetForwardAddress() string { if x != nil { return x.ForwardAddress } return "" } func (x *Config) GetForwardPort() uint32 { if x != nil { return x.ForwardPort } return 0 } func (x *Config) GetForwardTag() string { if x != nil { return x.ForwardTag } return "" } func (x *Config) GetCarrierConnectionTag() string { if x != nil { return x.CarrierConnectionTag } return "" } func (x *Config) GetEmbeddedTrafficGenerator() *tlstrafficgen.Config { if x != nil { return x.EmbeddedTrafficGenerator } return nil } func (x *Config) GetPrimaryKey() []byte { if x != nil { return x.PrimaryKey } return nil } func (x *Config) GetExplicitNonceCiphersuites() []uint32 { if x != nil { return x.ExplicitNonceCiphersuites } return nil } func (x *Config) GetDeferInstanceDerivedWriteTime() *TimeSpec { if x != nil { return x.DeferInstanceDerivedWriteTime } return nil } func (x *Config) GetTransportLayerPadding() *TransportLayerPadding { if x != nil { return x.TransportLayerPadding } return nil } func (x *Config) GetConnectionEnrolment() *mirrorenrollment.Config { if x != nil { return x.ConnectionEnrolment } return nil } func (x *Config) GetSequenceWatermarkingEnabled() bool { if x != nil { return x.SequenceWatermarkingEnabled } return false } var File_transport_internet_tlsmirror_server_config_proto protoreflect.FileDescriptor const file_transport_internet_tlsmirror_server_config_proto_rawDesc = "" + "\n" + "0transport/internet/tlsmirror/server/config.proto\x12.v2ray.core.transport.internet.tlsmirror.server\x1a common/protoext/extensions.proto\x1a7transport/internet/tlsmirror/tlstrafficgen/config.proto\x1a:transport/internet/tlsmirror/mirrorenrollment/config.proto\"\x88\x01\n" + "\bTimeSpec\x12)\n" + "\x10base_nanoseconds\x18\x01 \x01(\x04R\x0fbaseNanoseconds\x12Q\n" + "%uniform_random_multiplier_nanoseconds\x18\x02 \x01(\x04R\"uniformRandomMultiplierNanoseconds\"1\n" + "\x15TransportLayerPadding\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\"\xef\x06\n" + "\x06Config\x12'\n" + "\x0fforward_address\x18\x01 \x01(\tR\x0eforwardAddress\x12!\n" + "\fforward_port\x18\x02 \x01(\rR\vforwardPort\x12\x1f\n" + "\vforward_tag\x18\x03 \x01(\tR\n" + "forwardTag\x124\n" + "\x16carrier_connection_tag\x18\x04 \x01(\tR\x14carrierConnectionTag\x12{\n" + "\x1aembedded_traffic_generator\x18\x05 \x01(\v2=.v2ray.core.transport.internet.tlsmirror.tlstrafficgen.ConfigR\x18embeddedTrafficGenerator\x12\x1f\n" + "\vprimary_key\x18\x06 \x01(\fR\n" + "primaryKey\x12>\n" + "\x1bexplicit_nonce_ciphersuites\x18\a \x03(\rR\x19explicitNonceCiphersuites\x12\x82\x01\n" + "!defer_instance_derived_write_time\x18\b \x01(\v28.v2ray.core.transport.internet.tlsmirror.server.TimeSpecR\x1ddeferInstanceDerivedWriteTime\x12}\n" + "\x17transport_layer_padding\x18\t \x01(\v2E.v2ray.core.transport.internet.tlsmirror.server.TransportLayerPaddingR\x15transportLayerPadding\x12s\n" + "\x14connection_enrolment\x18\n" + " \x01(\v2@.v2ray.core.transport.internet.tlsmirror.mirrorenrollment.ConfigR\x13connectionEnrolment\x12B\n" + "\x1dsequence_watermarking_enabled\x18\v \x01(\bR\x1bsequenceWatermarkingEnabled:'\x82\xb5\x18#\n" + "\ttransport\x12\ttlsmirror\x8a\xff)\ttlsmirrorB\xab\x01\n" + "2com.v2ray.core.transport.internet.tlsmirror.serverP\x01ZBgithub.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/server\xaa\x02.V2Ray.Core.Transport.Internet.Tlsmirror.Serverb\x06proto3" var ( file_transport_internet_tlsmirror_server_config_proto_rawDescOnce sync.Once file_transport_internet_tlsmirror_server_config_proto_rawDescData []byte ) func file_transport_internet_tlsmirror_server_config_proto_rawDescGZIP() []byte { file_transport_internet_tlsmirror_server_config_proto_rawDescOnce.Do(func() { file_transport_internet_tlsmirror_server_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_server_config_proto_rawDesc), len(file_transport_internet_tlsmirror_server_config_proto_rawDesc))) }) return file_transport_internet_tlsmirror_server_config_proto_rawDescData } var file_transport_internet_tlsmirror_server_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_transport_internet_tlsmirror_server_config_proto_goTypes = []any{ (*TimeSpec)(nil), // 0: v2ray.core.transport.internet.tlsmirror.server.TimeSpec (*TransportLayerPadding)(nil), // 1: v2ray.core.transport.internet.tlsmirror.server.TransportLayerPadding (*Config)(nil), // 2: v2ray.core.transport.internet.tlsmirror.server.Config (*tlstrafficgen.Config)(nil), // 3: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config (*mirrorenrollment.Config)(nil), // 4: v2ray.core.transport.internet.tlsmirror.mirrorenrollment.Config } var file_transport_internet_tlsmirror_server_config_proto_depIdxs = []int32{ 3, // 0: v2ray.core.transport.internet.tlsmirror.server.Config.embedded_traffic_generator:type_name -> v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config 0, // 1: v2ray.core.transport.internet.tlsmirror.server.Config.defer_instance_derived_write_time:type_name -> v2ray.core.transport.internet.tlsmirror.server.TimeSpec 1, // 2: v2ray.core.transport.internet.tlsmirror.server.Config.transport_layer_padding:type_name -> v2ray.core.transport.internet.tlsmirror.server.TransportLayerPadding 4, // 3: v2ray.core.transport.internet.tlsmirror.server.Config.connection_enrolment:type_name -> v2ray.core.transport.internet.tlsmirror.mirrorenrollment.Config 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_transport_internet_tlsmirror_server_config_proto_init() } func file_transport_internet_tlsmirror_server_config_proto_init() { if File_transport_internet_tlsmirror_server_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_server_config_proto_rawDesc), len(file_transport_internet_tlsmirror_server_config_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_tlsmirror_server_config_proto_goTypes, DependencyIndexes: file_transport_internet_tlsmirror_server_config_proto_depIdxs, MessageInfos: file_transport_internet_tlsmirror_server_config_proto_msgTypes, }.Build() File_transport_internet_tlsmirror_server_config_proto = out.File file_transport_internet_tlsmirror_server_config_proto_goTypes = nil file_transport_internet_tlsmirror_server_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/tlsmirror/server/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.tlsmirror.server; option csharp_namespace = "V2Ray.Core.Transport.Internet.Tlsmirror.Server"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/server"; option java_package = "com.v2ray.core.transport.internet.tlsmirror.server"; option java_multiple_files = true; import "common/protoext/extensions.proto"; import "transport/internet/tlsmirror/tlstrafficgen/config.proto"; import "transport/internet/tlsmirror/mirrorenrollment/config.proto"; message TimeSpec { uint64 base_nanoseconds = 1; uint64 uniform_random_multiplier_nanoseconds = 2; } message TransportLayerPadding { bool enabled = 1; } message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "tlsmirror"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = false; option (v2ray.core.common.protoext.message_opt).transport_original_name = "tlsmirror"; string forward_address = 1; uint32 forward_port = 2; string forward_tag = 3; string carrier_connection_tag = 4; v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config embedded_traffic_generator = 5; bytes primary_key = 6; repeated uint32 explicit_nonce_ciphersuites = 7; TimeSpec defer_instance_derived_write_time = 8; TransportLayerPadding transport_layer_padding = 9; v2ray.core.transport.internet.tlsmirror.mirrorenrollment.Config connection_enrolment = 10; bool sequence_watermarking_enabled = 11; } ================================================ FILE: transport/internet/tlsmirror/server/conn.go ================================================ package server import ( "bytes" "context" "crypto/cipher" "net" "time" "golang.org/x/crypto/chacha20" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcommon" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcrypto" ) type connState struct { ctx context.Context done context.CancelFunc handler internet.ConnHandler mirrorConn tlsmirror.InsertableTLSConn localAddr net.Addr remoteAddr net.Addr activated bool decryptor *mirrorcrypto.Decryptor encryptor *mirrorcrypto.Encryptor primaryKey []byte readPipe chan []byte readBuffer *bytes.Buffer protocolVersion [2]byte firstWrite bool firstWriteDelay time.Duration transportLayerPadding *TransportLayerPadding connectionEnrollmentEnabled bool connectionEnrollmentProcessorEnrolled bool connectionEnrollmentProcessor tlsmirror.ConnectionEnrollmentConfirmationProcessor connectionEnrollmentRemover tlsmirror.RemoveConnectionFunc sequenceWatermarkEnabled bool sequenceWatermarkTx, sequenceWatermarkRx cipher.Stream } func (s *connState) VerifyConnectionEnrollment(req *tlsmirror.EnrollmentConfirmationReq) (*tlsmirror.EnrollmentConfirmationResp, error) { return &tlsmirror.EnrollmentConfirmationResp{Enrolled: true}, nil } func (s *connState) Read(b []byte) (n int, err error) { if s.readBuffer != nil { n, _ = s.readBuffer.Read(b) if n > 0 { return n, nil } s.readBuffer = nil } select { case <-s.ctx.Done(): return 0, s.ctx.Err() case data := <-s.readPipe: if s.transportLayerPadding != nil && s.transportLayerPadding.Enabled { var padding int data, padding = Unpack(data) _ = padding if data == nil { return 0, nil } } s.readBuffer = bytes.NewBuffer(data) n, err = s.readBuffer.Read(b) if err != nil { return 0, err } return n, nil } } func (s *connState) Write(b []byte) (n int, err error) { payloadSize := len(b) if s.firstWrite && s.firstWriteDelay > 0 { firstWriteDelayTimer := time.NewTimer(s.firstWriteDelay) defer firstWriteDelayTimer.Stop() select { case <-s.ctx.Done(): return 0, s.ctx.Err() case <-firstWriteDelayTimer.C: s.firstWrite = false } } if s.transportLayerPadding != nil && s.transportLayerPadding.Enabled { b = Pack(bytes.Clone(b), 0) } err = s.WriteMessage(b) if err != nil { return 0, err } n = payloadSize return n, nil } func (s *connState) LocalAddr() net.Addr { return s.localAddr } func (s *connState) RemoteAddr() net.Addr { return s.remoteAddr } func (s *connState) SetDeadline(t time.Time) error { return nil } func (s *connState) SetReadDeadline(t time.Time) error { return nil } func (s *connState) SetWriteDeadline(t time.Time) error { return nil } func (s *connState) Close() error { s.done() if s.connectionEnrollmentRemover != nil { if err := s.connectionEnrollmentRemover(); err != nil { newError("failed to remove connection enrollment").Base(err).AtWarning().WriteToLog() } s.connectionEnrollmentRemover = nil } return nil } func (s *connState) onS2CMessage(message *tlsmirror.TLSRecord) (drop bool, ok error) { if message.RecordType == mirrorcommon.TLSRecord_RecordType_handshake { if s.connectionEnrollmentEnabled && !s.connectionEnrollmentProcessorEnrolled { clientRandom, serverRandom, err := s.mirrorConn.GetHandshakeRandom() if err != nil { newError("failed to get handshake random").Base(err).AtWarning().WriteToLog() return false, nil } remove, err := s.connectionEnrollmentProcessor.AddConnection(s.ctx, clientRandom, serverRandom, s) if err != nil { newError("failed to add connection for enrollment").Base(err).AtWarning().WriteToLog() return false, nil } s.connectionEnrollmentRemover = remove s.connectionEnrollmentProcessorEnrolled = true return false, nil } } return false, nil } func (s *connState) onC2SMessage(message *tlsmirror.TLSRecord) (drop bool, ok error) { if s.sequenceWatermarkEnabled { if s.sequenceWatermarkRx != nil { if (message.RecordType == mirrorcommon.TLSRecord_RecordType_application_data || message.RecordType == mirrorcommon.TLSRecord_RecordType_alert) && len(message.Fragment) >= 16 { watermarkRegion := message.Fragment[len(message.Fragment)-16:] s.sequenceWatermarkRx.XORKeyStream(watermarkRegion, watermarkRegion) } } } if message.RecordType == mirrorcommon.TLSRecord_RecordType_application_data { if s.decryptor == nil { clientRandom, serverRandom, err := s.mirrorConn.GetHandshakeRandom() if err != nil { newError("failed to get handshake random").Base(err).AtWarning().WriteToLog() return false, nil } { encryptionKey, nonceMask, err := mirrorcrypto.DeriveEncryptionKey(s.primaryKey, clientRandom, serverRandom, ":c2s") if err != nil { newError("failed to derive C2S encryption key").Base(err).AtWarning().WriteToLog() return false, nil } s.decryptor = mirrorcrypto.NewDecryptor(encryptionKey, nonceMask) } { encryptionKey, nonceMask, err := mirrorcrypto.DeriveEncryptionKey(s.primaryKey, clientRandom, serverRandom, ":s2c") if err != nil { newError("failed to derive S2C encryption key").Base(err).AtWarning().WriteToLog() return false, nil } s.encryptor = mirrorcrypto.NewEncryptor(encryptionKey, nonceMask) } s.protocolVersion = message.LegacyProtocolVersion } explicitNonceReservedOverheadHeaderLength, err := s.mirrorConn.GetApplicationDataExplicitNonceReservedOverheadHeaderLength() if err != nil { return false, newError("failed to get explicit nonce reserved overhead header length").Base(err) } buffer := make([]byte, 0, len(message.Fragment)-s.decryptor.NonceSize()-explicitNonceReservedOverheadHeaderLength) buffer, err = s.decryptor.Open(buffer, message.Fragment[explicitNonceReservedOverheadHeaderLength:]) if err != nil { return false, nil } if s.sequenceWatermarkEnabled && s.sequenceWatermarkRx == nil { clientRandom, serverRandom, err := s.mirrorConn.GetHandshakeRandom() if err != nil { newError("failed to get handshake random").Base(err).AtError().WriteToLog() return true, nil } key, nonce, err := mirrorcrypto.DeriveSequenceWatermarkingKey(s.primaryKey, clientRandom, serverRandom, ":c2s") if err != nil { newError("failed to derive sequence watermarking key").Base(err).AtError().WriteToLog() return true, nil } s.sequenceWatermarkRx, err = chacha20.NewUnauthenticatedCipher(key, nonce) if err != nil { newError("failed to create sequence watermarking cipher").Base(err).AtError().WriteToLog() return true, nil } } if !s.activated { s.handler(s) s.activated = true } s.readPipe <- buffer return true, nil } return false, ok } func (s *connState) WriteMessage(message []byte) error { explicitNonceReservedOverheadHeaderLength, err := s.mirrorConn.GetApplicationDataExplicitNonceReservedOverheadHeaderLength() if err != nil { return newError("failed to get explicit nonce reserved overhead header length").Base(err) } buffer := make([]byte, explicitNonceReservedOverheadHeaderLength, explicitNonceReservedOverheadHeaderLength+len(message)+s.decryptor.NonceSize()) buffer, err = s.encryptor.Seal(buffer, message) if err != nil { return newError("failed to encrypt message").Base(err) } record := tlsmirror.TLSRecord{ RecordType: mirrorcommon.TLSRecord_RecordType_application_data, LegacyProtocolVersion: s.protocolVersion, RecordLength: uint16(len(buffer)), Fragment: buffer, InsertedMessage: true, } return s.mirrorConn.InsertS2CMessage(&record) } func (s *connState) onC2SMessageTx(message *tlsmirror.TLSRecord) (drop bool, ok error) { return false, nil } func (s *connState) onS2CMessageTx(message *tlsmirror.TLSRecord) (drop bool, ok error) { if s.sequenceWatermarkEnabled { if s.sequenceWatermarkTx != nil { if (message.RecordType == mirrorcommon.TLSRecord_RecordType_application_data || message.RecordType == mirrorcommon.TLSRecord_RecordType_alert) && len(message.Fragment) >= 16 { watermarkRegion := message.Fragment[len(message.Fragment)-16:] s.sequenceWatermarkTx.XORKeyStream(watermarkRegion, watermarkRegion) } } if message.InsertedMessage && s.sequenceWatermarkTx == nil { clientRandom, serverRandom, err := s.mirrorConn.GetHandshakeRandom() if err != nil { newError("failed to get handshake random").Base(err).AtError().WriteToLog() return true, nil } key, nonce, err := mirrorcrypto.DeriveSequenceWatermarkingKey(s.primaryKey, clientRandom, serverRandom, ":s2c") if err != nil { newError("failed to derive sequence watermarking key").Base(err).AtError().WriteToLog() return true, nil } s.sequenceWatermarkTx, err = chacha20.NewUnauthenticatedCipher(key, nonce) if err != nil { newError("failed to create sequence watermarking cipher").Base(err).AtError().WriteToLog() return true, nil } } } return false, nil } ================================================ FILE: transport/internet/tlsmirror/server/errors.generated.go ================================================ package server import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tlsmirror/server/hub.go ================================================ package server import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/transportcommon" ) func ListenTLSMirror(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler, ) (internet.Listener, error) { tlsMirrorSettings := streamSettings.ProtocolSettings.(*Config) listener, err := transportcommon.ListenWithSecuritySettings(ctx, address, port, streamSettings) if err != nil { return nil, newError("failed to listen TLS mirror").Base(err) } tlsMirrorServer, err := NewServer(ctx, listener, tlsMirrorSettings, handler) if err != nil { return nil, newError("failed to create TLS mirror server").Base(err) } go tlsMirrorServer.accepts() return tlsMirrorServer, nil } const protocolName = "tlsmirror" func init() { common.Must(internet.RegisterTransportListener(protocolName, ListenTLSMirror)) } func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/tlsmirror/server/outbound.go ================================================ package server import ( "context" "sync" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/transport" ) func NewOutboundListener() *OutboundListener { return &OutboundListener{ buffer: make(chan *connWithContext, 4), done: done.New(), } } type connWithContext struct { net.Conn ctx context.Context } func (c *connWithContext) GetConnectionContext() context.Context { return c.ctx } // OutboundListener is a net.Listener for listening gRPC connections. type OutboundListener struct { buffer chan *connWithContext done *done.Instance } func (l *OutboundListener) addWithContext(ctx context.Context, conn net.Conn) { select { case l.buffer <- &connWithContext{Conn: conn, ctx: ctx}: case <-l.done.Wait(): conn.Close() default: conn.Close() } } // Accept implements net.Listener. func (l *OutboundListener) Accept() (net.Conn, error) { select { case <-l.done.Wait(): return nil, newError("listen closed") case c := <-l.buffer: return c, nil } } // Close implement net.Listener. func (l *OutboundListener) Close() error { common.Must(l.done.Close()) L: for { select { case c := <-l.buffer: c.Close() default: break L } } return nil } // Addr implements net.Listener. func (l *OutboundListener) Addr() net.Addr { return &net.TCPAddr{ IP: net.IP{0, 0, 0, 0}, Port: 0, } } func NewOutbound(tag string, listener *OutboundListener) *Outbound { return &Outbound{ tag: tag, listener: listener, } } // Outbound is a outbound.Handler that handles gRPC connections. type Outbound struct { tag string listener *OutboundListener access sync.RWMutex closed bool } // Dispatch implements outbound.Handler. func (co *Outbound) Dispatch(ctx context.Context, link *transport.Link) { co.access.RLock() if co.closed { common.Interrupt(link.Reader) common.Interrupt(link.Writer) co.access.RUnlock() return } closeSignal := done.New() c := net.NewConnection(net.ConnectionInputMulti(link.Writer), net.ConnectionOutputMulti(link.Reader), net.ConnectionOnClose(closeSignal)) co.listener.addWithContext(ctx, c) co.access.RUnlock() <-closeSignal.Wait() } // Tag implements outbound.Handler. func (co *Outbound) Tag() string { return co.tag } // Start implements common.Runnable. func (co *Outbound) Start() error { co.access.Lock() co.closed = false co.access.Unlock() return nil } // Close implements common.Closable. func (co *Outbound) Close() error { co.access.Lock() defer co.access.Unlock() co.closed = true return co.listener.Close() } ================================================ FILE: transport/internet/tlsmirror/server/padding.go ================================================ package server // this file defines methods to pad packets // with a given number of bytes, and to unpack the padding from a padded packet. // The packet format is as follows if the desired output length is greater than // 4 bytes: // | data | padding | data length | // The data length is a 32-bit big-endian integer that represents the length of // the data in bytes. // If the desired output length is 4 bytes or less, the packet format is as // follows: // | padding | // No payload will be included in the packet. import "encoding/binary" // Pack pads the given data with the given number of bytes, and appends the // length of the data to the end of the data. The returned byte slice // contains the padded data. // This generates a packet with a length of // len(data_OWNERSHIP_RELINQUISHED) + padding + 2 // @param data_OWNERSHIP_RELINQUISHED - The payload, this reference is consumed and should not be used after this call. // @param padding - The number of padding bytes to add to the data. func Pack(data_OWNERSHIP_RELINQUISHED []byte, paddingLength int) []byte { data := append(data_OWNERSHIP_RELINQUISHED, make([]byte, paddingLength)...) //nolint:gocritic dataLength := len(data_OWNERSHIP_RELINQUISHED) data = binary.BigEndian.AppendUint32(data, uint32(dataLength)) return data } // Pad returns a padding packet of padding length. // If the padding length is less than 0, nil is returned. // @param padding - The number of padding bytes to add to the data. func Pad(paddingLength int) []byte { if assertPaddingLengthIsNotNegative := paddingLength < 0; assertPaddingLengthIsNotNegative { return nil } switch paddingLength { case 0: return []byte{} case 1: return []byte{0} case 2: return []byte{0, 0} case 3: return []byte{0, 0, 0} case 4: return []byte{0, 0, 0, 0} default: return append(make([]byte, paddingLength)) //nolint:gocritic, govet, staticcheck } } // Unpack extracts the data and padding from the given padded data. It // returns the data and the number of padding bytes. // the data may be nil. // @param wrappedData_OWNERSHIP_RELINQUISHED - The packet, this reference is consumed and should not be used after this call. func Unpack(wrappedData_OWNERSHIP_RELINQUISHED []byte) ([]byte, int) { dataLength := len(wrappedData_OWNERSHIP_RELINQUISHED) if dataLength < 4 { return nil, dataLength } dataLen := int(binary.BigEndian.Uint32(wrappedData_OWNERSHIP_RELINQUISHED[dataLength-4:])) if dataLen > len(wrappedData_OWNERSHIP_RELINQUISHED)-4 { return nil, 0 } paddingLength := dataLength - dataLen - 4 if paddingLength < 0 { return nil, paddingLength } return wrappedData_OWNERSHIP_RELINQUISHED[:dataLen], paddingLength } ================================================ FILE: transport/internet/tlsmirror/server/server.go ================================================ package server import ( cryptoRand "crypto/rand" "math/big" "strings" "time" "golang.org/x/net/context" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorbase" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type Server struct { config *Config listener net.Listener handler internet.ConnHandler ctx context.Context explicitNonceCiphersuiteLookup *ciphersuiteLookuper enrollmentConfirmationListener *OutboundListener enrollmentConfirmationOutbound *Outbound obm outbound.Manager enrollmentConfirmationServer *mirrorenrollment.EnrollmentConfirmationServer enrollmentConfirmationProcessor tlsmirror.ConnectionEnrollmentConfirmationProcessor } func (s *Server) process(conn net.Conn) { transportEnvironment := envctx.EnvironmentFromContext(s.ctx).(environment.TransportEnvironment) dialer := transportEnvironment.OutboundDialer() port, err := net.PortFromInt(s.config.ForwardPort) if err != nil { newError("failed to parse port").Base(err).AtWarning().WriteToLog() return } address := net.ParseAddress(s.config.ForwardAddress) dest := net.TCPDestination(address, port) forwardConn, err := dialer(s.ctx, dest, s.config.ForwardTag) if err != nil { newError("failed to dial to destination").Base(err).AtWarning().WriteToLog() return } s.accept(conn, forwardConn) } func (s *Server) accepts() { for { conn, err := s.listener.Accept() if err != nil { errStr := err.Error() if strings.Contains(errStr, "closed") { break } newError("failed to accepted raw connections").Base(err).AtWarning().WriteToLog() if strings.Contains(errStr, "too many") { time.Sleep(time.Millisecond * 500) } continue } go s.process(conn) } } func (s *Server) Close() error { if s.enrollmentConfirmationListener != nil { if err := s.enrollmentConfirmationListener.Close(); err != nil { newError("failed to close enrollment confirmation listener").Base(err).AtWarning().WriteToLog() } } return s.listener.Close() } func (s *Server) Addr() net.Addr { return s.listener.Addr() } func (s *Server) accept(clientConn net.Conn, serverConn net.Conn) { ctx, cancel := context.WithCancel(s.ctx) firstWriteDelay := time.Duration(0) if s.config.DeferInstanceDerivedWriteTime != nil { firstWriteDelay = time.Duration(s.config.DeferInstanceDerivedWriteTime.BaseNanoseconds) if s.config.DeferInstanceDerivedWriteTime.UniformRandomMultiplierNanoseconds > 0 { uniformRandomAdd := big.NewInt(int64(s.config.DeferInstanceDerivedWriteTime.UniformRandomMultiplierNanoseconds)) uniformRandomAddBigInt, err := cryptoRand.Int(cryptoRand.Reader, uniformRandomAdd) if err != nil { newError("failed to generate random delay").Base(err).AtWarning().WriteToLog() return } uniformRandomAddU64 := uint64(uniformRandomAddBigInt.Int64()) firstWriteDelay += time.Duration(uniformRandomAddU64) } } conn := &connState{ ctx: ctx, done: cancel, localAddr: clientConn.LocalAddr(), remoteAddr: clientConn.RemoteAddr(), primaryKey: s.config.PrimaryKey, handler: s.onIncomingReadyConnection, readPipe: make(chan []byte, 1), firstWrite: true, firstWriteDelay: firstWriteDelay, transportLayerPadding: s.config.TransportLayerPadding, sequenceWatermarkEnabled: s.config.SequenceWatermarkingEnabled, } if s.config.ConnectionEnrolment != nil { conn.connectionEnrollmentEnabled = true conn.connectionEnrollmentProcessor = s.enrollmentConfirmationProcessor } conn.mirrorConn = mirrorbase.NewMirroredTLSConn(ctx, clientConn, serverConn, conn.onC2SMessage, conn.onS2CMessage, conn, s.explicitNonceCiphersuiteLookup.Lookup, conn.onC2SMessageTx, conn.onS2CMessageTx) } func (s *Server) onIncomingReadyConnection(conn internet.Connection) { go s.handler(conn) } func (s *Server) init() error { if err := core.RequireFeatures(s.ctx, func(om outbound.Manager) { s.obm = om }); err != nil { return err } if s.config.ConnectionEnrolment != nil { s.enrollmentConfirmationListener = NewOutboundListener() s.enrollmentConfirmationOutbound = NewOutbound(s.config.ConnectionEnrolment.PrimaryIngressOutbound, s.enrollmentConfirmationListener) if err := s.enrollmentConfirmationOutbound.Start(); err != nil { return newError("failed to start enrollment confirmation outbound").Base(err).AtWarning() } if err := s.obm.RemoveHandler(context.Background(), s.config.ConnectionEnrolment.PrimaryIngressOutbound); err != nil { newError("failed to remove existing handler").Base(err).AtDebug().WriteToLog() } err := s.obm.AddHandler(context.Background(), s.enrollmentConfirmationOutbound) if err != nil { return newError("failed to add outbound handler").Base(err) } s.enrollmentConfirmationProcessor, err = mirrorenrollment.NewServerEnrollmentProcessor(s.config.PrimaryKey) if err != nil { return newError("failed to create enrollment confirmation processor").Base(err).AtError() } s.enrollmentConfirmationServer, err = mirrorenrollment.NewEnrollmentConfirmationServer(s.ctx, s.config.ConnectionEnrolment, s.enrollmentConfirmationProcessor) if err != nil { return newError("failed to create enrollment confirmation server").Base(err).AtError() } go func() { for { conn, err := s.enrollmentConfirmationListener.Accept() if err != nil { newError("failed to accept enrollment confirmation connection").Base(err).AtWarning().WriteToLog() continue } go func() { if err := s.enrollmentConfirmationServer.HandlePrimaryIngressConnection(s.ctx, conn); err != nil { newError("failed to handle primary ingress connection for enrollment confirmation").Base(err).AtWarning().WriteToLog() } }() } }() } return nil } func NewServer(ctx context.Context, listener net.Listener, config *Config, handler internet.ConnHandler) (*Server, error) { var explicitNonceCiphersuiteLookup *ciphersuiteLookuper if len(config.ExplicitNonceCiphersuites) > 0 { var err error explicitNonceCiphersuiteLookup, err = newCipherSuiteLookuperFromUint32Array(config.ExplicitNonceCiphersuites) if err != nil { newError("failed to create explicit nonce ciphersuite lookuper").Base(err).AtWarning().WriteToLog() } } else { explicitNonceCiphersuiteLookup = newEmptyCipherSuiteLookuper() newError("no explicit nonce ciphersuites configured, all ciphersuites will be treated as non-explicit nonce").AtWarning().WriteToLog() } s := &Server{ ctx: ctx, listener: listener, config: config, handler: handler, explicitNonceCiphersuiteLookup: explicitNonceCiphersuiteLookup, } if err := s.init(); err != nil { return nil, newError("failed to initialize TLS mirror server").Base(err).AtError() } return s, nil } ================================================ FILE: transport/internet/tlsmirror/tlstrafficgen/config.pb.go ================================================ package tlstrafficgen import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type TimeSpec struct { state protoimpl.MessageState `protogen:"open.v1"` BaseNanoseconds uint64 `protobuf:"varint,1,opt,name=base_nanoseconds,json=baseNanoseconds,proto3" json:"base_nanoseconds,omitempty"` UniformRandomMultiplierNanoseconds uint64 `protobuf:"varint,2,opt,name=uniform_random_multiplier_nanoseconds,json=uniformRandomMultiplierNanoseconds,proto3" json:"uniform_random_multiplier_nanoseconds,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TimeSpec) Reset() { *x = TimeSpec{} mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TimeSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*TimeSpec) ProtoMessage() {} func (x *TimeSpec) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TimeSpec.ProtoReflect.Descriptor instead. func (*TimeSpec) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescGZIP(), []int{0} } func (x *TimeSpec) GetBaseNanoseconds() uint64 { if x != nil { return x.BaseNanoseconds } return 0 } func (x *TimeSpec) GetUniformRandomMultiplierNanoseconds() uint64 { if x != nil { return x.UniformRandomMultiplierNanoseconds } return 0 } type Header struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` Values []string `protobuf:"bytes,3,rep,name=values,proto3" json:"values,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Header) Reset() { *x = Header{} mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Header) String() string { return protoimpl.X.MessageStringOf(x) } func (*Header) ProtoMessage() {} func (x *Header) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Header.ProtoReflect.Descriptor instead. func (*Header) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescGZIP(), []int{1} } func (x *Header) GetName() string { if x != nil { return x.Name } return "" } func (x *Header) GetValue() string { if x != nil { return x.Value } return "" } func (x *Header) GetValues() []string { if x != nil { return x.Values } return nil } type TransferCandidate struct { state protoimpl.MessageState `protogen:"open.v1"` Weight int32 `protobuf:"varint,1,opt,name=weight,proto3" json:"weight,omitempty"` GotoLocation int64 `protobuf:"varint,2,opt,name=goto_location,json=gotoLocation,proto3" json:"goto_location,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TransferCandidate) Reset() { *x = TransferCandidate{} mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TransferCandidate) String() string { return protoimpl.X.MessageStringOf(x) } func (*TransferCandidate) ProtoMessage() {} func (x *TransferCandidate) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TransferCandidate.ProtoReflect.Descriptor instead. func (*TransferCandidate) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescGZIP(), []int{2} } func (x *TransferCandidate) GetWeight() int32 { if x != nil { return x.Weight } return 0 } func (x *TransferCandidate) GetGotoLocation() int64 { if x != nil { return x.GotoLocation } return 0 } type Step struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Host string `protobuf:"bytes,8,opt,name=host,proto3" json:"host,omitempty"` Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` NextStep []*TransferCandidate `protobuf:"bytes,6,rep,name=next_step,json=nextStep,proto3" json:"next_step,omitempty"` ConnectionReady bool `protobuf:"varint,7,opt,name=connection_ready,json=connectionReady,proto3" json:"connection_ready,omitempty"` Headers []*Header `protobuf:"bytes,9,rep,name=headers,proto3" json:"headers,omitempty"` ConnectionRecallExit bool `protobuf:"varint,10,opt,name=connection_recall_exit,json=connectionRecallExit,proto3" json:"connection_recall_exit,omitempty"` WaitTime *TimeSpec `protobuf:"bytes,11,opt,name=wait_time,json=waitTime,proto3" json:"wait_time,omitempty"` H2DoNotWaitForDownloadFinish bool `protobuf:"varint,12,opt,name=h2_do_not_wait_for_download_finish,json=h2DoNotWaitForDownloadFinish,proto3" json:"h2_do_not_wait_for_download_finish,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Step) Reset() { *x = Step{} mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Step) String() string { return protoimpl.X.MessageStringOf(x) } func (*Step) ProtoMessage() {} func (x *Step) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Step.ProtoReflect.Descriptor instead. func (*Step) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescGZIP(), []int{3} } func (x *Step) GetName() string { if x != nil { return x.Name } return "" } func (x *Step) GetHost() string { if x != nil { return x.Host } return "" } func (x *Step) GetPath() string { if x != nil { return x.Path } return "" } func (x *Step) GetMethod() string { if x != nil { return x.Method } return "" } func (x *Step) GetNextStep() []*TransferCandidate { if x != nil { return x.NextStep } return nil } func (x *Step) GetConnectionReady() bool { if x != nil { return x.ConnectionReady } return false } func (x *Step) GetHeaders() []*Header { if x != nil { return x.Headers } return nil } func (x *Step) GetConnectionRecallExit() bool { if x != nil { return x.ConnectionRecallExit } return false } func (x *Step) GetWaitTime() *TimeSpec { if x != nil { return x.WaitTime } return nil } func (x *Step) GetH2DoNotWaitForDownloadFinish() bool { if x != nil { return x.H2DoNotWaitForDownloadFinish } return false } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Steps []*Step `protobuf:"bytes,1,rep,name=steps,proto3" json:"steps,omitempty"` SecuritySettings *anypb.Any `protobuf:"bytes,2,opt,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescGZIP(), []int{4} } func (x *Config) GetSteps() []*Step { if x != nil { return x.Steps } return nil } func (x *Config) GetSecuritySettings() *anypb.Any { if x != nil { return x.SecuritySettings } return nil } var File_transport_internet_tlsmirror_tlstrafficgen_config_proto protoreflect.FileDescriptor const file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDesc = "" + "\n" + "7transport/internet/tlsmirror/tlstrafficgen/config.proto\x125v2ray.core.transport.internet.tlsmirror.tlstrafficgen\x1a\x19google/protobuf/any.proto\"\x88\x01\n" + "\bTimeSpec\x12)\n" + "\x10base_nanoseconds\x18\x01 \x01(\x04R\x0fbaseNanoseconds\x12Q\n" + "%uniform_random_multiplier_nanoseconds\x18\x02 \x01(\x04R\"uniformRandomMultiplierNanoseconds\"J\n" + "\x06Header\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value\x12\x16\n" + "\x06values\x18\x03 \x03(\tR\x06values\"P\n" + "\x11TransferCandidate\x12\x16\n" + "\x06weight\x18\x01 \x01(\x05R\x06weight\x12#\n" + "\rgoto_location\x18\x02 \x01(\x03R\fgotoLocation\"\xa3\x04\n" + "\x04Step\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + "\x04host\x18\b \x01(\tR\x04host\x12\x12\n" + "\x04path\x18\x02 \x01(\tR\x04path\x12\x16\n" + "\x06method\x18\x03 \x01(\tR\x06method\x12e\n" + "\tnext_step\x18\x06 \x03(\v2H.v2ray.core.transport.internet.tlsmirror.tlstrafficgen.TransferCandidateR\bnextStep\x12)\n" + "\x10connection_ready\x18\a \x01(\bR\x0fconnectionReady\x12W\n" + "\aheaders\x18\t \x03(\v2=.v2ray.core.transport.internet.tlsmirror.tlstrafficgen.HeaderR\aheaders\x124\n" + "\x16connection_recall_exit\x18\n" + " \x01(\bR\x14connectionRecallExit\x12\\\n" + "\twait_time\x18\v \x01(\v2?.v2ray.core.transport.internet.tlsmirror.tlstrafficgen.TimeSpecR\bwaitTime\x12H\n" + "\"h2_do_not_wait_for_download_finish\x18\f \x01(\bR\x1ch2DoNotWaitForDownloadFinish\"\x9e\x01\n" + "\x06Config\x12Q\n" + "\x05steps\x18\x01 \x03(\v2;.v2ray.core.transport.internet.tlsmirror.tlstrafficgen.StepR\x05steps\x12A\n" + "\x11security_settings\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x10securitySettingsB\xc0\x01\n" + "9com.v2ray.core.transport.internet.tlsmirror.tlstrafficgenP\x01ZIgithub.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/tlstrafficgen\xaa\x025V2Ray.Core.Transport.Internet.Tlsmirror.Tlstrafficgenb\x06proto3" var ( file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescOnce sync.Once file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescData []byte ) func file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescGZIP() []byte { file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescOnce.Do(func() { file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDesc), len(file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDesc))) }) return file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDescData } var file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_transport_internet_tlsmirror_tlstrafficgen_config_proto_goTypes = []any{ (*TimeSpec)(nil), // 0: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.TimeSpec (*Header)(nil), // 1: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Header (*TransferCandidate)(nil), // 2: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.TransferCandidate (*Step)(nil), // 3: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Step (*Config)(nil), // 4: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config (*anypb.Any)(nil), // 5: google.protobuf.Any } var file_transport_internet_tlsmirror_tlstrafficgen_config_proto_depIdxs = []int32{ 2, // 0: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Step.next_step:type_name -> v2ray.core.transport.internet.tlsmirror.tlstrafficgen.TransferCandidate 1, // 1: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Step.headers:type_name -> v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Header 0, // 2: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Step.wait_time:type_name -> v2ray.core.transport.internet.tlsmirror.tlstrafficgen.TimeSpec 3, // 3: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config.steps:type_name -> v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Step 5, // 4: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config.security_settings:type_name -> google.protobuf.Any 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_transport_internet_tlsmirror_tlstrafficgen_config_proto_init() } func file_transport_internet_tlsmirror_tlstrafficgen_config_proto_init() { if File_transport_internet_tlsmirror_tlstrafficgen_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDesc), len(file_transport_internet_tlsmirror_tlstrafficgen_config_proto_rawDesc)), NumEnums: 0, NumMessages: 5, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_tlsmirror_tlstrafficgen_config_proto_goTypes, DependencyIndexes: file_transport_internet_tlsmirror_tlstrafficgen_config_proto_depIdxs, MessageInfos: file_transport_internet_tlsmirror_tlstrafficgen_config_proto_msgTypes, }.Build() File_transport_internet_tlsmirror_tlstrafficgen_config_proto = out.File file_transport_internet_tlsmirror_tlstrafficgen_config_proto_goTypes = nil file_transport_internet_tlsmirror_tlstrafficgen_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/tlsmirror/tlstrafficgen/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.tlsmirror.tlstrafficgen; option csharp_namespace = "V2Ray.Core.Transport.Internet.Tlsmirror.Tlstrafficgen"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/tlstrafficgen"; option java_package = "com.v2ray.core.transport.internet.tlsmirror.tlstrafficgen"; option java_multiple_files = true; import "google/protobuf/any.proto"; message TimeSpec { uint64 base_nanoseconds = 1; uint64 uniform_random_multiplier_nanoseconds = 2; } message Header { string name = 1; string value = 2; repeated string values = 3; } message TransferCandidate{ int32 weight = 1; int64 goto_location = 2; } message Step { string name = 1; string host = 8; string path = 2; string method = 3; repeated TransferCandidate next_step = 6; bool connection_ready = 7; repeated Header headers = 9; bool connection_recall_exit = 10; TimeSpec wait_time = 11; bool h2_do_not_wait_for_download_finish = 12; } message Config { repeated Step steps = 1; google.protobuf.Any security_settings = 2; } ================================================ FILE: transport/internet/tlsmirror/tlstrafficgen/errors.generated.go ================================================ package tlstrafficgen import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/tlsmirror/tlstrafficgen/trafficgen.go ================================================ package tlstrafficgen import ( "context" cryptoRand "crypto/rand" "io" "math/big" "net/http" "net/url" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/transport/internet/security" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror" "github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/httponconnection" ) //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen type TrafficGenerator struct { config *Config ctx context.Context destination net.Destination tag string } func NewTrafficGenerator(ctx context.Context, config *Config, destination net.Destination, tag string) *TrafficGenerator { return &TrafficGenerator{ ctx: ctx, config: config, destination: destination, tag: tag, } } type trafficGeneratorManagedConnectionController struct { readyCtx context.Context readyDone context.CancelFunc recallCtx context.Context recallDone context.CancelFunc invalidatedCtx context.Context invalidatedDone context.CancelFunc } func newTrafficGeneratorManagedConnectionController(parent context.Context) *trafficGeneratorManagedConnectionController { readyCtx, readyDone := context.WithCancel(parent) recallCtx, recallDone := context.WithCancel(parent) invalidatedCtx, invalidatedDone := context.WithCancel(parent) return &trafficGeneratorManagedConnectionController{ readyCtx: readyCtx, readyDone: readyDone, recallCtx: recallCtx, recallDone: recallDone, invalidatedCtx: invalidatedCtx, invalidatedDone: invalidatedDone, } } func (t *trafficGeneratorManagedConnectionController) WaitConnectionReady() context.Context { return t.readyCtx } func (t *trafficGeneratorManagedConnectionController) RecallTrafficGenerator() error { t.recallDone() return nil } func (t *trafficGeneratorManagedConnectionController) IsConnectionInvalidated() bool { return t.invalidatedCtx.Err() != nil } // Copied from https://brandur.org/fragments/crypto-rand-float64, Thanks func randIntN(max int64) int64 { nBig, err := cryptoRand.Int(cryptoRand.Reader, big.NewInt(max)) if err != nil { panic(err) } return nBig.Int64() } func randFloat64() float64 { return float64(randIntN(1<<53)) / (1 << 53) } func (generator *TrafficGenerator) GenerateNextTraffic(ctx context.Context) error { transportEnvironment := envctx.EnvironmentFromContext(generator.ctx).(environment.TransportEnvironment) dialer := transportEnvironment.OutboundDialer() trafficController := newTrafficGeneratorManagedConnectionController(generator.ctx) var trafficControllerIfce tlsmirror.TrafficGeneratorManagedConnection = trafficController managedConnectionContextValue := context.WithValue(generator.ctx, tlsmirror.TrafficGeneratorManagedConnectionContextKey, trafficControllerIfce) // nolint:staticcheck defer func() { trafficController.invalidatedDone() trafficController.readyDone() }() conn, err := dialer(managedConnectionContextValue, generator.destination, generator.tag) if err != nil { return newError("failed to dial to destination").Base(err).AtWarning() } tlsConn, err := generator.tlsHandshake(conn) if err != nil { return newError("failed to create TLS connection").Base(err).AtWarning() } getAlpn, ok := tlsConn.(security.ConnectionApplicationProtocol) if !ok { return newError("TLS connection does not support getting ALPN").AtWarning() } alpn, err := getAlpn.GetConnectionApplicationProtocol() if err != nil { return newError("failed to get ALPN from TLS connection").Base(err).AtWarning() } steps := generator.config.Steps currentStep := 0 httpRoundTripper, err := httponconnection.NewSingleConnectionHTTPTransport(tlsConn, alpn) if err != nil { return newError("failed to create HTTP transport").Base(err).AtWarning() } for { if currentStep >= len(steps) { return tlsConn.Close() } step := steps[currentStep] url := url.URL{ Scheme: "https", Host: step.Host, Path: step.Path, } httpReq := &http.Request{Host: url.Hostname(), Method: step.Method, URL: &url} if step.Headers != nil { header := make(http.Header, len(step.Headers)) for _, v := range step.Headers { if v.Value != "" { header.Add(v.Name, v.Value) } if v.Values != nil { for _, value := range v.Values { header.Add(v.Name, value) } } } httpReq.Header = header } startTime := time.Now() resp, err := httpRoundTripper.RoundTrip(httpReq) //nolint:bodyclose if err != nil { return newError("failed to send HTTP request").Base(err).AtWarning() } finishRequest := func() error { _, err = io.Copy(io.Discard, resp.Body) if err != nil { return newError("failed to read HTTP response body").Base(err).AtWarning() } err = resp.Body.Close() if err != nil { return newError("failed to close HTTP response body").Base(err).AtWarning() } return nil } if step.H2DoNotWaitForDownloadFinish && alpn == "h2" { go func() { if err := finishRequest(); err != nil { newError("failed to finish request in background").Base(err).AtWarning().WriteToLog() } }() } else { if err := finishRequest(); err != nil { return err } } endTime := time.Now() eclipsedTime := endTime.Sub(startTime) if step.WaitTime == nil { step.WaitTime = &TimeSpec{} newError("no wait time specified for step ", currentStep, ", using default 0 seconds").AtWarning().WriteToLog() } secondToWait := (float64(step.WaitTime.UniformRandomMultiplierNanoseconds)*randFloat64() + float64(step.WaitTime.BaseNanoseconds)) / float64(time.Second) if eclipsedTime < time.Duration(secondToWait*float64(time.Second)) { waitTime := time.Duration(secondToWait*float64(time.Second)) - eclipsedTime newError("waiting for ", waitTime, " after step ", currentStep).AtDebug().WriteToLog() waitTimer := time.NewTimer(waitTime) select { case <-ctx.Done(): waitTimer.Stop() return ctx.Err() case <-waitTimer.C: } } else { newError("step ", currentStep, " took too long: ", eclipsedTime, ", expected: ", secondToWait, " seconds").AtWarning().WriteToLog() } if step.ConnectionReady { trafficController.readyDone() newError("connection ready for payload traffic").AtInfo().WriteToLog() } if step.ConnectionRecallExit { if trafficController.recallCtx.Err() != nil { return tlsConn.Close() } } if step.NextStep == nil { currentStep++ } else { overallWeight := int32(0) for _, nextStep := range step.NextStep { overallWeight += nextStep.Weight } maxBound := big.NewInt(int64(overallWeight)) selectionValue, err := cryptoRand.Int(cryptoRand.Reader, maxBound) if err != nil { return newError("failed to generate random selection value").Base(err).AtWarning() } selectedValue := int32(selectionValue.Int64()) currentValue := int32(0) matched := false for _, nextStep := range step.NextStep { if currentValue >= selectedValue { currentStep = int(nextStep.GotoLocation) matched = true break } currentValue += nextStep.Weight } if !matched { newError("invalid steps jump instruction, check configuration for step ", currentStep).AtError().WriteToLog() currentStep++ } } } } func (generator *TrafficGenerator) tlsHandshake(conn net.Conn) (security.Conn, error) { securitySettingInstance, err := serial.GetInstanceOf(generator.config.SecuritySettings) if err != nil { return nil, newError("failed to get instance of security settings").Base(err) } securityEngine, err := common.CreateObject(generator.ctx, securitySettingInstance) if err != nil { return nil, newError("unable to create security engine from security settings").Base(err) } securityEngineTyped, ok := securityEngine.(security.Engine) if !ok { return nil, newError("type assertion error when create security engine from security settings") } return securityEngineTyped.Client(conn) } ================================================ FILE: transport/internet/transportcommon/dialer.go ================================================ package transportcommon import ( "context" "github.com/v2fly/v2ray-core/v5/transport/internet/security" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func DialWithSecuritySettings(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) dialer := transportEnvironment.Dialer() conn, err := dialer.Dial(ctx, nil, dest, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to dial to ", dest).Base(err) } securityEngine, err := security.CreateSecurityEngineFromSettings(ctx, streamSettings) if err != nil { return nil, newError("unable to create security engine").Base(err) } if securityEngine != nil { conn, err = securityEngine.Client(conn, security.OptionWithDestination{Dest: dest}) if err != nil { return nil, newError("unable to create security protocol client from security engine").Base(err) } } return internet.Connection(conn), nil } ================================================ FILE: transport/internet/transportcommon/errors.generated.go ================================================ package transportcommon import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/transportcommon/httpDialer.go ================================================ package transportcommon import ( "context" "crypto/tls" "errors" "fmt" "net" "net/http" "sync" "time" "golang.org/x/net/http2" "github.com/v2fly/v2ray-core/v5/transport/internet/security" ) type DialerFunc func(ctx context.Context, addr string) (net.Conn, error) func NewALPNAwareHTTPRoundTripper(ctx context.Context, dialer DialerFunc, backdropTransport http.RoundTripper, ) http.RoundTripper { return NewALPNAwareHTTPRoundTripperWithH2Pool(ctx, dialer, backdropTransport, 1) } // NewALPNAwareHTTPRoundTripperWithH2Pool creates an instance of RoundTripper that dial to remote HTTPS endpoint with // an alternative version of TLS implementation. func NewALPNAwareHTTPRoundTripperWithH2Pool(ctx context.Context, dialer DialerFunc, backdropTransport http.RoundTripper, h2PoolSize int, ) http.RoundTripper { rtImpl := &alpnAwareHTTPRoundTripperImpl{ connectWithH1: map[string]bool{}, backdropTransport: backdropTransport, pendingConn: map[pendingConnKey]*unclaimedConnection{}, dialer: dialer, ctx: ctx, } rtImpl.init() return rtImpl } type alpnAwareHTTPRoundTripperImpl struct { accessConnectWithH1 sync.Mutex connectWithH1 map[string]bool httpsH1Transport http.RoundTripper httpsH2Transport http.RoundTripper backdropTransport http.RoundTripper accessDialingConnection sync.Mutex pendingConn map[pendingConnKey]*unclaimedConnection ctx context.Context dialer DialerFunc h2PoolSize int } type pendingConnKey struct { isH2 bool dest string } var ( errEAGAIN = errors.New("incorrect ALPN negotiated, try again with another ALPN") errEAGAINTooMany = errors.New("incorrect ALPN negotiated") errExpired = errors.New("connection have expired") ) func (r *alpnAwareHTTPRoundTripperImpl) RoundTrip(req *http.Request) (*http.Response, error) { if req.URL.Scheme != "https" { return r.backdropTransport.RoundTrip(req) } for retryCount := 0; retryCount < 5; retryCount++ { effectivePort := req.URL.Port() if effectivePort == "" { effectivePort = "443" } if r.getShouldConnectWithH1(fmt.Sprintf("%v:%v", req.URL.Hostname(), effectivePort)) { resp, err := r.httpsH1Transport.RoundTrip(req) if errors.Is(err, errEAGAIN) { continue } return resp, err } resp, err := r.httpsH2Transport.RoundTrip(req) if errors.Is(err, errEAGAIN) { continue } return resp, err } return nil, errEAGAINTooMany } func (r *alpnAwareHTTPRoundTripperImpl) getShouldConnectWithH1(domainName string) bool { r.accessConnectWithH1.Lock() defer r.accessConnectWithH1.Unlock() if value, set := r.connectWithH1[domainName]; set { return value } return false } func (r *alpnAwareHTTPRoundTripperImpl) setShouldConnectWithH1(domainName string) { r.accessConnectWithH1.Lock() defer r.accessConnectWithH1.Unlock() r.connectWithH1[domainName] = true } func (r *alpnAwareHTTPRoundTripperImpl) clearShouldConnectWithH1(domainName string) { r.accessConnectWithH1.Lock() defer r.accessConnectWithH1.Unlock() r.connectWithH1[domainName] = false } func getPendingConnectionID(dest string, alpnIsH2 bool) pendingConnKey { return pendingConnKey{isH2: alpnIsH2, dest: dest} } func (r *alpnAwareHTTPRoundTripperImpl) putConn(addr string, alpnIsH2 bool, conn net.Conn) { connID := getPendingConnectionID(addr, alpnIsH2) r.pendingConn[connID] = NewUnclaimedConnection(conn, time.Minute) } func (r *alpnAwareHTTPRoundTripperImpl) getConn(addr string, alpnIsH2 bool) net.Conn { connID := getPendingConnectionID(addr, alpnIsH2) if conn, ok := r.pendingConn[connID]; ok { delete(r.pendingConn, connID) if claimedConnection, err := conn.claimConnection(); err == nil { return claimedConnection } } return nil } func (r *alpnAwareHTTPRoundTripperImpl) dialOrGetTLSWithExpectedALPN(ctx context.Context, addr string, expectedH2 bool) (net.Conn, error) { r.accessDialingConnection.Lock() defer r.accessDialingConnection.Unlock() if r.getShouldConnectWithH1(addr) == expectedH2 { return nil, errEAGAIN } // Get a cached connection if possible to reduce preflight connection closed without sending data if gconn := r.getConn(addr, expectedH2); gconn != nil { return gconn, nil } conn, err := r.dialTLS(ctx, addr) if err != nil { return nil, err } protocol := "" if connAPLNGetter, ok := conn.(security.ConnectionApplicationProtocol); ok { connectionALPN, err := connAPLNGetter.GetConnectionApplicationProtocol() if err != nil { return nil, newError("failed to get connection ALPN").Base(err).AtWarning() } protocol = connectionALPN } protocolIsH2 := protocol == http2.NextProtoTLS if protocolIsH2 == expectedH2 { return conn, err } r.putConn(addr, protocolIsH2, conn) if protocolIsH2 { r.clearShouldConnectWithH1(addr) } else { r.setShouldConnectWithH1(addr) } return nil, errEAGAIN } func (r *alpnAwareHTTPRoundTripperImpl) dialTLS(ctx context.Context, addr string) (net.Conn, error) { _ = ctx return r.dialer(r.ctx, addr) } func (r *alpnAwareHTTPRoundTripperImpl) init() { if r.h2PoolSize >= 2 { r.httpsH2Transport = newH2TransportPool(int64(r.h2PoolSize), func() *http2.Transport { return &http2.Transport{ DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { return r.dialOrGetTLSWithExpectedALPN(context.Background(), addr, true) }, } }) } else { r.httpsH2Transport = &http2.Transport{ DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { return r.dialOrGetTLSWithExpectedALPN(context.Background(), addr, true) }, } } r.httpsH1Transport = &http.Transport{ DialTLSContext: func(ctx context.Context, network string, addr string) (net.Conn, error) { return r.dialOrGetTLSWithExpectedALPN(ctx, addr, false) }, } } func NewUnclaimedConnection(conn net.Conn, expireTime time.Duration) *unclaimedConnection { c := &unclaimedConnection{ Conn: conn, } time.AfterFunc(expireTime, c.tick) return c } type unclaimedConnection struct { net.Conn claimed bool access sync.Mutex } func (c *unclaimedConnection) claimConnection() (net.Conn, error) { c.access.Lock() defer c.access.Unlock() if !c.claimed { c.claimed = true return c.Conn, nil } return nil, errExpired } func (c *unclaimedConnection) tick() { c.access.Lock() defer c.access.Unlock() if !c.claimed { c.claimed = true c.Conn.Close() //nolint: staticcheck c.Conn = nil } } type h2TransportFactory func() *http2.Transport func newH2TransportPool(size int64, h2factory h2TransportFactory) *h2TransportPool { return &h2TransportPool{ pool: make([]*http2.Transport, size), size: size, h2factory: h2factory, } } type h2TransportPool struct { pool []*http2.Transport h2factory h2TransportFactory usageCount int64 size int64 } func (h *h2TransportPool) RoundTrip(request *http.Request) (*http.Response, error) { currentSlot := h.usageCount % h.size h.usageCount++ if h.pool[currentSlot] == nil { h.pool[currentSlot] = h.h2factory() } return h.pool[currentSlot].RoundTrip(request) } ================================================ FILE: transport/internet/transportcommon/listener.go ================================================ package transportcommon import ( "context" "crypto/tls" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" v2tls "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) func ListenWithSecuritySettings(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig) ( net.Listener, error, ) { var l net.Listener transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) transportListener := transportEnvironment.Listener() if port == net.Port(0) { // unix if !address.Family().IsDomain() { return nil, newError("invalid address for unix domain socket: ", address) } listener, err := transportListener.Listen(ctx, &net.UnixAddr{ Name: address.Domain(), Net: "unix", }, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to listen unix domain socket on ", address).Base(err) } newError("listening unix domain socket on ", address).WriteToLog(session.ExportIDToError(ctx)) l = listener } else { // tcp listener, err := transportListener.Listen(ctx, &net.TCPAddr{ IP: address.IP(), Port: int(port), }, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to listen TCP on ", address, ":", port).Base(err) } newError("listening TCP on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx)) l = listener } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) } if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { l = tls.NewListener(l, tlsConfig) } } return l, nil } ================================================ FILE: transport/internet/transportcommon/tansportcommon.go ================================================ package transportcommon //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/udp/config.go ================================================ package udp import ( "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/udp/config.pb.go ================================================ package udp import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Config struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_udp_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_udp_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_udp_config_proto_rawDescGZIP(), []int{0} } var File_transport_internet_udp_config_proto protoreflect.FileDescriptor const file_transport_internet_udp_config_proto_rawDesc = "" + "\n" + "#transport/internet/udp/config.proto\x12!v2ray.core.transport.internet.udp\"\b\n" + "\x06ConfigB\x84\x01\n" + "%com.v2ray.core.transport.internet.udpP\x01Z5github.com/v2fly/v2ray-core/v5/transport/internet/udp\xaa\x02!V2Ray.Core.Transport.Internet.Udpb\x06proto3" var ( file_transport_internet_udp_config_proto_rawDescOnce sync.Once file_transport_internet_udp_config_proto_rawDescData []byte ) func file_transport_internet_udp_config_proto_rawDescGZIP() []byte { file_transport_internet_udp_config_proto_rawDescOnce.Do(func() { file_transport_internet_udp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_udp_config_proto_rawDesc), len(file_transport_internet_udp_config_proto_rawDesc))) }) return file_transport_internet_udp_config_proto_rawDescData } var file_transport_internet_udp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_transport_internet_udp_config_proto_goTypes = []any{ (*Config)(nil), // 0: v2ray.core.transport.internet.udp.Config } var file_transport_internet_udp_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_transport_internet_udp_config_proto_init() } func file_transport_internet_udp_config_proto_init() { if File_transport_internet_udp_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_udp_config_proto_rawDesc), len(file_transport_internet_udp_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_udp_config_proto_goTypes, DependencyIndexes: file_transport_internet_udp_config_proto_depIdxs, MessageInfos: file_transport_internet_udp_config_proto_msgTypes, }.Build() File_transport_internet_udp_config_proto = out.File file_transport_internet_udp_config_proto_goTypes = nil file_transport_internet_udp_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/udp/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.udp; option csharp_namespace = "V2Ray.Core.Transport.Internet.Udp"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/udp"; option java_package = "com.v2ray.core.transport.internet.udp"; option java_multiple_files = true; message Config {} ================================================ FILE: transport/internet/udp/copy.go ================================================ package udp import ( gonet "net" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type dataHandler func(content []byte, address gonet.Addr) type copyHandler struct { onData []dataHandler } type CopyOption func(*copyHandler) func CopyPacketConn(dst internet.AbstractPacketConnWriter, src internet.AbstractPacketConnReader, options ...CopyOption) error { var handler copyHandler for _, option := range options { option(&handler) } var buffer [2048]byte for { n, addr, err := src.ReadFrom(buffer[:]) if err != nil { return err } for _, handler := range handler.onData { handler(buffer[:n], addr) } _, err = dst.WriteTo(buffer[:n], addr) if err != nil { return err } } } func UpdateActivity(timer signal.ActivityUpdater) CopyOption { return func(handler *copyHandler) { handler.onData = append(handler.onData, func(content []byte, address gonet.Addr) { timer.Update() }) } } ================================================ FILE: transport/internet/udp/dialer.go ================================================ package udp import ( "context" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func init() { common.Must(internet.RegisterTransportDialer(protocolName, func(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { var sockopt *internet.SocketConfig if streamSettings != nil { sockopt = streamSettings.SocketSettings } conn, err := internet.DialSystem(ctx, dest, sockopt) if err != nil { return nil, err } // TODO: handle dialer options return internet.Connection(conn), nil })) } ================================================ FILE: transport/internet/udp/dispatcher.go ================================================ package udp import ( "context" "io" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" ) type DispatcherI interface { common.Closable Dispatch(ctx context.Context, destination net.Destination, payload *buf.Buffer) } var DispatcherConnectionTerminationSignalReceiverMark = "DispatcherConnectionTerminationSignalReceiverMark" type DispatcherConnectionTerminationSignalReceiver interface { io.Closer } ================================================ FILE: transport/internet/udp/dispatcher_packetaddr.go ================================================ package udp import ( "context" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/features/routing" ) type PacketAddrDispatcher struct { conn net.PacketConn callback ResponseCallback ctx context.Context } func (p PacketAddrDispatcher) Close() error { if p.ctx.Value(DispatcherConnectionTerminationSignalReceiverMark) != nil { p.ctx.Value(DispatcherConnectionTerminationSignalReceiverMark).(DispatcherConnectionTerminationSignalReceiver).Close() } return p.conn.Close() } func (p PacketAddrDispatcher) Dispatch(ctx context.Context, destination net.Destination, payload *buf.Buffer) { if destination.Network != net.Network_UDP { return } // Processing of domain address is unsupported as it adds unpredictable overhead, it will be dropped. if destination.Address.Family().IsDomain() { return } p.conn.WriteTo(payload.Bytes(), &net.UDPAddr{IP: destination.Address.IP(), Port: int(destination.Port.Value())}) } func (p PacketAddrDispatcher) readWorker() { for { readBuf := buf.New() n, addr, err := p.conn.ReadFrom(readBuf.Extend(2048)) if err != nil { return } readBuf.Resize(0, int32(n)) p.callback(p.ctx, &udp.Packet{Payload: readBuf, Source: net.DestinationFromAddr(addr)}) } } type PacketAddrDispatcherCreator struct { ctx context.Context } func NewPacketAddrDispatcherCreator(ctx context.Context) PacketAddrDispatcherCreator { return PacketAddrDispatcherCreator{ctx: ctx} } func (pdc *PacketAddrDispatcherCreator) NewPacketAddrDispatcher( dispatcher routing.Dispatcher, callback ResponseCallback, ) DispatcherI { packetConn, _ := packetaddr.CreatePacketAddrConn(pdc.ctx, dispatcher, false) pd := &PacketAddrDispatcher{conn: packetConn, callback: callback, ctx: pdc.ctx} go pd.readWorker() return pd } ================================================ FILE: transport/internet/udp/dispatcher_split.go ================================================ package udp import ( "context" "io" "sync" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport" ) type ResponseCallback func(ctx context.Context, packet *udp.Packet) type connEntry struct { link *transport.Link timer signal.ActivityUpdater cancel context.CancelFunc } type Dispatcher struct { sync.RWMutex conns map[net.Destination]*connEntry dispatcher routing.Dispatcher callback ResponseCallback } func (v *Dispatcher) Close() error { return nil } func NewSplitDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) DispatcherI { return &Dispatcher{ conns: make(map[net.Destination]*connEntry), dispatcher: dispatcher, callback: callback, } } func (v *Dispatcher) RemoveRay(dest net.Destination) { v.Lock() defer v.Unlock() if conn, found := v.conns[dest]; found { common.Close(conn.link.Reader) common.Close(conn.link.Writer) delete(v.conns, dest) } } func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) *connEntry { v.Lock() defer v.Unlock() if entry, found := v.conns[dest]; found { return entry } newError("establishing new connection for ", dest).WriteToLog() ctx, cancel := context.WithCancel(ctx) removeRay := func() { cancel() v.RemoveRay(dest) } timer := signal.CancelAfterInactivity(ctx, removeRay, time.Second*300) link, _ := v.dispatcher.Dispatch(ctx, dest) entry := &connEntry{ link: link, timer: timer, cancel: removeRay, } v.conns[dest] = entry go handleInput(ctx, entry, dest, v.callback) return entry } func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination, payload *buf.Buffer) { // TODO: Add user to destString newError("dispatch request to: ", destination).AtDebug().WriteToLog(session.ExportIDToError(ctx)) conn := v.getInboundRay(ctx, destination) outputStream := conn.link.Writer if outputStream != nil { if err := outputStream.WriteMultiBuffer(buf.MultiBuffer{payload}); err != nil { newError("failed to write first UDP payload").Base(err).WriteToLog(session.ExportIDToError(ctx)) conn.cancel() return } } } func handleInput(ctx context.Context, conn *connEntry, dest net.Destination, callback ResponseCallback) { defer conn.cancel() input := conn.link.Reader timer := conn.timer for { select { case <-ctx.Done(): return default: } mb, err := input.ReadMultiBuffer() if err != nil { newError("failed to handle UDP input").Base(err).WriteToLog(session.ExportIDToError(ctx)) return } timer.Update() for _, b := range mb { callback(ctx, &udp.Packet{ Payload: b, Source: dest, }) } } } type dispatcherConn struct { dispatcher *Dispatcher cache chan *udp.Packet done *done.Instance } func DialDispatcher(ctx context.Context, dispatcher routing.Dispatcher) (net.PacketConn, error) { c := &dispatcherConn{ cache: make(chan *udp.Packet, 16), done: done.New(), } d := NewSplitDispatcher(dispatcher, c.callback) c.dispatcher = d.(*Dispatcher) return c, nil } func (c *dispatcherConn) callback(ctx context.Context, packet *udp.Packet) { select { case <-c.done.Wait(): packet.Payload.Release() return case c.cache <- packet: default: packet.Payload.Release() return } } func (c *dispatcherConn) ReadFrom(p []byte) (int, net.Addr, error) { select { case <-c.done.Wait(): return 0, nil, io.EOF case packet := <-c.cache: n := copy(p, packet.Payload.Bytes()) return n, &net.UDPAddr{ IP: packet.Source.Address.IP(), Port: int(packet.Source.Port), }, nil } } func (c *dispatcherConn) WriteTo(p []byte, addr net.Addr) (int, error) { buffer := buf.New() raw := buffer.Extend(buf.Size) n := copy(raw, p) buffer.Resize(0, int32(n)) ctx := context.Background() c.dispatcher.Dispatch(ctx, net.DestinationFromAddr(addr), buffer) return n, nil } func (c *dispatcherConn) Close() error { return c.done.Close() } func (c *dispatcherConn) LocalAddr() net.Addr { return &net.UDPAddr{ IP: []byte{0, 0, 0, 0}, Port: 0, } } func (c *dispatcherConn) SetDeadline(t time.Time) error { return nil } func (c *dispatcherConn) SetReadDeadline(t time.Time) error { return nil } func (c *dispatcherConn) SetWriteDeadline(t time.Time) error { return nil } ================================================ FILE: transport/internet/udp/dispatcher_split_test.go ================================================ package udp_test import ( "context" "sync/atomic" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/transport" . "github.com/v2fly/v2ray-core/v5/transport/internet/udp" "github.com/v2fly/v2ray-core/v5/transport/pipe" ) type TestDispatcher struct { OnDispatch func(ctx context.Context, dest net.Destination) (*transport.Link, error) } func (d *TestDispatcher) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { return d.OnDispatch(ctx, dest) } func (d *TestDispatcher) Start() error { return nil } func (d *TestDispatcher) Close() error { return nil } func (*TestDispatcher) Type() interface{} { return routing.DispatcherType() } func TestSameDestinationDispatching(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) uplinkReader, uplinkWriter := pipe.New(pipe.WithSizeLimit(1024)) downlinkReader, downlinkWriter := pipe.New(pipe.WithSizeLimit(1024)) go func() { for { data, err := uplinkReader.ReadMultiBuffer() if err != nil { break } err = downlinkWriter.WriteMultiBuffer(data) common.Must(err) } }() var count uint32 td := &TestDispatcher{ OnDispatch: func(ctx context.Context, dest net.Destination) (*transport.Link, error) { atomic.AddUint32(&count, 1) return &transport.Link{Reader: downlinkReader, Writer: uplinkWriter}, nil }, } dest := net.UDPDestination(net.LocalHostIP, 53) b := buf.New() b.WriteString("abcd") var msgCount uint32 dispatcher := NewSplitDispatcher(td, func(ctx context.Context, packet *udp.Packet) { atomic.AddUint32(&msgCount, 1) }) dispatcher.Dispatch(ctx, dest, b) for i := 0; i < 5; i++ { dispatcher.Dispatch(ctx, dest, b) } time.Sleep(time.Second) cancel() if count != 1 { t.Error("count: ", count) } if v := atomic.LoadUint32(&msgCount); v != 6 { t.Error("msgCount: ", v) } } ================================================ FILE: transport/internet/udp/errors.generated.go ================================================ package udp import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/udp/hub.go ================================================ package udp import ( "context" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/envctx" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/udp" "github.com/v2fly/v2ray-core/v5/transport/internet" ) type HubOption func(h *Hub) func HubCapacity(capacity int) HubOption { return func(h *Hub) { h.capacity = capacity } } func HubReceiveOriginalDestination(r bool) HubOption { return func(h *Hub) { h.recvOrigDest = r } } type Hub struct { conn *net.UDPConn connPacket net.PacketConn cache chan *udp.Packet capacity int recvOrigDest bool } func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, options ...HubOption) (*Hub, error) { hub := &Hub{ capacity: 256, recvOrigDest: false, } for _, opt := range options { opt(hub) } var sockopt *internet.SocketConfig if streamSettings != nil { sockopt = streamSettings.SocketSettings } if sockopt != nil && sockopt.ReceiveOriginalDestAddress { hub.recvOrigDest = true } transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment) listener := transportEnvironment.Listener() udpConn, err := listener.ListenPacket(ctx, &net.UDPAddr{ IP: address.IP(), Port: int(port), }, sockopt) if err != nil { return nil, err } newError("listening UDP on ", address, ":", port).WriteToLog() if udpConnDirect, ok := udpConn.(*net.UDPConn); ok { hub.conn = udpConnDirect } else { hub.connPacket = udpConn } hub.cache = make(chan *udp.Packet, hub.capacity) go hub.start() return hub, nil } // Close implements net.Listener. func (h *Hub) Close() error { if h.connPacket != nil { h.connPacket.Close() return nil } h.conn.Close() return nil } func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) { if h.connPacket != nil { return h.connPacket.WriteTo(payload, &net.UDPAddr{ IP: dest.Address.IP(), Port: int(dest.Port), }) } return h.conn.WriteToUDP(payload, &net.UDPAddr{ IP: dest.Address.IP(), Port: int(dest.Port), }) } func (h *Hub) start() { c := h.cache defer close(c) oobBytes := make([]byte, 256) for { buffer := buf.New() if h.conn != nil { var noob int var addr *net.UDPAddr rawBytes := buffer.Extend(buf.Size) n, noob, _, addr, err := ReadUDPMsg(h.conn, rawBytes, oobBytes) if err != nil { newError("failed to read UDP msg").Base(err).WriteToLog() buffer.Release() break } buffer.Resize(0, int32(n)) if buffer.IsEmpty() { buffer.Release() continue } payload := &udp.Packet{ Payload: buffer, Source: net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)), } if h.recvOrigDest && noob > 0 { payload.Target = RetrieveOriginalDest(oobBytes[:noob]) if payload.Target.IsValid() { newError("UDP original destination: ", payload.Target).AtDebug().WriteToLog() } else { newError("failed to read UDP original destination").WriteToLog() } } select { case c <- payload: default: buffer.Release() payload.Payload = nil } } else { rawBytes := buffer.Extend(buf.Size) n, addr, err := h.connPacket.ReadFrom(rawBytes) if err != nil { newError("failed to read UDP msg").Base(err).WriteToLog() buffer.Release() break } buffer.Resize(0, int32(n)) if buffer.IsEmpty() { buffer.Release() continue } payload := &udp.Packet{ Payload: buffer, Source: net.DestinationFromAddr(addr), } select { case c <- payload: default: buffer.Release() payload.Payload = nil } } } } // Addr implements net.Listener. func (h *Hub) Addr() net.Addr { if h.conn == nil { return h.connPacket.LocalAddr() } return h.conn.LocalAddr() } func (h *Hub) Receive() <-chan *udp.Packet { return h.cache } ================================================ FILE: transport/internet/udp/hub_freebsd.go ================================================ //go:build freebsd // +build freebsd package udp import ( "bytes" "encoding/gob" "io" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) // RetrieveOriginalDest from stored laddr, caddr func RetrieveOriginalDest(oob []byte) net.Destination { dec := gob.NewDecoder(bytes.NewBuffer(oob)) var la, ra net.UDPAddr dec.Decode(&la) dec.Decode(&ra) ip, port, err := internet.OriginalDst(&la, &ra) if err != nil { return net.Destination{} } return net.UDPDestination(net.IPAddress(ip), net.Port(port)) } // ReadUDPMsg stores laddr, caddr for later use func ReadUDPMsg(conn *net.UDPConn, payload []byte, oob []byte) (int, int, int, *net.UDPAddr, error) { nBytes, addr, err := conn.ReadFromUDP(payload) if err != nil { return nBytes, 0, 0, addr, err } var buf bytes.Buffer enc := gob.NewEncoder(&buf) localAddr, ok := conn.LocalAddr().(*net.UDPAddr) if !ok { return 0, 0, 0, nil, errors.New("invalid local address") } if addr == nil { return 0, 0, 0, nil, errors.New("invalid remote address") } enc.Encode(localAddr) enc.Encode(addr) var reader io.Reader = &buf noob, _ := reader.Read(oob) return nBytes, noob, 0, addr, nil } ================================================ FILE: transport/internet/udp/hub_linux.go ================================================ //go:build linux // +build linux package udp import ( "syscall" "golang.org/x/sys/unix" "github.com/v2fly/v2ray-core/v5/common/net" ) func RetrieveOriginalDest(oob []byte) net.Destination { msgs, err := syscall.ParseSocketControlMessage(oob) if err != nil { return net.Destination{} } for _, msg := range msgs { if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR { ip := net.IPAddress(msg.Data[4:8]) port := net.PortFromBytes(msg.Data[2:4]) return net.UDPDestination(ip, port) } else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == unix.IPV6_RECVORIGDSTADDR { ip := net.IPAddress(msg.Data[8:24]) port := net.PortFromBytes(msg.Data[2:4]) return net.UDPDestination(ip, port) } } return net.Destination{} } func ReadUDPMsg(conn *net.UDPConn, payload []byte, oob []byte) (int, int, int, *net.UDPAddr, error) { return conn.ReadMsgUDP(payload, oob) } ================================================ FILE: transport/internet/udp/hub_other.go ================================================ //go:build !linux && !freebsd // +build !linux,!freebsd package udp import ( "github.com/v2fly/v2ray-core/v5/common/net" ) func RetrieveOriginalDest(oob []byte) net.Destination { return net.Destination{} } func ReadUDPMsg(conn *net.UDPConn, payload []byte, oob []byte) (int, int, int, *net.UDPAddr, error) { nBytes, addr, err := conn.ReadFromUDP(payload) return nBytes, 0, 0, addr, err } ================================================ FILE: transport/internet/udp/monodest.go ================================================ package udp import ( "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/transport/internet" ) func NewMonoDestUDPConn(conn internet.AbstractPacketConn, addr net.Addr) *MonoDestUDPConn { return &MonoDestUDPConn{ AbstractPacketConn: conn, dest: addr, } } type MonoDestUDPConn struct { internet.AbstractPacketConn dest net.Addr } func (m *MonoDestUDPConn) ReadMultiBuffer() (buf.MultiBuffer, error) { buffer := buf.New() buffer.Extend(2048) nBytes, _, err := m.AbstractPacketConn.ReadFrom(buffer.Bytes()) if err != nil { buffer.Release() return nil, err } buffer.Resize(0, int32(nBytes)) return buf.MultiBuffer{buffer}, nil } func (m *MonoDestUDPConn) WriteMultiBuffer(buffer buf.MultiBuffer) error { for _, b := range buffer { _, err := m.AbstractPacketConn.WriteTo(b.Bytes(), m.dest) if err != nil { return err } } buf.ReleaseMulti(buffer) return nil } func (m *MonoDestUDPConn) Read(p []byte) (n int, err error) { n, _, err = m.AbstractPacketConn.ReadFrom(p) return } func (m *MonoDestUDPConn) Write(p []byte) (n int, err error) { return m.AbstractPacketConn.WriteTo(p, m.dest) } ================================================ FILE: transport/internet/udp/udp.go ================================================ package udp //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen const protocolName = "udp" ================================================ FILE: transport/internet/websocket/config.go ================================================ package websocket import ( "net/http" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/transport/internet" ) const protocolName = "websocket" func (c *Config) GetNormalizedPath() string { path := c.Path if path == "" { return "/" } if path[0] != '/' { return "/" + path } return path } func (c *Config) GetRequestHeader() http.Header { header := http.Header{} for _, h := range c.Header { header.Add(h.Key, h.Value) } return header } func init() { common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} { return new(Config) })) } ================================================ FILE: transport/internet/websocket/config.pb.go ================================================ package websocket import ( _ "github.com/v2fly/v2ray-core/v5/common/protoext" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Header struct { state protoimpl.MessageState `protogen:"open.v1"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Header) Reset() { *x = Header{} mi := &file_transport_internet_websocket_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Header) String() string { return protoimpl.X.MessageStringOf(x) } func (*Header) ProtoMessage() {} func (x *Header) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_websocket_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Header.ProtoReflect.Descriptor instead. func (*Header) Descriptor() ([]byte, []int) { return file_transport_internet_websocket_config_proto_rawDescGZIP(), []int{0} } func (x *Header) GetKey() string { if x != nil { return x.Key } return "" } func (x *Header) GetValue() string { if x != nil { return x.Value } return "" } type Config struct { state protoimpl.MessageState `protogen:"open.v1"` // URL path to the WebSocket service. Empty value means root(/). Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` Header []*Header `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty"` AcceptProxyProtocol bool `protobuf:"varint,4,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` MaxEarlyData int32 `protobuf:"varint,5,opt,name=max_early_data,json=maxEarlyData,proto3" json:"max_early_data,omitempty"` UseBrowserForwarding bool `protobuf:"varint,6,opt,name=use_browser_forwarding,json=useBrowserForwarding,proto3" json:"use_browser_forwarding,omitempty"` EarlyDataHeaderName string `protobuf:"bytes,7,opt,name=early_data_header_name,json=earlyDataHeaderName,proto3" json:"early_data_header_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Config) Reset() { *x = Config{} mi := &file_transport_internet_websocket_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_transport_internet_websocket_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_transport_internet_websocket_config_proto_rawDescGZIP(), []int{1} } func (x *Config) GetPath() string { if x != nil { return x.Path } return "" } func (x *Config) GetHeader() []*Header { if x != nil { return x.Header } return nil } func (x *Config) GetAcceptProxyProtocol() bool { if x != nil { return x.AcceptProxyProtocol } return false } func (x *Config) GetMaxEarlyData() int32 { if x != nil { return x.MaxEarlyData } return 0 } func (x *Config) GetUseBrowserForwarding() bool { if x != nil { return x.UseBrowserForwarding } return false } func (x *Config) GetEarlyDataHeaderName() string { if x != nil { return x.EarlyDataHeaderName } return "" } var File_transport_internet_websocket_config_proto protoreflect.FileDescriptor const file_transport_internet_websocket_config_proto_rawDesc = "" + "\n" + ")transport/internet/websocket/config.proto\x12'v2ray.core.transport.internet.websocket\x1a common/protoext/extensions.proto\"0\n" + "\x06Header\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value\"\xd6\x02\n" + "\x06Config\x12\x12\n" + "\x04path\x18\x02 \x01(\tR\x04path\x12G\n" + "\x06header\x18\x03 \x03(\v2/.v2ray.core.transport.internet.websocket.HeaderR\x06header\x122\n" + "\x15accept_proxy_protocol\x18\x04 \x01(\bR\x13acceptProxyProtocol\x12$\n" + "\x0emax_early_data\x18\x05 \x01(\x05R\fmaxEarlyData\x124\n" + "\x16use_browser_forwarding\x18\x06 \x01(\bR\x14useBrowserForwarding\x123\n" + "\x16early_data_header_name\x18\a \x01(\tR\x13earlyDataHeaderName:$\x82\xb5\x18 \n" + "\ttransport\x12\x02ws\x8a\xff)\twebsocket\x90\xff)\x01J\x04\b\x01\x10\x02B\x96\x01\n" + "+com.v2ray.core.transport.internet.websocketP\x01Z;github.com/v2fly/v2ray-core/v5/transport/internet/websocket\xaa\x02'V2Ray.Core.Transport.Internet.Websocketb\x06proto3" var ( file_transport_internet_websocket_config_proto_rawDescOnce sync.Once file_transport_internet_websocket_config_proto_rawDescData []byte ) func file_transport_internet_websocket_config_proto_rawDescGZIP() []byte { file_transport_internet_websocket_config_proto_rawDescOnce.Do(func() { file_transport_internet_websocket_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_websocket_config_proto_rawDesc), len(file_transport_internet_websocket_config_proto_rawDesc))) }) return file_transport_internet_websocket_config_proto_rawDescData } var file_transport_internet_websocket_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_transport_internet_websocket_config_proto_goTypes = []any{ (*Header)(nil), // 0: v2ray.core.transport.internet.websocket.Header (*Config)(nil), // 1: v2ray.core.transport.internet.websocket.Config } var file_transport_internet_websocket_config_proto_depIdxs = []int32{ 0, // 0: v2ray.core.transport.internet.websocket.Config.header:type_name -> v2ray.core.transport.internet.websocket.Header 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_transport_internet_websocket_config_proto_init() } func file_transport_internet_websocket_config_proto_init() { if File_transport_internet_websocket_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_websocket_config_proto_rawDesc), len(file_transport_internet_websocket_config_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_transport_internet_websocket_config_proto_goTypes, DependencyIndexes: file_transport_internet_websocket_config_proto_depIdxs, MessageInfos: file_transport_internet_websocket_config_proto_msgTypes, }.Build() File_transport_internet_websocket_config_proto = out.File file_transport_internet_websocket_config_proto_goTypes = nil file_transport_internet_websocket_config_proto_depIdxs = nil } ================================================ FILE: transport/internet/websocket/config.proto ================================================ syntax = "proto3"; package v2ray.core.transport.internet.websocket; option csharp_namespace = "V2Ray.Core.Transport.Internet.Websocket"; option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/websocket"; option java_package = "com.v2ray.core.transport.internet.websocket"; option java_multiple_files = true; import "common/protoext/extensions.proto"; message Header { string key = 1; string value = 2; } message Config { option (v2ray.core.common.protoext.message_opt).type = "transport"; option (v2ray.core.common.protoext.message_opt).short_name = "ws"; option (v2ray.core.common.protoext.message_opt).allow_restricted_mode_load = true; option (v2ray.core.common.protoext.message_opt).transport_original_name = "websocket"; reserved 1; // URL path to the WebSocket service. Empty value means root(/). string path = 2; repeated Header header = 3; bool accept_proxy_protocol = 4; int32 max_early_data = 5; bool use_browser_forwarding = 6; string early_data_header_name = 7; } ================================================ FILE: transport/internet/websocket/connection.go ================================================ package websocket import ( "context" "io" "net" "time" "github.com/gorilla/websocket" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/errors" "github.com/v2fly/v2ray-core/v5/common/serial" ) var _ buf.Writer = (*connection)(nil) // connection is a wrapper for net.Conn over WebSocket connection. type connection struct { conn *websocket.Conn reader io.Reader remoteAddr net.Addr shouldWait bool delayedDialFinish context.Context finishedDial context.CancelFunc dialer DelayedDialer } type DelayedDialer interface { Dial(earlyData []byte) (*websocket.Conn, error) } func newConnection(conn *websocket.Conn, remoteAddr net.Addr) *connection { return &connection{ conn: conn, remoteAddr: remoteAddr, } } func newConnectionWithEarlyData(conn *websocket.Conn, remoteAddr net.Addr, earlyData io.Reader) *connection { return &connection{ conn: conn, remoteAddr: remoteAddr, reader: earlyData, } } func newConnectionWithDelayedDial(dialer DelayedDialer) *connection { delayedDialContext, cancelFunc := context.WithCancel(context.Background()) return &connection{ shouldWait: true, delayedDialFinish: delayedDialContext, finishedDial: cancelFunc, dialer: dialer, } } func newRelayedConnectionWithDelayedDial(dialer DelayedDialerForwarded) *connectionForwarder { delayedDialContext, cancelFunc := context.WithCancel(context.Background()) return &connectionForwarder{ shouldWait: true, delayedDialFinish: delayedDialContext, finishedDial: cancelFunc, dialer: dialer, } } func newRelayedConnection(conn io.ReadWriteCloser) *connectionForwarder { return &connectionForwarder{ ReadWriteCloser: conn, shouldWait: false, } } // Read implements net.Conn.Read() func (c *connection) Read(b []byte) (int, error) { for { reader, err := c.getReader() if err != nil { return 0, err } nBytes, err := reader.Read(b) if errors.Cause(err) == io.EOF { c.reader = nil continue } return nBytes, err } } func (c *connection) getReader() (io.Reader, error) { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { return nil, newError("unable to read delayed dial websocket connection as it do not exist") } } if c.reader != nil { return c.reader, nil } _, reader, err := c.conn.NextReader() if err != nil { return nil, err } c.reader = reader return reader, nil } // Write implements io.Writer. func (c *connection) Write(b []byte) (int, error) { if c.shouldWait { var err error c.conn, err = c.dialer.Dial(b) c.finishedDial() if err != nil { return 0, newError("Unable to proceed with delayed write").Base(err) } c.remoteAddr = c.conn.RemoteAddr() c.shouldWait = false return len(b), nil } if err := c.conn.WriteMessage(websocket.BinaryMessage, b); err != nil { return 0, err } return len(b), nil } func (c *connection) WriteMultiBuffer(mb buf.MultiBuffer) error { mb = buf.Compact(mb) mb, err := buf.WriteMultiBuffer(c, mb) buf.ReleaseMulti(mb) return err } func (c *connection) Close() error { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { return newError("unable to close delayed dial websocket connection as it do not exist") } } var errors []interface{} if err := c.conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(time.Second*5)); err != nil { errors = append(errors, err) } if err := c.conn.Close(); err != nil { errors = append(errors, err) } if len(errors) > 0 { return newError("failed to close connection").Base(newError(serial.Concat(errors...))) } return nil } func (c *connection) LocalAddr() net.Addr { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { newError("websocket transport is not materialized when LocalAddr() is called").AtWarning().WriteToLog() return &net.UnixAddr{ Name: "@placeholder", Net: "unix", } } } return c.conn.LocalAddr() } func (c *connection) RemoteAddr() net.Addr { return c.remoteAddr } func (c *connection) SetDeadline(t time.Time) error { if err := c.SetReadDeadline(t); err != nil { return err } return c.SetWriteDeadline(t) } func (c *connection) SetReadDeadline(t time.Time) error { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { newError("websocket transport is not materialized when SetReadDeadline() is called").AtWarning().WriteToLog() return nil } } return c.conn.SetReadDeadline(t) } func (c *connection) SetWriteDeadline(t time.Time) error { if c.shouldWait { <-c.delayedDialFinish.Done() if c.conn == nil { newError("websocket transport is not materialized when SetWriteDeadline() is called").AtWarning().WriteToLog() return nil } } return c.conn.SetWriteDeadline(t) } ================================================ FILE: transport/internet/websocket/connforwarder.go ================================================ package websocket import ( "context" "io" "net" "time" ) type connectionForwarder struct { io.ReadWriteCloser shouldWait bool delayedDialFinish context.Context finishedDial context.CancelFunc dialer DelayedDialerForwarded } func (c *connectionForwarder) Read(p []byte) (n int, err error) { if c.shouldWait { <-c.delayedDialFinish.Done() if c.ReadWriteCloser == nil { return 0, newError("unable to read delayed dial websocket connection as it do not exist") } } return c.ReadWriteCloser.Read(p) } func (c *connectionForwarder) Write(p []byte) (n int, err error) { if c.shouldWait { var err error c.ReadWriteCloser, err = c.dialer.Dial(p) c.finishedDial() if err != nil { return 0, newError("Unable to proceed with delayed write").Base(err) } c.shouldWait = false return len(p), nil } return c.ReadWriteCloser.Write(p) } func (c *connectionForwarder) Close() error { if c.shouldWait { <-c.delayedDialFinish.Done() if c.ReadWriteCloser == nil { return newError("unable to close delayed dial websocket connection as it do not exist") } } return c.ReadWriteCloser.Close() } func (c connectionForwarder) LocalAddr() net.Addr { return &net.UnixAddr{ Name: "not available", Net: "", } } func (c connectionForwarder) RemoteAddr() net.Addr { return &net.UnixAddr{ Name: "not available", Net: "", } } func (c connectionForwarder) SetDeadline(t time.Time) error { return nil } func (c connectionForwarder) SetReadDeadline(t time.Time) error { return nil } func (c connectionForwarder) SetWriteDeadline(t time.Time) error { return nil } type DelayedDialerForwarded interface { Dial(earlyData []byte) (io.ReadWriteCloser, error) } ================================================ FILE: transport/internet/websocket/dialer.go ================================================ package websocket import ( "bytes" "context" "encoding/base64" "io" gonet "net" "net/http" "time" "github.com/gorilla/websocket" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/features/extension" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/security" ) // Dial dials a WebSocket connection to the given destination. func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx)) conn, err := dialWebsocket(ctx, dest, streamSettings) if err != nil { return nil, newError("failed to dial WebSocket").Base(err) } return internet.Connection(conn), nil } func init() { common.Must(internet.RegisterTransportDialer(protocolName, Dial)) } func dialWebsocket(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) { wsSettings := streamSettings.ProtocolSettings.(*Config) dialer := &websocket.Dialer{ NetDial: func(network, addr string) (net.Conn, error) { return internet.DialSystem(ctx, dest, streamSettings.SocketSettings) }, ReadBufferSize: 4 * 1024, WriteBufferSize: 4 * 1024, HandshakeTimeout: time.Second * 8, } protocol := "ws" securityEngine, err := security.CreateSecurityEngineFromSettings(ctx, streamSettings) if err != nil { return nil, newError("unable to create security engine").Base(err) } if securityEngine != nil { protocol = "wss" dialer.NetDialTLSContext = func(ctx context.Context, network, addr string) (gonet.Conn, error) { conn, err := dialer.NetDial(network, addr) if err != nil { return nil, newError("dial TLS connection failed").Base(err) } conn, err = securityEngine.Client(conn, security.OptionWithDestination{Dest: dest}, security.OptionWithALPN{ALPNs: []string{"http/1.1"}}) if err != nil { return nil, newError("unable to create security protocol client from security engine").Base(err) } return conn, nil } } host := dest.NetAddr() if (protocol == "ws" && dest.Port == 80) || (protocol == "wss" && dest.Port == 443) { host = dest.Address.String() } uri := protocol + "://" + host + wsSettings.GetNormalizedPath() if wsSettings.UseBrowserForwarding { var forwarder extension.BrowserForwarder err := core.RequireFeatures(ctx, func(Forwarder extension.BrowserForwarder) { forwarder = Forwarder }) if err != nil { return nil, newError("cannot find browser forwarder service").Base(err) } if wsSettings.MaxEarlyData != 0 { return newRelayedConnectionWithDelayedDial(&dialerWithEarlyDataRelayed{ forwarder: forwarder, uriBase: uri, config: wsSettings, }), nil } conn, err := forwarder.DialWebsocket(uri, nil) if err != nil { return nil, newError("cannot dial with browser forwarder service").Base(err) } return newRelayedConnection(conn), nil } if wsSettings.MaxEarlyData != 0 { return newConnectionWithDelayedDial(&dialerWithEarlyData{ dialer: dialer, uriBase: uri, config: wsSettings, }), nil } conn, resp, err := dialer.Dial(uri, wsSettings.GetRequestHeader()) // nolint: bodyclose if err != nil { var reason string if resp != nil { reason = resp.Status } return nil, newError("failed to dial to (", uri, "): ", reason).Base(err) } return newConnection(conn, conn.RemoteAddr()), nil } type dialerWithEarlyData struct { dialer *websocket.Dialer uriBase string config *Config } func (d dialerWithEarlyData) Dial(earlyData []byte) (*websocket.Conn, error) { earlyDataBuf := bytes.NewBuffer(nil) base64EarlyDataEncoder := base64.NewEncoder(base64.RawURLEncoding, earlyDataBuf) earlydata := bytes.NewReader(earlyData) limitedEarlyDatareader := io.LimitReader(earlydata, int64(d.config.MaxEarlyData)) n, encerr := io.Copy(base64EarlyDataEncoder, limitedEarlyDatareader) if encerr != nil { return nil, newError("websocket delayed dialer cannot encode early data").Base(encerr) } if errc := base64EarlyDataEncoder.Close(); errc != nil { return nil, newError("websocket delayed dialer cannot encode early data tail").Base(errc) } dialFunction := func() (*websocket.Conn, *http.Response, error) { return d.dialer.Dial(d.uriBase+earlyDataBuf.String(), d.config.GetRequestHeader()) } if d.config.EarlyDataHeaderName != "" { dialFunction = func() (*websocket.Conn, *http.Response, error) { earlyDataStr := earlyDataBuf.String() currentHeader := d.config.GetRequestHeader() currentHeader.Set(d.config.EarlyDataHeaderName, earlyDataStr) return d.dialer.Dial(d.uriBase, currentHeader) } } conn, resp, err := dialFunction() // nolint: bodyclose if err != nil { var reason string if resp != nil { reason = resp.Status } return nil, newError("failed to dial to (", d.uriBase, ") with early data: ", reason).Base(err) } if n != int64(len(earlyData)) { if errWrite := conn.WriteMessage(websocket.BinaryMessage, earlyData[n:]); errWrite != nil { return nil, newError("failed to dial to (", d.uriBase, ") with early data as write of remainder early data failed: ").Base(errWrite) } } return conn, nil } type dialerWithEarlyDataRelayed struct { forwarder extension.BrowserForwarder uriBase string config *Config } func (d dialerWithEarlyDataRelayed) Dial(earlyData []byte) (io.ReadWriteCloser, error) { earlyDataBuf := bytes.NewBuffer(nil) base64EarlyDataEncoder := base64.NewEncoder(base64.RawURLEncoding, earlyDataBuf) earlydata := bytes.NewReader(earlyData) limitedEarlyDatareader := io.LimitReader(earlydata, int64(d.config.MaxEarlyData)) n, encerr := io.Copy(base64EarlyDataEncoder, limitedEarlyDatareader) if encerr != nil { return nil, newError("websocket delayed dialer cannot encode early data").Base(encerr) } if errc := base64EarlyDataEncoder.Close(); errc != nil { return nil, newError("websocket delayed dialer cannot encode early data tail").Base(errc) } dialFunction := func() (io.ReadWriteCloser, error) { return d.forwarder.DialWebsocket(d.uriBase+earlyDataBuf.String(), d.config.GetRequestHeader()) } if d.config.EarlyDataHeaderName != "" { earlyDataStr := earlyDataBuf.String() currentHeader := d.config.GetRequestHeader() currentHeader.Set(d.config.EarlyDataHeaderName, earlyDataStr) return d.forwarder.DialWebsocket(d.uriBase, currentHeader) } conn, err := dialFunction() if err != nil { var reason string return nil, newError("failed to dial to (", d.uriBase, ") with early data: ", reason).Base(err) } if n != int64(len(earlyData)) { if _, errWrite := conn.Write(earlyData[n:]); errWrite != nil { return nil, newError("failed to dial to (", d.uriBase, ") with early data as write of remainder early data failed: ").Base(errWrite) } } return conn, nil } ================================================ FILE: transport/internet/websocket/errors.generated.go ================================================ package websocket import "github.com/v2fly/v2ray-core/v5/common/errors" type errPathObjHolder struct{} func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) } ================================================ FILE: transport/internet/websocket/hub.go ================================================ package websocket import ( "bytes" "context" "crypto/tls" "encoding/base64" "io" "net/http" "strings" "sync" "time" "github.com/gorilla/websocket" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" http_proto "github.com/v2fly/v2ray-core/v5/common/protocol/http" "github.com/v2fly/v2ray-core/v5/common/session" "github.com/v2fly/v2ray-core/v5/transport/internet" v2tls "github.com/v2fly/v2ray-core/v5/transport/internet/tls" ) type requestHandler struct { path string ln *Listener earlyDataEnabled bool earlyDataHeaderName string } var upgrader = &websocket.Upgrader{ ReadBufferSize: 4 * 1024, WriteBufferSize: 4 * 1024, HandshakeTimeout: time.Second * 4, CheckOrigin: func(r *http.Request) bool { return true }, } func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { responseHeader := http.Header{} var earlyData io.Reader if !h.earlyDataEnabled { // nolint: gocritic if request.URL.Path != h.path { writer.WriteHeader(http.StatusNotFound) return } } else if h.earlyDataHeaderName != "" { if request.URL.Path != h.path { writer.WriteHeader(http.StatusNotFound) return } earlyDataStr := request.Header.Get(h.earlyDataHeaderName) earlyData = base64.NewDecoder(base64.RawURLEncoding, bytes.NewReader([]byte(earlyDataStr))) if strings.EqualFold("Sec-WebSocket-Protocol", h.earlyDataHeaderName) { responseHeader.Set(h.earlyDataHeaderName, earlyDataStr) } } else { if strings.HasPrefix(request.URL.RequestURI(), h.path) { earlyDataStr := request.URL.RequestURI()[len(h.path):] earlyData = base64.NewDecoder(base64.RawURLEncoding, bytes.NewReader([]byte(earlyDataStr))) } else { writer.WriteHeader(http.StatusNotFound) return } } conn, err := upgrader.Upgrade(writer, request, responseHeader) if err != nil { newError("failed to convert to WebSocket connection").Base(err).WriteToLog() return } forwardedAddrs := http_proto.ParseXForwardedFor(request.Header) remoteAddr := conn.RemoteAddr() if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { remoteAddr = &net.TCPAddr{ IP: forwardedAddrs[0].IP(), Port: int(0), } } if earlyData == nil { h.ln.addConn(newConnection(conn, remoteAddr)) } else { h.ln.addConn(newConnectionWithEarlyData(conn, remoteAddr, earlyData)) } } type Listener struct { sync.Mutex server http.Server listener net.Listener config *Config addConn internet.ConnHandler } func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) { l := &Listener{ addConn: addConn, } wsSettings := streamSettings.ProtocolSettings.(*Config) l.config = wsSettings if l.config != nil { if streamSettings.SocketSettings == nil { streamSettings.SocketSettings = &internet.SocketConfig{} } streamSettings.SocketSettings.AcceptProxyProtocol = l.config.AcceptProxyProtocol } var listener net.Listener var err error if port == net.Port(0) { // unix listener, err = internet.ListenSystem(ctx, &net.UnixAddr{ Name: address.Domain(), Net: "unix", }, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to listen unix domain socket(for WS) on ", address).Base(err) } newError("listening unix domain socket(for WS) on ", address).WriteToLog(session.ExportIDToError(ctx)) } else { // tcp listener, err = internet.ListenSystem(ctx, &net.TCPAddr{ IP: address.IP(), Port: int(port), }, streamSettings.SocketSettings) if err != nil { return nil, newError("failed to listen TCP(for WS) on ", address, ":", port).Base(err) } newError("listening TCP(for WS) on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx)) } if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol { newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx)) } if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { listener = tls.NewListener(listener, tlsConfig) } } l.listener = listener useEarlyData := false earlyDataHeaderName := "" if wsSettings.MaxEarlyData != 0 { useEarlyData = true earlyDataHeaderName = wsSettings.EarlyDataHeaderName } l.server = http.Server{ Handler: &requestHandler{ path: wsSettings.GetNormalizedPath(), ln: l, earlyDataEnabled: useEarlyData, earlyDataHeaderName: earlyDataHeaderName, }, ReadHeaderTimeout: time.Second * 4, MaxHeaderBytes: http.DefaultMaxHeaderBytes, } go func() { if err := l.server.Serve(l.listener); err != nil { newError("failed to serve http for WebSocket").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) } }() return l, err } // Addr implements net.Listener.Addr(). func (ln *Listener) Addr() net.Addr { return ln.listener.Addr() } // Close implements net.Listener.Close(). func (ln *Listener) Close() error { return ln.listener.Close() } func init() { common.Must(internet.RegisterTransportListener(protocolName, ListenWS)) } ================================================ FILE: transport/internet/websocket/ws.go ================================================ /* Package websocket implements Websocket transport Websocket transport implements an HTTP(S) compliable, surveillance proof transport method with plausible deniability. */ package websocket //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen ================================================ FILE: transport/internet/websocket/ws_test.go ================================================ package websocket_test import ( "context" "runtime" "testing" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol/tls/cert" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" . "github.com/v2fly/v2ray-core/v5/transport/internet/websocket" ) func Test_listenWSAndDial(t *testing.T) { listen, err := ListenWS(context.Background(), net.LocalHostIP, 13146, &internet.MemoryStreamConfig{ ProtocolName: "websocket", ProtocolSettings: &Config{ Path: "ws", }, }, func(conn internet.Connection) { go func(c internet.Connection) { defer c.Close() var b [1024]byte _, err := c.Read(b[:]) if err != nil { return } common.Must2(c.Write([]byte("Response"))) }(conn) }) common.Must(err) ctx := context.Background() streamSettings := &internet.MemoryStreamConfig{ ProtocolName: "websocket", ProtocolSettings: &Config{Path: "ws"}, } conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146), streamSettings) common.Must(err) _, err = conn.Write([]byte("Test connection 1")) common.Must(err) var b [1024]byte n, err := conn.Read(b[:]) common.Must(err) if string(b[:n]) != "Response" { t.Error("response: ", string(b[:n])) } common.Must(conn.Close()) <-time.After(time.Second * 5) conn, err = Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146), streamSettings) common.Must(err) _, err = conn.Write([]byte("Test connection 2")) common.Must(err) n, err = conn.Read(b[:]) common.Must(err) if string(b[:n]) != "Response" { t.Error("response: ", string(b[:n])) } common.Must(conn.Close()) common.Must(listen.Close()) } func TestDialWithRemoteAddr(t *testing.T) { listen, err := ListenWS(context.Background(), net.LocalHostIP, 13148, &internet.MemoryStreamConfig{ ProtocolName: "websocket", ProtocolSettings: &Config{ Path: "ws", }, }, func(conn internet.Connection) { go func(c internet.Connection) { defer c.Close() var b [1024]byte _, err := c.Read(b[:]) // common.Must(err) if err != nil { return } _, err = c.Write([]byte("Response")) common.Must(err) }(conn) }) common.Must(err) conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), 13148), &internet.MemoryStreamConfig{ ProtocolName: "websocket", ProtocolSettings: &Config{Path: "ws", Header: []*Header{{Key: "X-Forwarded-For", Value: "1.1.1.1"}}}, }) common.Must(err) _, err = conn.Write([]byte("Test connection 1")) common.Must(err) var b [1024]byte n, err := conn.Read(b[:]) common.Must(err) if string(b[:n]) != "Response" { t.Error("response: ", string(b[:n])) } common.Must(listen.Close()) } func Test_listenWSAndDial_TLS(t *testing.T) { if runtime.GOARCH == "arm64" { return } start := time.Now() streamSettings := &internet.MemoryStreamConfig{ ProtocolName: "websocket", ProtocolSettings: &Config{ Path: "wss", }, SecurityType: "tls", SecuritySettings: &tls.Config{ AllowInsecure: true, Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))}, }, } listen, err := ListenWS(context.Background(), net.LocalHostIP, 13143, streamSettings, func(conn internet.Connection) { go func() { _ = conn.Close() }() }) common.Must(err) defer listen.Close() conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), 13143), streamSettings) common.Must(err) _ = conn.Close() end := time.Now() if !end.Before(start.Add(time.Second * 5)) { t.Error("end: ", end, " start: ", start) } } ================================================ FILE: transport/link.go ================================================ package transport import "github.com/v2fly/v2ray-core/v5/common/buf" // Link is a utility for connecting between an inbound and an outbound proxy handler. type Link struct { Reader buf.Reader Writer buf.Writer } ================================================ FILE: transport/pipe/impl.go ================================================ package pipe import ( "errors" "io" "runtime" "sync" "time" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/signal/done" ) type state byte const ( open state = iota closed errord ) type pipeOption struct { limit int32 // maximum buffer size in bytes discardOverflow bool } func (o *pipeOption) isFull(curSize int32) bool { return o.limit >= 0 && curSize > o.limit } type pipe struct { sync.Mutex data buf.MultiBuffer readSignal *signal.Notifier writeSignal *signal.Notifier done *done.Instance option pipeOption state state } var ( errBufferFull = errors.New("buffer full") errSlowDown = errors.New("slow down") ) func (p *pipe) getState(forRead bool) error { switch p.state { case open: if !forRead && p.option.isFull(p.data.Len()) { return errBufferFull } return nil case closed: if !forRead { return io.ErrClosedPipe } if !p.data.IsEmpty() { return nil } return io.EOF case errord: return io.ErrClosedPipe default: panic("impossible case") } } func (p *pipe) readMultiBufferInternal() (buf.MultiBuffer, error) { p.Lock() defer p.Unlock() if err := p.getState(true); err != nil { return nil, err } data := p.data p.data = nil return data, nil } func (p *pipe) ReadMultiBuffer() (buf.MultiBuffer, error) { for { data, err := p.readMultiBufferInternal() if data != nil || err != nil { p.writeSignal.Signal() return data, err } select { case <-p.readSignal.Wait(): case <-p.done.Wait(): } } } func (p *pipe) ReadMultiBufferTimeout(d time.Duration) (buf.MultiBuffer, error) { timer := time.NewTimer(d) defer timer.Stop() for { data, err := p.readMultiBufferInternal() if data != nil || err != nil { p.writeSignal.Signal() return data, err } select { case <-p.readSignal.Wait(): case <-p.done.Wait(): case <-timer.C: return nil, buf.ErrReadTimeout } } } func (p *pipe) writeMultiBufferInternal(mb buf.MultiBuffer) error { p.Lock() defer p.Unlock() if err := p.getState(false); err != nil { return err } if p.data == nil { p.data = mb return nil } p.data, _ = buf.MergeMulti(p.data, mb) return errSlowDown } func (p *pipe) WriteMultiBuffer(mb buf.MultiBuffer) error { if mb.IsEmpty() { return nil } for { err := p.writeMultiBufferInternal(mb) if err == nil { p.readSignal.Signal() return nil } if err == errSlowDown { p.readSignal.Signal() // Yield current goroutine. Hopefully the reading counterpart can pick up the payload. runtime.Gosched() return nil } if err == errBufferFull && p.option.discardOverflow { buf.ReleaseMulti(mb) return nil } if err != errBufferFull { buf.ReleaseMulti(mb) p.readSignal.Signal() return err } select { case <-p.writeSignal.Wait(): case <-p.done.Wait(): return io.ErrClosedPipe } } } func (p *pipe) Close() error { p.Lock() defer p.Unlock() if p.state == closed || p.state == errord { return nil } p.state = closed common.Must(p.done.Close()) return nil } // Interrupt implements common.Interruptible. func (p *pipe) Interrupt() { p.Lock() defer p.Unlock() if p.state == closed || p.state == errord { return } p.state = errord if !p.data.IsEmpty() { buf.ReleaseMulti(p.data) p.data = nil } common.Must(p.done.Close()) } ================================================ FILE: transport/pipe/pipe.go ================================================ package pipe import ( "context" "github.com/v2fly/v2ray-core/v5/common/signal" "github.com/v2fly/v2ray-core/v5/common/signal/done" "github.com/v2fly/v2ray-core/v5/features/policy" ) // Option for creating new Pipes. type Option func(*pipeOption) // WithoutSizeLimit returns an Option for Pipe to have no size limit. func WithoutSizeLimit() Option { return func(opt *pipeOption) { opt.limit = -1 } } // WithSizeLimit returns an Option for Pipe to have the given size limit. func WithSizeLimit(limit int32) Option { return func(opt *pipeOption) { opt.limit = limit } } // DiscardOverflow returns an Option for Pipe to discard writes if full. func DiscardOverflow() Option { return func(opt *pipeOption) { opt.discardOverflow = true } } // OptionsFromContext returns a list of Options from context. func OptionsFromContext(ctx context.Context) []Option { var opt []Option bp := policy.BufferPolicyFromContext(ctx) if bp.PerConnection >= 0 { opt = append(opt, WithSizeLimit(bp.PerConnection)) } else { opt = append(opt, WithoutSizeLimit()) } return opt } // New creates a new Reader and Writer that connects to each other. func New(opts ...Option) (*Reader, *Writer) { p := &pipe{ readSignal: signal.NewNotifier(), writeSignal: signal.NewNotifier(), done: done.New(), option: pipeOption{ limit: -1, }, } for _, opt := range opts { opt(&(p.option)) } return &Reader{ pipe: p, }, &Writer{ pipe: p, } } ================================================ FILE: transport/pipe/pipe_test.go ================================================ package pipe_test import ( "errors" "io" "testing" "time" "github.com/google/go-cmp/cmp" "golang.org/x/sync/errgroup" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/buf" . "github.com/v2fly/v2ray-core/v5/transport/pipe" ) func TestPipeReadWrite(t *testing.T) { pReader, pWriter := New(WithSizeLimit(1024)) b := buf.New() b.WriteString("abcd") common.Must(pWriter.WriteMultiBuffer(buf.MultiBuffer{b})) b2 := buf.New() b2.WriteString("efg") common.Must(pWriter.WriteMultiBuffer(buf.MultiBuffer{b2})) rb, err := pReader.ReadMultiBuffer() common.Must(err) if r := cmp.Diff(rb.String(), "abcdefg"); r != "" { t.Error(r) } } func TestPipeInterrupt(t *testing.T) { pReader, pWriter := New(WithSizeLimit(1024)) payload := []byte{'a', 'b', 'c', 'd'} b := buf.New() b.Write(payload) common.Must(pWriter.WriteMultiBuffer(buf.MultiBuffer{b})) pWriter.Interrupt() rb, err := pReader.ReadMultiBuffer() if err != io.ErrClosedPipe { t.Fatal("expect io.ErrClosePipe, but got ", err) } if !rb.IsEmpty() { t.Fatal("expect empty buffer, but got ", rb.Len()) } } func TestPipeClose(t *testing.T) { pReader, pWriter := New(WithSizeLimit(1024)) payload := []byte{'a', 'b', 'c', 'd'} b := buf.New() common.Must2(b.Write(payload)) common.Must(pWriter.WriteMultiBuffer(buf.MultiBuffer{b})) common.Must(pWriter.Close()) rb, err := pReader.ReadMultiBuffer() common.Must(err) if rb.String() != string(payload) { t.Fatal("expect content ", string(payload), " but actually ", rb.String()) } rb, err = pReader.ReadMultiBuffer() if err != io.EOF { t.Fatal("expected EOF, but got ", err) } if !rb.IsEmpty() { t.Fatal("expect empty buffer, but got ", rb.String()) } } func TestPipeLimitZero(t *testing.T) { pReader, pWriter := New(WithSizeLimit(0)) bb := buf.New() common.Must2(bb.Write([]byte{'a', 'b'})) common.Must(pWriter.WriteMultiBuffer(buf.MultiBuffer{bb})) var errg errgroup.Group errg.Go(func() error { b := buf.New() b.Write([]byte{'c', 'd'}) return pWriter.WriteMultiBuffer(buf.MultiBuffer{b}) }) errg.Go(func() error { time.Sleep(time.Second) var container buf.MultiBufferContainer if err := buf.Copy(pReader, &container); err != nil { return err } if r := cmp.Diff(container.String(), "abcd"); r != "" { return errors.New(r) } return nil }) errg.Go(func() error { time.Sleep(time.Second * 2) return pWriter.Close() }) if err := errg.Wait(); err != nil { t.Error(err) } } func TestPipeWriteMultiThread(t *testing.T) { pReader, pWriter := New(WithSizeLimit(0)) var errg errgroup.Group for i := 0; i < 10; i++ { errg.Go(func() error { b := buf.New() b.WriteString("abcd") return pWriter.WriteMultiBuffer(buf.MultiBuffer{b}) }) } time.Sleep(time.Millisecond * 100) pWriter.Close() errg.Wait() b, err := pReader.ReadMultiBuffer() common.Must(err) if r := cmp.Diff(b[0].Bytes(), []byte{'a', 'b', 'c', 'd'}); r != "" { t.Error(r) } } func TestInterfaces(t *testing.T) { _ = (buf.Reader)(new(Reader)) _ = (buf.TimeoutReader)(new(Reader)) _ = (common.Interruptible)(new(Reader)) _ = (common.Interruptible)(new(Writer)) _ = (common.Closable)(new(Writer)) } func BenchmarkPipeReadWrite(b *testing.B) { reader, writer := New(WithoutSizeLimit()) a := buf.New() a.Extend(buf.Size) c := buf.MultiBuffer{a} b.ResetTimer() for i := 0; i < b.N; i++ { common.Must(writer.WriteMultiBuffer(c)) d, err := reader.ReadMultiBuffer() common.Must(err) c = d } } ================================================ FILE: transport/pipe/reader.go ================================================ package pipe import ( "time" "github.com/v2fly/v2ray-core/v5/common/buf" ) // Reader is a buf.Reader that reads content from a pipe. type Reader struct { pipe *pipe } // ReadMultiBuffer implements buf.Reader. func (r *Reader) ReadMultiBuffer() (buf.MultiBuffer, error) { return r.pipe.ReadMultiBuffer() } // ReadMultiBufferTimeout reads content from a pipe within the given duration, or returns buf.ErrTimeout otherwise. func (r *Reader) ReadMultiBufferTimeout(d time.Duration) (buf.MultiBuffer, error) { return r.pipe.ReadMultiBufferTimeout(d) } // Interrupt implements common.Interruptible. func (r *Reader) Interrupt() { r.pipe.Interrupt() } ================================================ FILE: transport/pipe/writer.go ================================================ package pipe import ( "github.com/v2fly/v2ray-core/v5/common/buf" ) // Writer is a buf.Writer that writes data into a pipe. type Writer struct { pipe *pipe } // WriteMultiBuffer implements buf.Writer. func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error { return w.pipe.WriteMultiBuffer(mb) } // Close implements io.Closer. After the pipe is closed, writing to the pipe will return io.ErrClosedPipe, while reading will return io.EOF. func (w *Writer) Close() error { return w.pipe.Close() } // Interrupt implements common.Interruptible. func (w *Writer) Interrupt() { w.pipe.Interrupt() } ================================================ FILE: v2ray.go ================================================ package core import ( "context" "reflect" sync "sync" "github.com/v2fly/v2ray-core/v5/common/environment/deferredpersistentstorage" "github.com/v2fly/v2ray-core/v5/common/environment/filesystemimpl" "github.com/v2fly/v2ray-core/v5/features/extension/storage" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/environment" "github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl" "github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/features" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/dns/localdns" "github.com/v2fly/v2ray-core/v5/features/inbound" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/routing" "github.com/v2fly/v2ray-core/v5/features/stats" ) // Server is an instance of V2Ray. At any time, there must be at most one Server instance running. type Server interface { common.Runnable } // ServerType returns the type of the server. func ServerType() interface{} { return (*Instance)(nil) } type resolution struct { deps []reflect.Type callback interface{} } func getFeature(allFeatures []features.Feature, t reflect.Type) features.Feature { for _, f := range allFeatures { if reflect.TypeOf(f.Type()) == t { return f } } return nil } func (r *resolution) resolve(allFeatures []features.Feature) (bool, error) { var fs []features.Feature for _, d := range r.deps { f := getFeature(allFeatures, d) if f == nil { return false, nil } fs = append(fs, f) } callback := reflect.ValueOf(r.callback) var input []reflect.Value callbackType := callback.Type() for i := 0; i < callbackType.NumIn(); i++ { pt := callbackType.In(i) for _, f := range fs { if reflect.TypeOf(f).AssignableTo(pt) { input = append(input, reflect.ValueOf(f)) break } } } if len(input) != callbackType.NumIn() { panic("Can't get all input parameters") } var err error ret := callback.Call(input) errInterface := reflect.TypeOf((*error)(nil)).Elem() for i := len(ret) - 1; i >= 0; i-- { if ret[i].Type() == errInterface { v := ret[i].Interface() if v != nil { err = v.(error) } break } } return true, err } // Instance combines all functionalities in V2Ray. type Instance struct { access sync.Mutex features []features.Feature featureResolutions []resolution running bool env environment.RootEnvironment ctx context.Context } func AddInboundHandler(server *Instance, config *InboundHandlerConfig) error { inboundManager := server.GetFeature(inbound.ManagerType()).(inbound.Manager) proxyEnv := server.env.ProxyEnvironment("i" + config.Tag) rawHandler, err := CreateObjectWithEnvironment(server, config, proxyEnv) if err != nil { return err } handler, ok := rawHandler.(inbound.Handler) if !ok { return newError("not an InboundHandler") } if err := inboundManager.AddHandler(server.ctx, handler); err != nil { return err } return nil } func addInboundHandlers(server *Instance, configs []*InboundHandlerConfig) error { for _, inboundConfig := range configs { if err := AddInboundHandler(server, inboundConfig); err != nil { return err } } return nil } func AddOutboundHandler(server *Instance, config *OutboundHandlerConfig) error { outboundManager := server.GetFeature(outbound.ManagerType()).(outbound.Manager) proxyEnv := server.env.ProxyEnvironment("o" + config.Tag) rawHandler, err := CreateObjectWithEnvironment(server, config, proxyEnv) if err != nil { return err } handler, ok := rawHandler.(outbound.Handler) if !ok { return newError("not an OutboundHandler") } if err := outboundManager.AddHandler(server.ctx, handler); err != nil { return err } return nil } func RemoveOutboundHandler(server *Instance, tag string) error { outboundManager := server.GetFeature(outbound.ManagerType()).(outbound.Manager) if err := outboundManager.RemoveHandler(server.ctx, tag); err != nil { return err } if err := server.env.DropProxyEnvironment("o" + tag); err != nil { return err } return nil } func addOutboundHandlers(server *Instance, configs []*OutboundHandlerConfig) error { for _, outboundConfig := range configs { if err := AddOutboundHandler(server, outboundConfig); err != nil { return err } } return nil } // RequireFeatures is a helper function to require features from Instance in context. // See Instance.RequireFeatures for more information. func RequireFeatures(ctx context.Context, callback interface{}) error { v := MustFromContext(ctx) return v.RequireFeatures(callback) } // New returns a new V2Ray instance based on given configuration. // The instance is not started at this point. // To ensure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional. func New(config *Config) (*Instance, error) { server := &Instance{ctx: context.Background()} done, err := initInstanceWithConfig(config, server) if done { return nil, err } return server, nil } func NewWithContext(ctx context.Context, config *Config) (*Instance, error) { server := &Instance{ctx: ctx} done, err := initInstanceWithConfig(config, server) if done { return nil, err } return server, nil } func initInstanceWithConfig(config *Config, server *Instance) (bool, error) { if config.Transport != nil { features.PrintDeprecatedFeatureWarning("global transport settings") } if err := config.Transport.Apply(); err != nil { return true, err } defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault() defaultFilesystemImpl := filesystemimpl.NewDefaultFileSystemDefaultImpl() deferredPersistentStorageImpl := deferredpersistentstorage.NewDeferredPersistentStorage(server.ctx) server.env = environment.NewRootEnvImpl(server.ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener(), defaultFilesystemImpl, deferredPersistentStorageImpl) for _, appSettings := range config.App { settings, err := serial.GetInstanceOf(appSettings) if err != nil { return true, err } key := appSettings.TypeUrl appEnv := server.env.AppEnvironment(key) obj, err := CreateObjectWithEnvironment(server, settings, appEnv) if err != nil { return true, err } if feature, ok := obj.(features.Feature); ok { if err := server.AddFeature(feature); err != nil { return true, err } } } essentialFeatures := []struct { Type interface{} Instance features.Feature }{ {dns.ClientType(), localdns.New()}, {policy.ManagerType(), policy.DefaultManager{}}, {routing.RouterType(), routing.DefaultRouter{}}, {stats.ManagerType(), stats.NoopManager{}}, } for _, f := range essentialFeatures { if server.GetFeature(f.Type) == nil { if err := server.AddFeature(f.Instance); err != nil { return true, err } } } if server.featureResolutions != nil { return true, newError("not all dependency are resolved.") } if persistentStorageService := server.GetFeature(storage.ScopedPersistentStorageServiceType); persistentStorageService != nil { deferredPersistentStorageImpl.ProvideInner(server.ctx, persistentStorageService.(storage.ScopedPersistentStorage)) } else { deferredPersistentStorageImpl.ProvideInner(server.ctx, nil) } if err := addInboundHandlers(server, config.Inbound); err != nil { return true, err } if err := addOutboundHandlers(server, config.Outbound); err != nil { return true, err } return false, nil } // Type implements common.HasType. func (s *Instance) Type() interface{} { return ServerType() } // Close shutdown the V2Ray instance. func (s *Instance) Close() error { s.access.Lock() defer s.access.Unlock() s.running = false var errors []interface{} for _, f := range s.features { if err := f.Close(); err != nil { errors = append(errors, err) } } if len(errors) > 0 { return newError("failed to close all features").Base(newError(serial.Concat(errors...))) } return nil } // RequireFeatures registers a callback, which will be called when all dependent features are registered. // The callback must be a func(). All its parameters must be features.Feature. func (s *Instance) RequireFeatures(callback interface{}) error { callbackType := reflect.TypeOf(callback) if callbackType.Kind() != reflect.Func { panic("not a function") } var featureTypes []reflect.Type for i := 0; i < callbackType.NumIn(); i++ { featureTypes = append(featureTypes, reflect.PtrTo(callbackType.In(i))) } r := resolution{ deps: featureTypes, callback: callback, } if finished, err := r.resolve(s.features); finished { return err } s.featureResolutions = append(s.featureResolutions, r) return nil } // AddFeature registers a feature into current Instance. func (s *Instance) AddFeature(feature features.Feature) error { s.features = append(s.features, feature) if s.running { if err := feature.Start(); err != nil { newError("failed to start feature").Base(err).WriteToLog() } return nil } if s.featureResolutions == nil { return nil } var pendingResolutions []resolution for _, r := range s.featureResolutions { finished, err := r.resolve(s.features) if finished && err != nil { return err } if !finished { pendingResolutions = append(pendingResolutions, r) } } if len(pendingResolutions) == 0 { s.featureResolutions = nil } else if len(pendingResolutions) < len(s.featureResolutions) { s.featureResolutions = pendingResolutions } return nil } // GetFeature returns a feature of the given type, or nil if such feature is not registered. func (s *Instance) GetFeature(featureType interface{}) features.Feature { return getFeature(s.features, reflect.TypeOf(featureType)) } // Start starts the V2Ray instance, including all registered features. When Start returns error, the state of the instance is unknown. // A V2Ray instance can be started only once. Upon closing, the instance is not guaranteed to start again. // // v2ray:api:stable func (s *Instance) Start() error { s.access.Lock() defer s.access.Unlock() s.running = true for _, f := range s.features { if err := f.Start(); err != nil { return err } } newError("V2Ray ", Version(), " started").AtWarning().WriteToLog() return nil } ================================================ FILE: v2ray_test.go ================================================ package core_test import ( "testing" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" . "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/uuid" "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/dns/localdns" _ "github.com/v2fly/v2ray-core/v5/main/distro/all" "github.com/v2fly/v2ray-core/v5/proxy/dokodemo" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/outbound" "github.com/v2fly/v2ray-core/v5/testing/servers/tcp" ) func TestV2RayDependency(t *testing.T) { instance := new(Instance) wait := make(chan bool, 1) instance.RequireFeatures(func(d dns.Client) { if d == nil { t.Error("expected dns client fulfilled, but actually nil") } wait <- true }) instance.AddFeature(localdns.New()) <-wait } func TestV2RayClose(t *testing.T) { port := tcp.PickPort() userID := uuid.New() config := &Config{ App: []*anypb.Any{ serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, Inbound: []*InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: net.SinglePortRange(port), Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(0), NetworkList: &net.NetworkList{ Network: []net.Network{net.Network_TCP, net.Network_UDP}, }, }), }, }, Outbound: []*OutboundHandlerConfig{ { ProxySettings: serial.ToTypedMessage(&outbound.Config{ Receiver: []*protocol.ServerEndpoint{ { Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(0), User: []*protocol.User{ { Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), }, }, }, }, }), }, }, } cfgBytes, err := proto.Marshal(config) common.Must(err) server, err := StartInstance(FormatProtobuf, cfgBytes) common.Must(err) server.Close() }